builder.go raw
1 package mxssa
2
3 import (
4 "go/constant"
5 "go/token"
6
7 "moxie/syntax"
8 "moxie/typecheck"
9 )
10
11 // ─── Package builder ──────────────────────────────────────────────────────────
12
13 // CreatePackage builds the SSA Package for pkg from its type-checked syntax files.
14 func (prog *Program) CreatePackage(pkg *typecheck.Package, files []*syntax.File, info *typecheck.Info) *Package {
15 p := &Package{
16 Prog: prog,
17 Pkg: pkg,
18 Members: map[string]Member{},
19 }
20 prog.packages[pkg] = p
21 prog.imported[pkg.Path()] = p
22
23 pb := &pkgBuilder{pkg: p, info: info, prog: prog}
24
25 // Pass 1: register top-level members (no bodies yet).
26 for _, f := range files {
27 pb.registerFile(f)
28 }
29
30 // Pass 2: build function bodies.
31 for _, f := range files {
32 pb.buildFile(f)
33 }
34
35 return p
36 }
37
38 // pkgBuilder builds one Package.
39 type pkgBuilder struct {
40 pkg *Package
41 info *typecheck.Info
42 prog *Program
43 }
44
45 func (pb *pkgBuilder) registerFile(f *syntax.File) {
46 for _, d := range f.DeclList {
47 switch d := d.(type) {
48 case *syntax.FuncDecl:
49 pb.registerFunc(d)
50 case *syntax.VarDecl:
51 pb.registerVar(d)
52 case *syntax.TypeDecl:
53 pb.registerType(d)
54 case *syntax.ConstDecl:
55 pb.registerConst(d)
56 }
57 }
58 }
59
60 func (pb *pkgBuilder) registerFunc(d *syntax.FuncDecl) {
61 if d.Name.Value == "init" {
62 return // init functions handled separately
63 }
64 var obj *typecheck.Func
65 if o := pb.pkg.Pkg.Scope().Lookup(d.Name.Value); o != nil {
66 obj, _ = o.(*typecheck.Func)
67 }
68 var sig *typecheck.Signature
69 if obj != nil {
70 sig, _ = obj.Type().(*typecheck.Signature)
71 }
72 fn := &Function{
73 name: d.Name.Value,
74 object: obj,
75 Signature: sig,
76 pos: posFromSyntax(d.Name.Pos()),
77 Pkg: pb.pkg,
78 Prog: pb.prog,
79 }
80 pb.pkg.Members[fn.name] = fn
81 }
82
83 func (pb *pkgBuilder) registerVar(d *syntax.VarDecl) {
84 for _, name := range d.NameList {
85 var obj *typecheck.Var
86 if o := pb.pkg.Pkg.Scope().Lookup(name.Value); o != nil {
87 obj, _ = o.(*typecheck.Var)
88 }
89 var typ typecheck.Type
90 if obj != nil {
91 typ = obj.Type()
92 }
93 g := &Global{
94 name: name.Value,
95 object: obj,
96 typ: typecheck.NewPointer(typ), // globals are pointers in SSA
97 pos: posFromSyntax(name.Pos()),
98 pkg: pb.pkg,
99 }
100 pb.pkg.Members[name.Value] = g
101 }
102 }
103
104 func (pb *pkgBuilder) registerType(d *syntax.TypeDecl) {
105 var obj *typecheck.TypeName
106 if o := pb.pkg.Pkg.Scope().Lookup(d.Name.Value); o != nil {
107 obj, _ = o.(*typecheck.TypeName)
108 }
109 if obj == nil {
110 return
111 }
112 t := &Type_{object: obj, pkg: pb.pkg}
113 pb.pkg.Members[d.Name.Value] = t
114 }
115
116 func (pb *pkgBuilder) registerConst(d *syntax.ConstDecl) {
117 for _, name := range d.NameList {
118 var obj *typecheck.Const
119 if o := pb.pkg.Pkg.Scope().Lookup(name.Value); o != nil {
120 obj, _ = o.(*typecheck.Const)
121 }
122 if obj == nil {
123 continue
124 }
125 var cval constant.Value
126 if obj.Val() != nil {
127 cval, _ = obj.Val().(constant.Value)
128 }
129 c := &NamedConst{
130 object: obj,
131 Value: &Const{typ: obj.Type(), val: cval},
132 pkg: pb.pkg,
133 }
134 pb.pkg.Members[name.Value] = c
135 }
136 }
137
138 func (pb *pkgBuilder) buildFile(f *syntax.File) {
139 for _, d := range f.DeclList {
140 if fd, ok := d.(*syntax.FuncDecl); ok {
141 pb.buildFunc(fd)
142 }
143 }
144 }
145
146 func (pb *pkgBuilder) buildFunc(d *syntax.FuncDecl) {
147 if d.Body == nil {
148 return // external function
149 }
150 fn, _ := pb.pkg.Members[d.Name.Value].(*Function)
151 if fn == nil {
152 return
153 }
154 fb := newFuncBuilder(fn, pb.info)
155 fb.buildBody(d)
156 }
157
158 // ─── Function builder ─────────────────────────────────────────────────────────
159
160 // funcBuilder builds one Function body.
161 type funcBuilder struct {
162 fn *Function
163 info *typecheck.Info
164 currentBlock *BasicBlock
165 vars map[typecheck.Object]*Alloc // local variable allocas
166 namedResults []*Alloc
167 counter int // for generating unique SSA names
168
169 // Loop/defer state
170 loops []loopState
171 deferred int // number of pending defer statements
172 }
173
174 type loopState struct {
175 body *BasicBlock
176 post *BasicBlock
177 done *BasicBlock
178 }
179
180 func newFuncBuilder(fn *Function, info *typecheck.Info) *funcBuilder {
181 return &funcBuilder{
182 fn: fn,
183 info: info,
184 vars: map[typecheck.Object]*Alloc{},
185 }
186 }
187
188 func (fb *funcBuilder) newBlock(comment string) *BasicBlock {
189 return NewBasicBlock(fb.fn, comment)
190 }
191
192 func (fb *funcBuilder) emit(instr Instruction) {
193 if fb.currentBlock == nil {
194 return
195 }
196 instr.setBlock(fb.currentBlock)
197 fb.currentBlock.Instrs = append(fb.currentBlock.Instrs, instr)
198 }
199
200 func (fb *funcBuilder) nextName() string {
201 fb.counter++
202 return "t" + itoa(fb.counter)
203 }
204
205 func (fb *funcBuilder) emitValue(instr interface {
206 Instruction
207 Value
208 }) Value {
209 instr.setName(fb.nextName())
210 fb.emit(instr)
211 return instr
212 }
213
214 // buildBody initializes parameters and builds the function body.
215 func (fb *funcBuilder) buildBody(d *syntax.FuncDecl) {
216 entry := fb.newBlock("entry")
217 fb.currentBlock = entry
218 fb.fn.currentBlock = entry
219
220 sig := fb.fn.Signature
221 if sig == nil {
222 return
223 }
224
225 // Allocate parameters.
226 params := sig.Params()
227 if d.Recv != nil {
228 // receiver as first param
229 recv := sig.Recv()
230 if recv != nil {
231 var recvObj *typecheck.Var
232 if d.Recv.Name != nil {
233 recvObj = fb.lookupVar(d.Recv.Name.Value)
234 }
235 p := &Parameter{
236 name: recv.Name(),
237 object: recvObj,
238 typ: recv.Type(),
239 pos: posFromSyntax(d.Recv.Pos()),
240 parent: fb.fn,
241 }
242 fb.fn.Params = append(fb.fn.Params, p)
243 if recvObj != nil {
244 alloc := fb.emitAlloc(recv.Type(), p.pos)
245 fb.vars[recvObj] = alloc
246 fb.emitStore(alloc, p)
247 }
248 }
249 }
250 if params != nil {
251 for i := 0; i < params.Len(); i++ {
252 pvar := params.At(i)
253 p := &Parameter{
254 name: pvar.Name(),
255 typ: pvar.Type(),
256 pos: fb.fn.pos,
257 parent: fb.fn,
258 }
259 fb.fn.Params = append(fb.fn.Params, p)
260 if pvar.Name() != "" && pvar.Name() != "_" {
261 if obj := fb.lookupVarByType(pvar.Name(), pvar.Type()); obj != nil {
262 alloc := fb.emitAlloc(pvar.Type(), p.pos)
263 fb.vars[obj] = alloc
264 fb.emitStore(alloc, p)
265 }
266 }
267 }
268 }
269
270 // Named results.
271 if sig.Results() != nil {
272 for i := 0; i < sig.Results().Len(); i++ {
273 r := sig.Results().At(i)
274 if r.Name() != "" && r.Name() != "_" {
275 alloc := fb.emitAlloc(r.Type(), fb.fn.pos)
276 fb.namedResults = append(fb.namedResults, alloc)
277 fb.fn.Locals = append(fb.fn.Locals, alloc)
278 // store zero value implicitly
279 }
280 }
281 }
282
283 // Build the body.
284 if d.Body != nil {
285 fb.buildBlock(d.Body)
286 }
287
288 // Implicit return at end.
289 if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
290 fb.emitReturn(nil, 0)
291 }
292 }
293
294 func (fb *funcBuilder) emitAlloc(typ typecheck.Type, pos token.Pos) *Alloc {
295 a := &Alloc{Heap: false}
296 a.typ = typecheck.NewPointer(typ)
297 a.pos = pos
298 a.name = fb.nextName()
299 fb.emit(a)
300 fb.fn.Locals = append(fb.fn.Locals, a)
301 return a
302 }
303
304 func (fb *funcBuilder) emitStore(addr Value, val Value) {
305 fb.emit(&Store{Addr: addr, Val: val})
306 }
307
308 func (fb *funcBuilder) emitLoad(addr Value, typ typecheck.Type) Value {
309 u := &UnOp{Op: token.MUL, X: addr}
310 u.typ = typ
311 u.name = fb.nextName()
312 fb.emit(u)
313 return u
314 }
315
316 func (fb *funcBuilder) emitReturn(vals []Value, pos token.Pos) {
317 fb.emit(&Return{Results: vals, pos: pos})
318 fb.currentBlock = nil
319 }
320
321 func (fb *funcBuilder) blockTerminated(b *BasicBlock) bool {
322 if len(b.Instrs) == 0 {
323 return false
324 }
325 switch b.Instrs[len(b.Instrs)-1].(type) {
326 case *Return, *Jump, *If, *Panic:
327 return true
328 }
329 return false
330 }
331
332 // ─── Statement builder ────────────────────────────────────────────────────────
333
334 func (fb *funcBuilder) buildBlock(b *syntax.BlockStmt) {
335 if b == nil {
336 return
337 }
338 for _, s := range b.List {
339 fb.buildStmt(s)
340 if fb.currentBlock == nil {
341 return // terminated
342 }
343 }
344 }
345
346 func (fb *funcBuilder) buildStmt(s syntax.Stmt) {
347 if s == nil || fb.currentBlock == nil {
348 return
349 }
350 switch s := s.(type) {
351 case *syntax.EmptyStmt:
352 // nothing
353 case *syntax.ExprStmt:
354 fb.buildExpr(s.X)
355 case *syntax.AssignStmt:
356 fb.buildAssign(s)
357 case *syntax.BlockStmt:
358 fb.buildBlock(s)
359 case *syntax.DeclStmt:
360 for _, d := range s.DeclList {
361 fb.buildLocalDecl(d)
362 }
363 case *syntax.IfStmt:
364 fb.buildIf(s)
365 case *syntax.ForStmt:
366 fb.buildFor(s)
367 case *syntax.SwitchStmt:
368 fb.buildSwitch(s)
369 case *syntax.SelectStmt:
370 fb.buildSelect(s)
371 case *syntax.ReturnStmt:
372 fb.buildReturn(s)
373 case *syntax.BranchStmt:
374 fb.buildBranch(s)
375 case *syntax.LabeledStmt:
376 fb.buildStmt(s.Stmt)
377 case *syntax.SendStmt:
378 ch := fb.buildExpr(s.Chan)
379 val := fb.buildExpr(s.Value)
380 if ch != nil && val != nil {
381 fb.emit(&Send{Chan: ch, X: val})
382 }
383 case *syntax.CallStmt:
384 call, _ := s.Call.(*syntax.CallExpr)
385 switch s.Tok {
386 case syntax.Go:
387 if call != nil {
388 fb.buildGoStmt(call)
389 }
390 case syntax.Defer:
391 if call != nil {
392 fb.buildDeferStmt(call)
393 }
394 default:
395 fb.buildExpr(s.Call)
396 }
397 }
398 }
399
400 func (fb *funcBuilder) buildAssign(s *syntax.AssignStmt) {
401 if s.Rhs == nil {
402 // x++ or x--
403 lv := fb.buildExpr(s.Lhs)
404 if lv == nil {
405 return
406 }
407 one := &Const{typ: lv.Type(), val: constant.MakeInt64(1)}
408 op := token.ADD
409 if s.Op == syntax.Sub {
410 op = token.SUB
411 }
412 bin := &BinOp{Op: op, X: lv, Y: one}
413 bin.typ = lv.Type()
414 bin.name = fb.nextName()
415 fb.emit(bin)
416 fb.buildStore(s.Lhs, bin)
417 return
418 }
419 if s.Op == syntax.Def {
420 fb.buildShortVarDecl(s)
421 return
422 }
423 // compound assignment (+=, -=, etc.): Op != 0 means an operator is applied before =
424 if s.Op != 0 {
425 lv := fb.buildExpr(s.Lhs)
426 rv := fb.buildExpr(s.Rhs)
427 if lv == nil || rv == nil {
428 return
429 }
430 op := compoundOp(s.Op)
431 bin := &BinOp{Op: op, X: lv, Y: rv}
432 bin.typ = lv.Type()
433 bin.name = fb.nextName()
434 fb.emit(bin)
435 fb.buildStore(s.Lhs, bin)
436 return
437 }
438 // plain assignment
439 rhs := fb.buildExpr(s.Rhs)
440 if rhs == nil {
441 return
442 }
443 fb.buildStore(s.Lhs, rhs)
444 }
445
446 // compoundOp translates a syntax.Operator (from a compound assignment like +=)
447 // to the corresponding token.Token for BinOp. The parser stores the base op
448 // (syntax.Add for +=) in AssignStmt.Op; there are no AddAssign etc. constants.
449 func compoundOp(op syntax.Operator) token.Token {
450 switch op {
451 case syntax.Add, syntax.Or: // += and |= both map to ADD (| on strings is concat)
452 return token.ADD
453 case syntax.Sub:
454 return token.SUB
455 case syntax.Mul:
456 return token.MUL
457 case syntax.Div:
458 return token.QUO
459 case syntax.Rem:
460 return token.REM
461 case syntax.And:
462 return token.AND
463 case syntax.Xor:
464 return token.XOR
465 case syntax.Shl:
466 return token.SHL
467 case syntax.Shr:
468 return token.SHR
469 case syntax.AndNot:
470 return token.AND_NOT
471 }
472 return token.ILLEGAL
473 }
474
475 func (fb *funcBuilder) buildStore(lhs syntax.Expr, val Value) {
476 switch lhs := lhs.(type) {
477 case *syntax.Name:
478 if lhs.Value == "_" {
479 return
480 }
481 obj := fb.lookupObject(lhs.Value)
482 if obj == nil {
483 return
484 }
485 if alloc, ok := fb.vars[obj]; ok {
486 fb.emitStore(alloc, val)
487 }
488 case *syntax.Operation:
489 if lhs.Y == nil && lhs.Op == syntax.Mul {
490 // *ptr = val
491 ptr := fb.buildExpr(lhs.X)
492 if ptr != nil {
493 fb.emitStore(ptr, val)
494 }
495 }
496 case *syntax.SelectorExpr:
497 base := fb.buildExpr(lhs.X)
498 if base == nil {
499 return
500 }
501 idx := fb.fieldIndex(base.Type(), lhs.Sel.Value)
502 fa := &FieldAddr{X: base, Field: idx}
503 fa.typ = typecheck.NewPointer(fb.fieldType(base.Type(), idx))
504 fa.name = fb.nextName()
505 fb.emit(fa)
506 fb.emitStore(fa, val)
507 case *syntax.IndexExpr:
508 base := fb.buildExpr(lhs.X)
509 idx := fb.buildExpr(lhs.Index)
510 if base == nil || idx == nil {
511 return
512 }
513 ia := &IndexAddr{X: base, Index: idx}
514 ia.typ = typecheck.NewPointer(elemType(base.Type()))
515 ia.name = fb.nextName()
516 fb.emit(ia)
517 fb.emitStore(ia, val)
518 case *syntax.ListExpr:
519 // multi-assign: a, b = ...
520 // val is a tuple; extract each component
521 for i, e := range lhs.ElemList {
522 ext := &Extract{Tuple: val, Index: i}
523 ext.typ = tupleElemType(val.Type(), i)
524 ext.name = fb.nextName()
525 fb.emit(ext)
526 fb.buildStore(e, ext)
527 }
528 }
529 }
530
531 func (fb *funcBuilder) buildShortVarDecl(s *syntax.AssignStmt) {
532 rhs := fb.buildExpr(s.Rhs)
533 names := exprNames(s.Lhs)
534
535 for i, name := range names {
536 if name.Value == "_" {
537 continue
538 }
539 var typ typecheck.Type
540 if rhs != nil {
541 if i == 0 {
542 typ = rhs.Type()
543 } else if tup, ok := rhs.Type().(*typecheck.Tuple); ok && i < tup.Len() {
544 typ = tup.At(i).Type()
545 }
546 }
547 // Look up existing object from the info.Defs map.
548 var obj typecheck.Object
549 if fb.info != nil {
550 obj = fb.info.Defs[name]
551 }
552 if obj == nil {
553 // fall back to a synthetic var
554 obj = typecheck.NewVar(fb.fn.Pkg.Pkg, name.Value, typ)
555 }
556 alloc := fb.emitAlloc(typ, posFromSyntax(name.Pos()))
557 fb.vars[obj] = alloc
558 // Store the initial value.
559 var initVal Value
560 if rhs != nil {
561 if i == 0 {
562 initVal = rhs
563 } else {
564 ext := &Extract{Tuple: rhs, Index: i}
565 ext.typ = typ
566 ext.name = fb.nextName()
567 fb.emit(ext)
568 initVal = ext
569 }
570 }
571 if initVal != nil {
572 fb.emitStore(alloc, initVal)
573 }
574 }
575 }
576
577 func (fb *funcBuilder) buildLocalDecl(d syntax.Decl) {
578 switch d := d.(type) {
579 case *syntax.VarDecl:
580 var typ typecheck.Type
581 if fb.info != nil && len(d.NameList) > 0 {
582 if obj := fb.info.Defs[d.NameList[0]]; obj != nil {
583 typ = obj.Type()
584 }
585 }
586 initVal := fb.buildExpr(d.Values)
587 for _, name := range d.NameList {
588 if name.Value == "_" {
589 continue
590 }
591 var obj typecheck.Object
592 if fb.info != nil {
593 obj = fb.info.Defs[name]
594 }
595 if obj == nil {
596 obj = typecheck.NewVar(fb.fn.Pkg.Pkg, name.Value, typ)
597 }
598 alloc := fb.emitAlloc(obj.Type(), posFromSyntax(name.Pos()))
599 fb.vars[obj] = alloc
600 fb.fn.Locals = append(fb.fn.Locals, alloc)
601 if initVal != nil {
602 fb.emitStore(alloc, initVal)
603 }
604 }
605 case *syntax.ConstDecl:
606 // Constants don't generate alloca; they're inlined as Const values.
607 case *syntax.TypeDecl:
608 // Local type declarations don't generate code.
609 }
610 }
611
612 // buildIf builds an if statement.
613 func (fb *funcBuilder) buildIf(s *syntax.IfStmt) {
614 // Optional init.
615 if s.Init != nil {
616 fb.buildStmt(s.Init)
617 }
618 if fb.currentBlock == nil {
619 return
620 }
621 cond := fb.buildExpr(s.Cond)
622 if cond == nil {
623 return
624 }
625
626 thenBlock := fb.newBlock("if.then")
627 var elseBlock *BasicBlock
628 doneBlock := fb.newBlock("if.done")
629
630 if s.Else != nil {
631 elseBlock = fb.newBlock("if.else")
632 } else {
633 elseBlock = doneBlock
634 }
635
636 fb.emit(&If{Cond: cond})
637 fb.currentBlock.Succs = append(fb.currentBlock.Succs, thenBlock, elseBlock)
638 thenBlock.Preds = append(thenBlock.Preds, fb.currentBlock)
639 elseBlock.Preds = append(elseBlock.Preds, fb.currentBlock)
640
641 // then branch
642 fb.currentBlock = thenBlock
643 fb.buildBlock(s.Then)
644 if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
645 fb.emit(&Jump{Comment: "if.done"})
646 fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
647 doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
648 }
649
650 // else branch
651 if s.Else != nil {
652 fb.currentBlock = elseBlock
653 fb.buildStmt(s.Else)
654 if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
655 fb.emit(&Jump{Comment: "if.done"})
656 fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
657 doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
658 }
659 }
660
661 fb.currentBlock = doneBlock
662 }
663
664 // buildFor builds a for/range loop.
665 func (fb *funcBuilder) buildFor(s *syntax.ForStmt) {
666 if s.Init != nil {
667 if rc, ok := s.Init.(*syntax.RangeClause); ok {
668 fb.buildRangeLoop(s, rc)
669 return
670 }
671 fb.buildStmt(s.Init)
672 }
673
674 condBlock := fb.newBlock("for.cond")
675 bodyBlock := fb.newBlock("for.body")
676 postBlock := fb.newBlock("for.post")
677 doneBlock := fb.newBlock("for.done")
678
679 // Jump to cond.
680 fb.emit(&Jump{Comment: "for.cond"})
681 fb.currentBlock.Succs = append(fb.currentBlock.Succs, condBlock)
682 condBlock.Preds = append(condBlock.Preds, fb.currentBlock)
683
684 fb.loops = append(fb.loops, loopState{body: bodyBlock, post: postBlock, done: doneBlock})
685
686 // Condition.
687 fb.currentBlock = condBlock
688 if s.Cond != nil {
689 cond := fb.buildExpr(s.Cond)
690 if cond != nil {
691 fb.emit(&If{Cond: cond})
692 fb.currentBlock.Succs = append(fb.currentBlock.Succs, bodyBlock, doneBlock)
693 bodyBlock.Preds = append(bodyBlock.Preds, condBlock)
694 doneBlock.Preds = append(doneBlock.Preds, condBlock)
695 }
696 } else {
697 fb.emit(&Jump{Comment: "for.body"})
698 fb.currentBlock.Succs = append(fb.currentBlock.Succs, bodyBlock)
699 bodyBlock.Preds = append(bodyBlock.Preds, condBlock)
700 }
701
702 // Body.
703 fb.currentBlock = bodyBlock
704 fb.buildBlock(s.Body)
705 if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
706 fb.emit(&Jump{Comment: "for.post"})
707 fb.currentBlock.Succs = append(fb.currentBlock.Succs, postBlock)
708 postBlock.Preds = append(postBlock.Preds, fb.currentBlock)
709 }
710
711 // Post.
712 fb.currentBlock = postBlock
713 if s.Post != nil {
714 fb.buildStmt(s.Post)
715 }
716 if fb.currentBlock != nil {
717 fb.emit(&Jump{Comment: "for.cond"})
718 fb.currentBlock.Succs = append(fb.currentBlock.Succs, condBlock)
719 condBlock.Preds = append(condBlock.Preds, fb.currentBlock)
720 }
721
722 fb.loops = fb.loops[:len(fb.loops)-1]
723 fb.currentBlock = doneBlock
724 }
725
726 func (fb *funcBuilder) buildRangeLoop(s *syntax.ForStmt, rc *syntax.RangeClause) {
727 iterExpr := fb.buildExpr(rc.X)
728 if iterExpr == nil {
729 return
730 }
731
732 r := &Range{X: iterExpr}
733 r.typ = iterType(iterExpr.Type())
734 r.name = fb.nextName()
735 fb.emit(r)
736
737 bodyBlock := fb.newBlock("range.body")
738 doneBlock := fb.newBlock("range.done")
739
740 fb.loops = append(fb.loops, loopState{body: bodyBlock, done: doneBlock})
741
742 // Check done.
743 condBlock := fb.newBlock("range.cond")
744 fb.emit(&Jump{Comment: "range.cond"})
745 fb.currentBlock.Succs = append(fb.currentBlock.Succs, condBlock)
746 condBlock.Preds = append(condBlock.Preds, fb.currentBlock)
747
748 fb.currentBlock = condBlock
749 nxt := &Next{Iter: r, IsString: isStringType(iterExpr.Type())}
750 nxt.typ = nextType(r.typ)
751 nxt.name = fb.nextName()
752 fb.emit(nxt)
753
754 // Extract ok (index 0).
755 okExt := &Extract{Tuple: nxt, Index: 0}
756 okExt.typ = typecheck.Typ[typecheck.Bool]
757 okExt.name = fb.nextName()
758 fb.emit(okExt)
759
760 fb.emit(&If{Cond: okExt})
761 fb.currentBlock.Succs = append(fb.currentBlock.Succs, bodyBlock, doneBlock)
762 bodyBlock.Preds = append(bodyBlock.Preds, condBlock)
763 doneBlock.Preds = append(doneBlock.Preds, condBlock)
764
765 // Body: bind key/value.
766 fb.currentBlock = bodyBlock
767 if rc.Lhs != nil && rc.Def {
768 names := exprNames(rc.Lhs)
769 // index 1 = key, index 2 = value
770 for i, name := range names {
771 if name.Value == "_" {
772 continue
773 }
774 ext := &Extract{Tuple: nxt, Index: i + 1}
775 ext.typ = extractNthType(nxt.typ, i+1)
776 ext.name = fb.nextName()
777 fb.emit(ext)
778 var obj typecheck.Object
779 if fb.info != nil {
780 obj = fb.info.Defs[name]
781 }
782 if obj == nil {
783 obj = typecheck.NewVar(fb.fn.Pkg.Pkg, name.Value, ext.typ)
784 }
785 alloc := fb.emitAlloc(ext.typ, posFromSyntax(name.Pos()))
786 fb.vars[obj] = alloc
787 fb.emitStore(alloc, ext)
788 }
789 }
790 fb.buildBlock(s.Body)
791 if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
792 fb.emit(&Jump{Comment: "range.cond"})
793 fb.currentBlock.Succs = append(fb.currentBlock.Succs, condBlock)
794 condBlock.Preds = append(condBlock.Preds, fb.currentBlock)
795 }
796
797 fb.loops = fb.loops[:len(fb.loops)-1]
798 fb.currentBlock = doneBlock
799 }
800
801 func (fb *funcBuilder) buildSwitch(s *syntax.SwitchStmt) {
802 // Optional init.
803 if s.Init != nil {
804 fb.buildStmt(s.Init)
805 }
806 if fb.currentBlock == nil {
807 return
808 }
809
810 doneBlock := fb.newBlock("switch.done")
811 savedLoops := fb.loops
812 fb.loops = append(fb.loops, loopState{done: doneBlock})
813
814 // Type switch vs expression switch.
815 if s.Tag != nil {
816 if tsg, ok := s.Tag.(*syntax.TypeSwitchGuard); ok {
817 fb.buildTypeSwitch(s, tsg, doneBlock)
818 fb.loops = savedLoops
819 fb.currentBlock = doneBlock
820 return
821 }
822 }
823
824 var tag Value
825 if s.Tag != nil {
826 tag = fb.buildExpr(s.Tag)
827 }
828
829 for _, clause := range s.Body {
830 caseBlock := fb.newBlock("switch.case")
831 nextBlock := fb.newBlock("switch.next")
832
833 if clause.Cases != nil && tag != nil {
834 caseVal := fb.buildExpr(clause.Cases)
835 cmp := &BinOp{Op: token.EQL, X: tag, Y: caseVal}
836 cmp.typ = typecheck.Typ[typecheck.Bool]
837 cmp.name = fb.nextName()
838 fb.emit(cmp)
839 fb.emit(&If{Cond: cmp})
840 fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock, nextBlock)
841 caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock)
842 nextBlock.Preds = append(nextBlock.Preds, fb.currentBlock)
843 } else {
844 // default or no tag
845 fb.emit(&Jump{Comment: "switch.case"})
846 fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock)
847 caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock)
848 }
849
850 fb.currentBlock = caseBlock
851 for _, stmt := range clause.Body {
852 fb.buildStmt(stmt)
853 if fb.currentBlock == nil {
854 break
855 }
856 }
857 if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
858 fb.emit(&Jump{Comment: "switch.done"})
859 fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
860 doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
861 }
862 fb.currentBlock = nextBlock
863 }
864
865 // Fall through last nextBlock to done.
866 if fb.currentBlock != nil {
867 fb.emit(&Jump{Comment: "switch.done"})
868 fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
869 doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
870 }
871
872 fb.loops = savedLoops
873 fb.currentBlock = doneBlock
874 }
875
876 func (fb *funcBuilder) buildTypeSwitch(s *syntax.SwitchStmt, tsg *syntax.TypeSwitchGuard, doneBlock *BasicBlock) {
877 x := fb.buildExpr(tsg.X)
878 for _, clause := range s.Body {
879 caseBlock := fb.newBlock("typeswitch.case")
880 nextBlock := fb.newBlock("typeswitch.next")
881
882 if clause.Cases != nil {
883 var assertedType typecheck.Type
884 if fb.info != nil {
885 tv := fb.info.Types[clause.Cases]
886 assertedType = tv.Type
887 }
888 if assertedType == nil {
889 assertedType = typecheck.Typ[typecheck.Invalid]
890 }
891 ta := &TypeAssert{X: x, AssertedType: assertedType, CommaOk: true}
892 ta.typ = typecheck.NewTuple(
893 typecheck.NewVar(nil, "val", assertedType),
894 typecheck.NewVar(nil, "ok", typecheck.Typ[typecheck.Bool]),
895 )
896 ta.name = fb.nextName()
897 fb.emit(ta)
898 okExt := &Extract{Tuple: ta, Index: 1}
899 okExt.typ = typecheck.Typ[typecheck.Bool]
900 okExt.name = fb.nextName()
901 fb.emit(okExt)
902 fb.emit(&If{Cond: okExt})
903 fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock, nextBlock)
904 caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock)
905 nextBlock.Preds = append(nextBlock.Preds, fb.currentBlock)
906
907 // Bind guard variable in case block.
908 fb.currentBlock = caseBlock
909 if tsg.Lhs != nil {
910 guardVal := &Extract{Tuple: ta, Index: 0}
911 guardVal.typ = assertedType
912 guardVal.name = fb.nextName()
913 fb.emit(guardVal)
914 obj := typecheck.NewVar(fb.fn.Pkg.Pkg, tsg.Lhs.Value, assertedType)
915 alloc := fb.emitAlloc(assertedType, posFromSyntax(tsg.Lhs.Pos()))
916 fb.vars[obj] = alloc
917 fb.emitStore(alloc, guardVal)
918 }
919 } else {
920 fb.emit(&Jump{Comment: "typeswitch.case"})
921 fb.currentBlock.Succs = append(fb.currentBlock.Succs, caseBlock)
922 caseBlock.Preds = append(caseBlock.Preds, fb.currentBlock)
923 fb.currentBlock = caseBlock
924 }
925
926 for _, stmt := range clause.Body {
927 fb.buildStmt(stmt)
928 if fb.currentBlock == nil {
929 break
930 }
931 }
932 if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
933 fb.emit(&Jump{Comment: "switch.done"})
934 fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
935 doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
936 }
937 fb.currentBlock = nextBlock
938 }
939 if fb.currentBlock != nil {
940 fb.emit(&Jump{Comment: "switch.done"})
941 fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
942 doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
943 }
944 }
945
946 func (fb *funcBuilder) buildSelect(s *syntax.SelectStmt) {
947 doneBlock := fb.newBlock("select.done")
948
949 var states []*SelectState
950 caseBlocks := make([]*BasicBlock, len(s.Body))
951 for i, clause := range s.Body {
952 cb := fb.newBlock("select.case")
953 caseBlocks[i] = cb
954 state := &SelectState{}
955 if clause.Comm != nil {
956 switch comm := clause.Comm.(type) {
957 case *syntax.SendStmt:
958 state.Dir = token.ARROW // SEND direction marker
959 state.Chan = fb.buildExpr(comm.Chan)
960 state.Send = fb.buildExpr(comm.Value)
961 case *syntax.AssignStmt:
962 // recv: v, ok := <-ch or v := <-ch
963 var chanExpr syntax.Expr
964 if op, ok2 := comm.Rhs.(*syntax.Operation); ok2 && op.Y == nil && op.Op == syntax.Recv {
965 chanExpr = op.X
966 }
967 if chanExpr != nil {
968 state.Dir = token.ILLEGAL // RECV direction marker
969 state.Chan = fb.buildExpr(chanExpr)
970 }
971 case *syntax.ExprStmt:
972 if op, ok2 := comm.X.(*syntax.Operation); ok2 && op.Y == nil && op.Op == syntax.Recv {
973 state.Dir = token.ILLEGAL // RECV direction marker
974 state.Chan = fb.buildExpr(op.X)
975 }
976 }
977 }
978 states = append(states, state)
979 }
980
981 sel := &Select{States: states, Blocking: true}
982 sel.typ = selectResultType(states)
983 sel.name = fb.nextName()
984 fb.emit(sel)
985
986 for i, clause := range s.Body {
987 fb.currentBlock = caseBlocks[i]
988 for _, stmt := range clause.Body {
989 fb.buildStmt(stmt)
990 if fb.currentBlock == nil {
991 break
992 }
993 }
994 if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
995 fb.emit(&Jump{Comment: "select.done"})
996 fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
997 doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
998 }
999 }
1000
1001 fb.currentBlock = doneBlock
1002 }
1003
1004 func (fb *funcBuilder) buildReturn(s *syntax.ReturnStmt) {
1005 var vals []Value
1006 if s.Results != nil {
1007 v := fb.buildExpr(s.Results)
1008 if v != nil {
1009 vals = append(vals, v)
1010 }
1011 }
1012 fb.emitReturn(vals, posFromSyntax(s.Pos()))
1013 }
1014
1015 func (fb *funcBuilder) buildBranch(s *syntax.BranchStmt) {
1016 if fb.currentBlock == nil {
1017 return
1018 }
1019 switch s.Tok {
1020 case syntax.Break:
1021 if len(fb.loops) > 0 {
1022 doneBlock := fb.loops[len(fb.loops)-1].done
1023 fb.emit(&Jump{Comment: "break"})
1024 fb.currentBlock.Succs = append(fb.currentBlock.Succs, doneBlock)
1025 doneBlock.Preds = append(doneBlock.Preds, fb.currentBlock)
1026 fb.currentBlock = nil
1027 }
1028 case syntax.Continue:
1029 for i := len(fb.loops) - 1; i >= 0; i-- {
1030 postBlock := fb.loops[i].post
1031 if postBlock == nil {
1032 postBlock = fb.loops[i].body
1033 }
1034 if postBlock == nil {
1035 continue // select-loop state has no post/body; skip upward
1036 }
1037 fb.emit(&Jump{Comment: "continue"})
1038 fb.currentBlock.Succs = append(fb.currentBlock.Succs, postBlock)
1039 postBlock.Preds = append(postBlock.Preds, fb.currentBlock)
1040 fb.currentBlock = nil
1041 break
1042 }
1043 case syntax.Return:
1044 fb.emitReturn(nil, 0)
1045 }
1046 }
1047
1048 func (fb *funcBuilder) buildGoStmt(call *syntax.CallExpr) {
1049 fn := fb.buildExpr(call.Fun)
1050 if fn == nil {
1051 return
1052 }
1053 args := fb.buildArgs(call.ArgList)
1054 g := &Go{Call: CallCommon{Value: fn, Args: args}}
1055 fb.emit(g)
1056 }
1057
1058 func (fb *funcBuilder) buildDeferStmt(call *syntax.CallExpr) {
1059 fn := fb.buildExpr(call.Fun)
1060 if fn == nil {
1061 return
1062 }
1063 args := fb.buildArgs(call.ArgList)
1064 d := &Defer{Call: CallCommon{Value: fn, Args: args}}
1065 fb.emit(d)
1066 fb.deferred++
1067 }
1068
1069 // ─── Expression builder ───────────────────────────────────────────────────────
1070
1071 // buildExpr compiles an expression and returns the resulting Value.
1072 // Returns nil for void expressions or errors.
1073 func (fb *funcBuilder) buildExpr(e syntax.Expr) Value {
1074 if e == nil || fb.currentBlock == nil {
1075 return nil
1076 }
1077 switch e := e.(type) {
1078 case *syntax.Name:
1079 return fb.buildIdent(e)
1080 case *syntax.BasicLit:
1081 return fb.buildLit(e)
1082 case *syntax.Operation:
1083 return fb.buildOperation(e)
1084 case *syntax.CallExpr:
1085 return fb.buildCall(e)
1086 case *syntax.SelectorExpr:
1087 return fb.buildSelector(e)
1088 case *syntax.IndexExpr:
1089 return fb.buildIndex(e)
1090 case *syntax.SliceExpr:
1091 return fb.buildSlice(e)
1092 case *syntax.AssertExpr:
1093 return fb.buildAssert(e)
1094 case *syntax.CompositeLit:
1095 return fb.buildCompositeLit(e)
1096 case *syntax.FuncLit:
1097 return fb.buildFuncLit(e)
1098 case *syntax.ParenExpr:
1099 return fb.buildExpr(e.X)
1100 case *syntax.ListExpr:
1101 // Multiple return values — return the last (callers handle multi-return differently)
1102 var last Value
1103 for _, el := range e.ElemList {
1104 last = fb.buildExpr(el)
1105 }
1106 return last
1107 case *syntax.KeyValueExpr:
1108 return fb.buildExpr(e.Value)
1109 }
1110 return nil
1111 }
1112
1113 func (fb *funcBuilder) buildIdent(e *syntax.Name) Value {
1114 if e.Value == "_" || e.Value == "nil" {
1115 return &Const{typ: nil, val: nil}
1116 }
1117 obj := fb.lookupObject(e.Value)
1118 if obj == nil {
1119 // Could be a builtin.
1120 return fb.builtinValue(e.Value)
1121 }
1122 switch obj := obj.(type) {
1123 case *typecheck.Var:
1124 if alloc, ok := fb.vars[obj]; ok {
1125 return fb.emitLoad(alloc, obj.Type())
1126 }
1127 // Package-level global.
1128 if g, ok := fb.fn.Pkg.Members[e.Value].(*Global); ok {
1129 return fb.emitLoad(g, obj.Type())
1130 }
1131 return &Const{typ: obj.Type(), val: nil}
1132 case *typecheck.Const:
1133 var cval constant.Value
1134 if obj.Val() != nil {
1135 cval, _ = obj.Val().(constant.Value)
1136 }
1137 return &Const{typ: obj.Type(), val: cval}
1138 case *typecheck.Func:
1139 fn, _ := fb.fn.Pkg.Members[e.Value].(*Function)
1140 if fn != nil {
1141 return fn
1142 }
1143 return &Const{typ: obj.Type(), val: nil}
1144 case *typecheck.TypeName:
1145 return nil // type name used as expression (e.g., conversion)
1146 }
1147 return nil
1148 }
1149
1150 func (fb *funcBuilder) builtinValue(name string) Value {
1151 id, ok := builtinID(name)
1152 if !ok {
1153 return nil
1154 }
1155 return &Builtin{id: id, name: name}
1156 }
1157
1158 func (fb *funcBuilder) buildLit(e *syntax.BasicLit) Value {
1159 switch e.Kind {
1160 case syntax.IntLit:
1161 n, _ := constant.Val(constant.MakeFromLiteral(e.Value, token.INT, 0)).(int64)
1162 return &Const{typ: typecheck.Typ[typecheck.UntypedInt], val: constant.MakeInt64(n)}
1163 case syntax.FloatLit:
1164 return &Const{
1165 typ: typecheck.Typ[typecheck.UntypedFloat],
1166 val: constant.MakeFromLiteral(e.Value, token.FLOAT, 0),
1167 }
1168 case syntax.StringLit:
1169 s := e.Value
1170 if len(s) >= 2 {
1171 s = s[1 : len(s)-1] // strip quotes (simplified)
1172 }
1173 return &Const{typ: typecheck.Typ[typecheck.UntypedString], val: constant.MakeString(s)}
1174 case syntax.RuneLit:
1175 return &Const{
1176 typ: typecheck.Typ[typecheck.UntypedRune],
1177 val: constant.MakeFromLiteral(e.Value, token.CHAR, 0),
1178 }
1179 }
1180 return nil
1181 }
1182
1183 func (fb *funcBuilder) buildOperation(e *syntax.Operation) Value {
1184 if e.Y == nil {
1185 // unary
1186 x := fb.buildExpr(e.X)
1187 if x == nil {
1188 return nil
1189 }
1190 op := syntaxOpToToken(e.Op, true)
1191 if op == token.ARROW {
1192 // receive
1193 u := &UnOp{Op: token.ARROW, X: x}
1194 u.typ = chanElemType(x.Type())
1195 u.name = fb.nextName()
1196 fb.emit(u)
1197 return u
1198 }
1199 if op == token.AND {
1200 // address-of
1201 if name, ok := e.X.(*syntax.Name); ok {
1202 obj := fb.lookupObject(name.Value)
1203 if obj != nil {
1204 if alloc, ok2 := fb.vars[obj]; ok2 {
1205 return alloc
1206 }
1207 if g, ok2 := fb.fn.Pkg.Members[name.Value].(*Global); ok2 {
1208 return g
1209 }
1210 }
1211 }
1212 // heap alloc
1213 inner := x
1214 a := &Alloc{Heap: true}
1215 a.typ = typecheck.NewPointer(inner.Type())
1216 a.name = fb.nextName()
1217 fb.emit(a)
1218 fb.emitStore(a, inner)
1219 return a
1220 }
1221 u := &UnOp{Op: op, X: x}
1222 u.typ = x.Type()
1223 u.name = fb.nextName()
1224 fb.emit(u)
1225 return u
1226 }
1227 // binary
1228 x := fb.buildExpr(e.X)
1229 y := fb.buildExpr(e.Y)
1230 if x == nil || y == nil {
1231 return nil
1232 }
1233 op := syntaxOpToToken(e.Op, false)
1234 b := &BinOp{Op: op, X: x, Y: y}
1235 b.name = fb.nextName()
1236 switch op {
1237 case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ,
1238 token.LAND, token.LOR:
1239 b.typ = typecheck.Typ[typecheck.Bool]
1240 default:
1241 if x.Type() != nil {
1242 b.typ = x.Type()
1243 } else if y.Type() != nil {
1244 b.typ = y.Type()
1245 }
1246 }
1247 fb.emit(b)
1248 return b
1249 }
1250
1251 func (fb *funcBuilder) buildCall(e *syntax.CallExpr) Value {
1252 fn := fb.buildExpr(e.Fun)
1253 args := fb.buildArgs(e.ArgList)
1254
1255 if fn == nil {
1256 return nil
1257 }
1258 if _, ok := fn.(*Builtin); ok {
1259 return fb.buildBuiltinCall(e, fn.(*Builtin), args)
1260 }
1261
1262 var retType typecheck.Type
1263 if fn.Type() != nil {
1264 if sig, ok := fn.Type().Underlying().(*typecheck.Signature); ok {
1265 if sig.Results() != nil && sig.Results().Len() == 1 {
1266 retType = sig.Results().At(0).Type()
1267 } else if sig.Results() != nil && sig.Results().Len() > 1 {
1268 retType = sig.Results()
1269 }
1270 }
1271 }
1272
1273 call := &Call{Call: CallCommon{Value: fn, Args: args}}
1274 call.typ = retType
1275 call.name = fb.nextName()
1276 fb.emit(call)
1277
1278 if retType == nil {
1279 return nil // void call
1280 }
1281 return call
1282 }
1283
1284 func (fb *funcBuilder) buildArgs(argList []syntax.Expr) []Value {
1285 args := make([]Value, 0, len(argList))
1286 for _, a := range argList {
1287 v := fb.buildExpr(a)
1288 if v != nil {
1289 args = append(args, v)
1290 }
1291 }
1292 return args
1293 }
1294
1295 func (fb *funcBuilder) buildBuiltinCall(e *syntax.CallExpr, b *Builtin, args []Value) Value {
1296 switch b.id {
1297 case BuiltinLen, BuiltinCap:
1298 call := &Call{Call: CallCommon{Value: b, Args: args}}
1299 call.typ = typecheck.Typ[typecheck.Int32]
1300 call.name = fb.nextName()
1301 fb.emit(call)
1302 return call
1303 case BuiltinAppend:
1304 call := &Call{Call: CallCommon{Value: b, Args: args}}
1305 if len(args) > 0 {
1306 call.typ = args[0].Type()
1307 }
1308 call.name = fb.nextName()
1309 fb.emit(call)
1310 return call
1311 case BuiltinMake:
1312 if len(e.ArgList) == 0 {
1313 return nil
1314 }
1315 typ := fb.resolveType(e.ArgList[0])
1316 // args already has type expr filtered (buildExpr returns nil for types),
1317 // so args == the size arguments.
1318 return fb.emitMake(typ, args)
1319 case BuiltinNew:
1320 if len(e.ArgList) == 0 {
1321 return nil
1322 }
1323 typ := fb.resolveType(e.ArgList[0])
1324 a := &Alloc{Heap: true}
1325 a.typ = typecheck.NewPointer(typ)
1326 a.name = fb.nextName()
1327 fb.emit(a)
1328 return a
1329 case BuiltinPanic:
1330 if len(args) > 0 {
1331 fb.emit(&Panic{X: args[0]})
1332 fb.currentBlock = nil
1333 }
1334 return nil
1335 case BuiltinClose, BuiltinDelete, BuiltinClear, BuiltinPrint, BuiltinPrintln:
1336 call := &Call{Call: CallCommon{Value: b, Args: args}}
1337 call.name = fb.nextName()
1338 fb.emit(call)
1339 return nil
1340 case BuiltinCopy:
1341 call := &Call{Call: CallCommon{Value: b, Args: args}}
1342 call.typ = typecheck.Typ[typecheck.Int32]
1343 call.name = fb.nextName()
1344 fb.emit(call)
1345 return call
1346 case BuiltinRecover:
1347 call := &Call{Call: CallCommon{Value: b, Args: args}}
1348 call.typ = typecheck.Typ[typecheck.String] // any
1349 call.name = fb.nextName()
1350 fb.emit(call)
1351 return call
1352 }
1353 call := &Call{Call: CallCommon{Value: b, Args: args}}
1354 call.name = fb.nextName()
1355 fb.emit(call)
1356 return call
1357 }
1358
1359 func (fb *funcBuilder) emitMake(typ typecheck.Type, sizeArgs []Value) Value {
1360 if typ == nil {
1361 return nil
1362 }
1363 switch typ.Underlying().(type) {
1364 case *typecheck.Slice:
1365 ms := &MakeSlice{}
1366 ms.typ = typ
1367 ms.name = fb.nextName()
1368 if len(sizeArgs) > 0 {
1369 ms.Len = sizeArgs[0]
1370 }
1371 if len(sizeArgs) > 1 {
1372 ms.Cap = sizeArgs[1]
1373 }
1374 fb.emit(ms)
1375 return ms
1376 case *typecheck.Map:
1377 mm := &MakeMap{}
1378 mm.typ = typ
1379 mm.name = fb.nextName()
1380 if len(sizeArgs) > 0 {
1381 mm.Reserve = sizeArgs[0]
1382 }
1383 fb.emit(mm)
1384 return mm
1385 case *typecheck.Chan:
1386 mc := &MakeChan{}
1387 mc.typ = typ
1388 mc.name = fb.nextName()
1389 if len(sizeArgs) > 0 {
1390 mc.Size = sizeArgs[0]
1391 }
1392 fb.emit(mc)
1393 return mc
1394 }
1395 return nil
1396 }
1397
1398 func (fb *funcBuilder) buildSelector(e *syntax.SelectorExpr) Value {
1399 x := fb.buildExpr(e.X)
1400 if x == nil || x.Type() == nil {
1401 // Package-qualified name.
1402 if name, ok := e.X.(*syntax.Name); ok {
1403 obj := fb.lookupObject(name.Value)
1404 if _, ok := obj.(*typecheck.PkgName); ok {
1405 // imported name — look up in that package's members
1406 // (simplified: return nil for now; proper resolution in B3)
1407 return nil
1408 }
1409 }
1410 return nil
1411 }
1412 // field or method
1413 fieldIdx := fb.fieldIndex(x.Type(), e.Sel.Value)
1414 if fieldIdx >= 0 {
1415 fa := &FieldAddr{X: x, Field: fieldIdx}
1416 fa.typ = typecheck.NewPointer(fb.fieldType(x.Type(), fieldIdx))
1417 fa.name = fb.nextName()
1418 fb.emit(fa)
1419 return fb.emitLoad(fa, fb.fieldType(x.Type(), fieldIdx))
1420 }
1421 // method value
1422 return nil
1423 }
1424
1425 func (fb *funcBuilder) buildIndex(e *syntax.IndexExpr) Value {
1426 x := fb.buildExpr(e.X)
1427 idx := fb.buildExpr(e.Index)
1428 if x == nil || idx == nil {
1429 return nil
1430 }
1431 if x.Type() == nil {
1432 return nil
1433 }
1434 switch t := x.Type().Underlying().(type) {
1435 case *typecheck.Map:
1436 l := &Lookup{X: x, Index: idx}
1437 l.typ = t.Elem()
1438 l.name = fb.nextName()
1439 fb.emit(l)
1440 return l
1441 default:
1442 ia := &IndexAddr{X: x, Index: idx}
1443 ia.typ = typecheck.NewPointer(elemType(x.Type()))
1444 ia.name = fb.nextName()
1445 fb.emit(ia)
1446 return fb.emitLoad(ia, elemType(x.Type()))
1447 }
1448 }
1449
1450 func (fb *funcBuilder) buildSlice(e *syntax.SliceExpr) Value {
1451 x := fb.buildExpr(e.X)
1452 if x == nil {
1453 return nil
1454 }
1455 sl := &Slice{X: x}
1456 if len(e.Index) > 0 && e.Index[0] != nil {
1457 sl.Low = fb.buildExpr(e.Index[0])
1458 }
1459 if len(e.Index) > 1 && e.Index[1] != nil {
1460 sl.High = fb.buildExpr(e.Index[1])
1461 }
1462 if len(e.Index) > 2 && e.Index[2] != nil {
1463 sl.Max = fb.buildExpr(e.Index[2])
1464 }
1465 sl.typ = sliceOf(x.Type())
1466 sl.name = fb.nextName()
1467 fb.emit(sl)
1468 return sl
1469 }
1470
1471 func (fb *funcBuilder) buildAssert(e *syntax.AssertExpr) Value {
1472 x := fb.buildExpr(e.X)
1473 assertedType := fb.resolveType(e.Type)
1474 if x == nil {
1475 return nil
1476 }
1477 ta := &TypeAssert{X: x, AssertedType: assertedType, CommaOk: false}
1478 ta.typ = assertedType
1479 ta.name = fb.nextName()
1480 fb.emit(ta)
1481 return ta
1482 }
1483
1484 func (fb *funcBuilder) buildCompositeLit(e *syntax.CompositeLit) Value {
1485 var typ typecheck.Type
1486 if e.Type != nil {
1487 typ = fb.resolveType(e.Type)
1488 }
1489 if typ == nil {
1490 return nil
1491 }
1492 alloc := fb.emitAlloc(typ, posFromSyntax(e.Pos()))
1493 for _, el := range e.ElemList {
1494 if kv, ok := el.(*syntax.KeyValueExpr); ok {
1495 // struct field or map key
1496 if _, ok2 := typ.Underlying().(*typecheck.Struct); ok2 {
1497 idx := fb.fieldIndex(typ, kv.Key.(*syntax.Name).Value)
1498 if idx >= 0 {
1499 fa := &FieldAddr{X: alloc, Field: idx}
1500 fa.typ = typecheck.NewPointer(fb.fieldType(typ, idx))
1501 fa.name = fb.nextName()
1502 fb.emit(fa)
1503 v := fb.buildExpr(kv.Value)
1504 if v != nil {
1505 fb.emitStore(fa, v)
1506 }
1507 }
1508 } else if _, ok2 := typ.Underlying().(*typecheck.Map); ok2 {
1509 k := fb.buildExpr(kv.Key)
1510 v := fb.buildExpr(kv.Value)
1511 if k != nil && v != nil {
1512 fb.emit(&MapUpdate{Map: alloc, Key: k, Value: v})
1513 }
1514 }
1515 } else {
1516 v := fb.buildExpr(el)
1517 _ = v
1518 }
1519 }
1520 return fb.emitLoad(alloc, typ)
1521 }
1522
1523 func (fb *funcBuilder) buildFuncLit(e *syntax.FuncLit) Value {
1524 // Create an anonymous function member.
1525 var sig *typecheck.Signature
1526 if fb.info != nil {
1527 if tv, ok := fb.info.Types[e]; ok {
1528 sig, _ = tv.Type.(*typecheck.Signature)
1529 }
1530 }
1531 name := fb.fn.name + "$" + itoa(len(fb.fn.AnonFuncs)+1)
1532 anon := &Function{
1533 name: name,
1534 Signature: sig,
1535 pos: posFromSyntax(e.Pos()),
1536 Pkg: fb.fn.Pkg,
1537 Prog: fb.fn.Prog,
1538 parent: fb.fn,
1539 }
1540 fb.fn.AnonFuncs = append(fb.fn.AnonFuncs, anon)
1541
1542 // Build anon's body.
1543 ab := newFuncBuilder(anon, fb.info)
1544 d := &syntax.FuncDecl{
1545 Name: &syntax.Name{Value: name},
1546 Type: e.Type,
1547 Body: e.Body,
1548 }
1549 ab.buildBody(d)
1550
1551 // Create closure if there are free variables.
1552 if len(anon.FreeVars) == 0 {
1553 return anon
1554 }
1555 bindings := make([]Value, len(anon.FreeVars))
1556 for i, fv := range anon.FreeVars {
1557 obj := fb.lookupObject(fv.name)
1558 if obj != nil {
1559 if alloc, ok := fb.vars[obj]; ok {
1560 bindings[i] = alloc
1561 }
1562 }
1563 }
1564 mc := &MakeClosure{Fn: anon, Bindings: bindings}
1565 mc.typ = sig
1566 mc.name = fb.nextName()
1567 fb.emit(mc)
1568 return mc
1569 }
1570
1571 // ─── Helpers ──────────────────────────────────────────────────────────────────
1572
1573 func (fb *funcBuilder) lookupObject(name string) typecheck.Object {
1574 // Look in local vars first.
1575 for obj := range fb.vars {
1576 if obj.Name() == name {
1577 return obj
1578 }
1579 }
1580 // Package scope.
1581 if fb.fn.Pkg != nil {
1582 if _, obj := fb.fn.Pkg.Pkg.Scope().LookupParent(name); obj != nil {
1583 return obj
1584 }
1585 }
1586 return nil
1587 }
1588
1589 func (fb *funcBuilder) lookupVar(name string) *typecheck.Var {
1590 obj := fb.lookupObject(name)
1591 v, _ := obj.(*typecheck.Var)
1592 return v
1593 }
1594
1595 func (fb *funcBuilder) lookupVarByType(name string, typ typecheck.Type) typecheck.Object {
1596 // For parameter binding: find the Var in info.Defs or synthesize one.
1597 return typecheck.NewVar(fb.fn.Pkg.Pkg, name, typ)
1598 }
1599
1600 func (fb *funcBuilder) resolveType(e syntax.Expr) typecheck.Type {
1601 if fb.info != nil {
1602 if tv, ok := fb.info.Types[e]; ok && tv.Type != nil {
1603 return tv.Type
1604 }
1605 }
1606 return nil
1607 }
1608
1609 func (fb *funcBuilder) fieldIndex(t typecheck.Type, name string) int {
1610 if t == nil {
1611 return -1
1612 }
1613 // dereference pointer
1614 if pt, ok := t.Underlying().(*typecheck.Pointer); ok {
1615 t = pt.Elem()
1616 }
1617 if st, ok := t.Underlying().(*typecheck.Struct); ok {
1618 for i := 0; i < st.NumFields(); i++ {
1619 if st.Field(i).Name() == name {
1620 return i
1621 }
1622 }
1623 }
1624 return -1
1625 }
1626
1627 func (fb *funcBuilder) fieldType(t typecheck.Type, idx int) typecheck.Type {
1628 if t == nil {
1629 return nil
1630 }
1631 if pt, ok := t.Underlying().(*typecheck.Pointer); ok {
1632 t = pt.Elem()
1633 }
1634 if st, ok := t.Underlying().(*typecheck.Struct); ok {
1635 if idx >= 0 && idx < st.NumFields() {
1636 return st.Field(idx).Type()
1637 }
1638 }
1639 return nil
1640 }
1641
1642 // ─── Type helpers ─────────────────────────────────────────────────────────────
1643
1644 func elemType(t typecheck.Type) typecheck.Type {
1645 if t == nil {
1646 return nil
1647 }
1648 switch t := t.Underlying().(type) {
1649 case *typecheck.Slice:
1650 return t.Elem()
1651 case *typecheck.Array:
1652 return t.Elem()
1653 case *typecheck.Map:
1654 return t.Elem()
1655 case *typecheck.Pointer:
1656 return t.Elem()
1657 }
1658 return nil
1659 }
1660
1661 func chanElemType(t typecheck.Type) typecheck.Type {
1662 if t == nil {
1663 return nil
1664 }
1665 if ch, ok := t.Underlying().(*typecheck.Chan); ok {
1666 return ch.Elem()
1667 }
1668 return nil
1669 }
1670
1671 func isStringType(t typecheck.Type) bool {
1672 if t == nil {
1673 return false
1674 }
1675 if b, ok := t.Underlying().(*typecheck.Basic); ok {
1676 return b.Info()&typecheck.IsString != 0
1677 }
1678 return false
1679 }
1680
1681 func sliceOf(t typecheck.Type) typecheck.Type {
1682 if t == nil {
1683 return nil
1684 }
1685 switch t := t.Underlying().(type) {
1686 case *typecheck.Slice:
1687 return t
1688 case *typecheck.Array:
1689 return typecheck.NewSlice(t.Elem())
1690 }
1691 return typecheck.NewSlice(typecheck.Typ[typecheck.Uint8])
1692 }
1693
1694 func tupleElemType(t typecheck.Type, i int) typecheck.Type {
1695 if t == nil {
1696 return nil
1697 }
1698 if tup, ok := t.(*typecheck.Tuple); ok && i < tup.Len() {
1699 return tup.At(i).Type()
1700 }
1701 return nil
1702 }
1703
1704 func extractNthType(t typecheck.Type, n int) typecheck.Type {
1705 return tupleElemType(t, n)
1706 }
1707
1708 func iterType(t typecheck.Type) typecheck.Type {
1709 // Range iterator is an opaque type; approximate with a struct.
1710 return typecheck.Typ[typecheck.Invalid]
1711 }
1712
1713 func nextType(iterT typecheck.Type) typecheck.Type {
1714 // next(iter) returns a tuple (bool, key, val) — approximate.
1715 return typecheck.NewTuple(
1716 typecheck.NewVar(nil, "ok", typecheck.Typ[typecheck.Bool]),
1717 typecheck.NewVar(nil, "k", typecheck.Typ[typecheck.Int32]),
1718 typecheck.NewVar(nil, "v", typecheck.Typ[typecheck.Invalid]),
1719 )
1720 }
1721
1722 func selectResultType(states []*SelectState) typecheck.Type {
1723 return typecheck.NewTuple(
1724 typecheck.NewVar(nil, "index", typecheck.Typ[typecheck.Int32]),
1725 typecheck.NewVar(nil, "recvOk", typecheck.Typ[typecheck.Bool]),
1726 )
1727 }
1728
1729 // ─── Operator mapping ─────────────────────────────────────────────────────────
1730
1731 func syntaxOpToToken(op syntax.Operator, unary bool) token.Token {
1732 if unary {
1733 switch op {
1734 case syntax.Not:
1735 return token.NOT
1736 case syntax.Recv:
1737 return token.ARROW
1738 case syntax.And:
1739 return token.AND
1740 case syntax.Mul:
1741 return token.MUL
1742 case syntax.Sub:
1743 return token.SUB
1744 case syntax.Xor:
1745 return token.XOR
1746 }
1747 }
1748 switch op {
1749 case syntax.Add, syntax.Or:
1750 return token.ADD // Moxie: | on strings is concat
1751 case syntax.Sub:
1752 return token.SUB
1753 case syntax.Mul:
1754 return token.MUL
1755 case syntax.Div:
1756 return token.QUO
1757 case syntax.Rem:
1758 return token.REM
1759 case syntax.And:
1760 return token.AND
1761 case syntax.Xor:
1762 return token.XOR
1763 case syntax.Shl:
1764 return token.SHL
1765 case syntax.Shr:
1766 return token.SHR
1767 case syntax.AndNot:
1768 return token.AND_NOT
1769 case syntax.OrOr:
1770 return token.LOR
1771 case syntax.AndAnd:
1772 return token.LAND
1773 case syntax.Eql:
1774 return token.EQL
1775 case syntax.Neq:
1776 return token.NEQ
1777 case syntax.Lss:
1778 return token.LSS
1779 case syntax.Leq:
1780 return token.LEQ
1781 case syntax.Gtr:
1782 return token.GTR
1783 case syntax.Geq:
1784 return token.GEQ
1785 }
1786 return token.ILLEGAL
1787 }
1788
1789 // ─── Builtin ID lookup ────────────────────────────────────────────────────────
1790
1791 func builtinID(name string) (BuiltinID, bool) {
1792 switch name {
1793 case "append":
1794 return BuiltinAppend, true
1795 case "cap":
1796 return BuiltinCap, true
1797 case "clear":
1798 return BuiltinClear, true
1799 case "close":
1800 return BuiltinClose, true
1801 case "copy":
1802 return BuiltinCopy, true
1803 case "delete":
1804 return BuiltinDelete, true
1805 case "len":
1806 return BuiltinLen, true
1807 case "make":
1808 return BuiltinMake, true
1809 case "max":
1810 return BuiltinMax, true
1811 case "min":
1812 return BuiltinMin, true
1813 case "new":
1814 return BuiltinNew, true
1815 case "panic":
1816 return BuiltinPanic, true
1817 case "print":
1818 return BuiltinPrint, true
1819 case "println":
1820 return BuiltinPrintln, true
1821 case "recover":
1822 return BuiltinRecover, true
1823 }
1824 return 0, false
1825 }
1826
1827 // ─── Misc utilities ───────────────────────────────────────────────────────────
1828
1829 func posFromSyntax(p syntax.Pos) token.Pos {
1830 // syntax.Pos is a struct (base,line,col); full mapping via fset requires
1831 // the adapter's file. For B2, positions are debug-only; return NoPos.
1832 // B3 will thread fset through the builder for accurate positions.
1833 return token.NoPos
1834 }
1835
1836 func itoa(n int) string {
1837 if n == 0 {
1838 return "0"
1839 }
1840 buf := [20]byte{}
1841 pos := len(buf)
1842 for n > 0 {
1843 pos--
1844 buf[pos] = byte('0' + n%10)
1845 n /= 10
1846 }
1847 return string(buf[pos:])
1848 }
1849
1850 func exprNames(e syntax.Expr) []*syntax.Name {
1851 if e == nil {
1852 return nil
1853 }
1854 if n, ok := e.(*syntax.Name); ok {
1855 return []*syntax.Name{n}
1856 }
1857 if l, ok := e.(*syntax.ListExpr); ok {
1858 var names []*syntax.Name
1859 for _, el := range l.ElemList {
1860 if n, ok := el.(*syntax.Name); ok {
1861 names = append(names, n)
1862 }
1863 }
1864 return names
1865 }
1866 return nil
1867 }
1868