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