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