ssa_func.mx raw

   1  package ssa
   2  
   3  import (
   4  	"git.smesh.lol/moxie/pkg/token"
   5  	"git.smesh.lol/moxie/pkg/syntax"
   6  	"git.smesh.lol/moxie/pkg/types"
   7  )
   8  
   9  func newSSAFuncBuilder(fn *SSAFunction, info *types.Info) (p *ssaFuncBuilder) {
  10  	return &ssaFuncBuilder{
  11  		fn:         fn,
  12  		info:       info,
  13  		vars:       map[types.Object]*SSAAlloc{},
  14  		localTypes: map[string]types.Type{},
  15  	}
  16  }
  17  
  18  func (fb *ssaFuncBuilder) newBlock(comment string) (s *SSABasicBlock) {
  19  	b := NewSSABasicBlock(fb.fn, comment)
  20  	b.ScopeID = fb.scopeID
  21  	return b
  22  }
  23  
  24  func (fb *ssaFuncBuilder) enterScope() int32 {
  25  	fb.nextScopeID++
  26  	old := fb.scopeID
  27  	fb.scopeID = fb.nextScopeID
  28  	return old
  29  }
  30  
  31  func (fb *ssaFuncBuilder) exitScope(old int32) {
  32  	fb.scopeID = old
  33  }
  34  
  35  func (fb *ssaFuncBuilder) labelBlock(name string) (s *SSABasicBlock) {
  36  	if fb.labels == nil {
  37  		fb.labels = map[string]*SSABasicBlock{}
  38  	}
  39  	if b := fb.labels[name]; b != nil {
  40  		return b
  41  	}
  42  	b := fb.newBlock("label." | name)
  43  	fb.labels[name] = b
  44  	return b
  45  }
  46  
  47  func (fb *ssaFuncBuilder) emit(instr SSAInstruction) {
  48  	if fb.currentBlock == nil {
  49  		return
  50  	}
  51  	instr.setBlock(fb.currentBlock)
  52  	fb.currentBlock.Instrs = append(fb.currentBlock.Instrs, instr)
  53  }
  54  
  55  func (fb *ssaFuncBuilder) nextName() (s string) {
  56  	fb.counter++
  57  	return "t" | ssaItoa(fb.counter)
  58  }
  59  
  60  func (fb *ssaFuncBuilder) buildBody(d *syntax.FuncDecl) {
  61  	entry := fb.newBlock("entry")
  62  	fb.currentBlock = entry
  63  
  64  	sig := fb.fn.Signature
  65  	if sig == nil {
  66  		return
  67  	}
  68  	params := sig.Params()
  69  	if d.Recv != nil {
  70  		recv := sig.Recv()
  71  		if recv != nil {
  72  			recvName := recv.Name()
  73  			if recvName == "" && d.Recv.Name != nil {
  74  				recvName = d.Recv.Name.Value
  75  			}
  76  			p := &SSAParameter{
  77  				name:   recvName,
  78  				typ:    recv.Type(),
  79  				pos:    0,
  80  				parent: fb.fn,
  81  			}
  82  			fb.fn.Params = append(fb.fn.Params, p)
  83  			if recvName != "" && recvName != "_" {
  84  				recvObj := types.NewTCVar(fb.fn.Pkg.Pkg, recvName, recv.Type())
  85  				p.object = recvObj
  86  				alloc := fb.emitAlloc(recv.Type(), 0)
  87  				fb.vars[recvObj] = alloc
  88  				fb.paramAllocas = append(fb.paramAllocas, alloc)
  89  				fb.emitStore(alloc, p)
  90  			}
  91  		}
  92  	}
  93  	if params != nil {
  94  		for i := 0; i < params.Len(); i++ {
  95  			pvar := params.At(i)
  96  			p := &SSAParameter{
  97  				name:   pvar.Name(),
  98  				typ:    pvar.Type(),
  99  				pos:    fb.fn.pos,
 100  				parent: fb.fn,
 101  			}
 102  			fb.fn.Params = append(fb.fn.Params, p)
 103  			if pvar.Name() != "" && pvar.Name() != "_" {
 104  				obj := types.NewTCVar(fb.fn.Pkg.Pkg, pvar.Name(), pvar.Type())
 105  				alloc := fb.emitAlloc(pvar.Type(), p.pos)
 106  				fb.vars[obj] = alloc
 107  				fb.paramAllocas = append(fb.paramAllocas, alloc)
 108  				fb.emitStore(alloc, p)
 109  			}
 110  		}
 111  	}
 112  
 113  	if sig.Results() != nil {
 114  		for i := 0; i < sig.Results().Len(); i++ {
 115  			r := sig.Results().At(i)
 116  			if r.Name() != "" && r.Name() != "_" {
 117  				alloc := fb.emitAlloc(r.Type(), fb.fn.pos)
 118  				fb.namedResults = append(fb.namedResults, alloc)
 119  				fb.fn.Locals = append(fb.fn.Locals, alloc)
 120  				fb.vars[r] = alloc
 121  			}
 122  		}
 123  	}
 124  
 125  	if len(fb.paramAllocas) > 0 {
 126  		fb.tailBlock = fb.newBlock("tailcall")
 127  		fb.emit(&SSAJump{Comment: "enter.body"})
 128  		fb.currentBlock.Succs = append(fb.currentBlock.Succs, fb.tailBlock)
 129  		fb.tailBlock.Preds = append(fb.tailBlock.Preds, fb.currentBlock)
 130  		fb.currentBlock = fb.tailBlock
 131  	}
 132  
 133  	if d.Body != nil {
 134  		fb.buildBlock(d.Body)
 135  	}
 136  
 137  	if fb.currentBlock != nil && !fb.blockTerminated(fb.currentBlock) {
 138  		if len(fb.namedResults) > 0 {
 139  			var vals []SSAValue
 140  			for _, nr := range fb.namedResults {
 141  				elemType := nr.SSAType()
 142  				if p, ok := elemType.(*types.Pointer); ok {
 143  					elemType = p.Elem()
 144  				}
 145  				vals = append(vals, fb.emitLoad(nr, elemType))
 146  			}
 147  			fb.emitReturn(vals, 0)
 148  		} else {
 149  			fb.emitReturn(nil, 0)
 150  		}
 151  	}
 152  	fb.fn.NamedResults = fb.namedResults
 153  }
 154  
 155  func (fb *ssaFuncBuilder) emitAlloc(typ types.Type, pos int32) (s *SSAAlloc) {
 156  	a := &SSAAlloc{Heap: false}
 157  	a.typ = types.NewPointer(typ)
 158  	a.pos = pos
 159  	a.name = fb.nextName()
 160  	fb.emit(a)
 161  	fb.fn.Locals = append(fb.fn.Locals, a)
 162  	return a
 163  }
 164  
 165  func (fb *ssaFuncBuilder) emitStore(addr SSAValue, val SSAValue) {
 166  	fb.emit(&SSAStore{Addr: addr, Val: val})
 167  }
 168  
 169  func (fb *ssaFuncBuilder) emitLoad(addr SSAValue, typ types.Type) (s SSAValue) {
 170  	u := &SSAUnOp{Op: OpMul, X: addr}
 171  	u.typ = typ
 172  	u.name = fb.nextName()
 173  	fb.emit(u)
 174  	return u
 175  }
 176  
 177  func (fb *ssaFuncBuilder) emitReturn(vals []SSAValue, pos int32) {
 178  	fb.emit(&SSAReturn{Results: vals, pos: pos})
 179  	fb.currentBlock = nil
 180  }
 181  
 182  func (fb *ssaFuncBuilder) blockTerminated(b *SSABasicBlock) (ok bool) {
 183  	if len(b.Instrs) == 0 {
 184  		return false
 185  	}
 186  	switch b.Instrs[len(b.Instrs)-1].(type) {
 187  	case *SSAReturn, *SSAJump, *SSAIf, *SSAPanic:
 188  		return true
 189  	}
 190  	return false
 191  }
 192  
 193  // Statement builders.
 194  
 195  func (fb *ssaFuncBuilder) buildBlock(b *syntax.BlockStmt) {
 196  	if b == nil {
 197  		return
 198  	}
 199  	for _, s := range b.List {
 200  		fb.buildStmt(s)
 201  	}
 202  }
 203  
 204  func (fb *ssaFuncBuilder) buildStmt(s syntax.Stmt) {
 205  	if s == nil {
 206  		return
 207  	}
 208  	if ls, ok := s.(*syntax.LabeledStmt); ok {
 209  		if ls.Label != nil {
 210  			fb.pendingLabel = ls.Label.Value
 211  			lblBlock := fb.labelBlock(ls.Label.Value)
 212  			if fb.currentBlock != nil {
 213  				fb.emit(&SSAJump{Comment: "goto.label." | ls.Label.Value})
 214  				fb.currentBlock.Succs = append(fb.currentBlock.Succs, lblBlock)
 215  				lblBlock.Preds = append(lblBlock.Preds, fb.currentBlock)
 216  			}
 217  			fb.currentBlock = lblBlock
 218  		}
 219  		fb.buildStmt(ls.Stmt)
 220  		fb.pendingLabel = ""
 221  		return
 222  	}
 223  	if fb.currentBlock == nil {
 224  		return
 225  	}
 226  	switch s := s.(type) {
 227  	case *syntax.EmptyStmt:
 228  		// nothing
 229  	case *syntax.ExprStmt:
 230  		fb.buildExpr(s.X)
 231  	case *syntax.AssignStmt:
 232  		fb.buildAssign(s)
 233  	case *syntax.BlockStmt:
 234  		fb.buildBlock(s)
 235  	case *syntax.DeclStmt:
 236  		for _, d := range s.DeclList {
 237  			fb.buildLocalDecl(d)
 238  		}
 239  	case *syntax.IfStmt:
 240  		fb.buildIf(s)
 241  	case *syntax.ForStmt:
 242  		fb.buildFor(s)
 243  	case *syntax.SwitchStmt:
 244  		fb.buildSwitch(s)
 245  	case *syntax.SelectStmt:
 246  		fb.buildSelect(s)
 247  	case *syntax.ReturnStmt:
 248  		fb.buildReturn(s)
 249  	case *syntax.BranchStmt:
 250  		fb.buildBranch(s)
 251  	case *syntax.SendStmt:
 252  		ch := fb.buildExpr(s.Chan)
 253  		val := fb.buildExpr(s.Value)
 254  		if ch != nil && val != nil {
 255  			fb.emit(&SSASend{Chan: ch, X: val})
 256  		}
 257  	case *syntax.CallStmt:
 258  		call, _ := s.Call.(*syntax.CallExpr)
 259  		switch s.Tok {
 260  		case token.Go:
 261  			if call != nil {
 262  				fb.buildGoStmt(call)
 263  			}
 264  		case token.Defer:
 265  			if call != nil {
 266  				fb.buildDeferStmt(call)
 267  			}
 268  		default:
 269  			fb.buildExpr(s.Call)
 270  		}
 271  	}
 272  }
 273  
 274  func (fb *ssaFuncBuilder) buildAssign(s *syntax.AssignStmt) {
 275  	if s.Rhs == nil {
 276  		lv := fb.buildExpr(s.Lhs)
 277  		if lv == nil {
 278  			return
 279  		}
 280  		one := &SSAConst{typ: lv.SSAType(), val: &types.ConstInt{V:1}}
 281  		op := OpAdd
 282  		if s.Op == token.Sub {
 283  			op = OpSub
 284  		}
 285  		bin := &SSABinOp{Op: op, X: lv, Y: one}
 286  		bin.typ = lv.SSAType()
 287  		bin.name = fb.nextName()
 288  		fb.emit(bin)
 289  		fb.buildStore(s.Lhs, bin)
 290  		return
 291  	}
 292  	if s.Op == token.Def {
 293  		fb.buildShortVarDecl(s)
 294  		return
 295  	}
 296  	if s.Op != 0 {
 297  		lv := fb.buildExpr(s.Lhs)
 298  		rv := fb.buildExpr(s.Rhs)
 299  		if lv == nil || rv == nil {
 300  			return
 301  		}
 302  		op := compoundOp(s.Op)
 303  		bin := &SSABinOp{Op: op, X: lv, Y: rv}
 304  		bin.typ = lv.SSAType()
 305  		bin.name = fb.nextName()
 306  		fb.emit(bin)
 307  		fb.buildStore(s.Lhs, bin)
 308  		return
 309  	}
 310  	lhsList, lhsIsList := s.Lhs.(*syntax.ListExpr)
 311  	rhsList, rhsIsList := s.Rhs.(*syntax.ListExpr)
 312  	if lhsIsList && rhsIsList && len(lhsList.ElemList) == len(rhsList.ElemList) {
 313  		vals := []SSAValue{:len(rhsList.ElemList)}
 314  		for i, re := range rhsList.ElemList {
 315  			vals[i] = fb.buildExpr(re)
 316  		}
 317  		for i, le := range lhsList.ElemList {
 318  			if vals[i] != nil {
 319  				fb.buildStore(le, vals[i])
 320  			}
 321  		}
 322  		return
 323  	}
 324  	if lhsIsList && len(lhsList.ElemList) == 2 {
 325  		if ae, ok := s.Rhs.(*syntax.AssertExpr); ok {
 326  			x := fb.buildExpr(ae.X)
 327  			assertedType := fb.resolveType(ae.Type)
 328  			if x != nil && assertedType != nil {
 329  				ta := &SSATypeAssert{X: x, AssertedType: assertedType, CommaOk: true}
 330  				ta.typ = types.NewTuple(
 331  					types.NewTCVar(nil, "val", assertedType),
 332  					types.NewTCVar(nil, "ok", types.Typ[types.Bool]),
 333  				)
 334  				ta.name = fb.nextName()
 335  				fb.emit(ta)
 336  				for i, le := range lhsList.ElemList {
 337  					if n, ok := le.(*syntax.Name); ok && n.Value == "_" {
 338  						continue
 339  					}
 340  					ext := &SSAExtract{Tuple: ta, Index: i}
 341  					ext.typ = ssaTupleElemType(ta.typ, i)
 342  					ext.name = fb.nextName()
 343  					fb.emit(ext)
 344  					fb.buildStore(le, ext)
 345  				}
 346  				return
 347  			}
 348  		}
 349  		if ie, ok := s.Rhs.(*syntax.IndexExpr); ok {
 350  			x := fb.buildExpr(ie.X)
 351  			idx := fb.buildExpr(ie.Index)
 352  			if x != nil && idx != nil {
 353  				if mt, ok2 := types.SafeUnderlying(x.SSAType()).(*types.TCMap); ok2 {
 354  					idx = fb.coerceToInterface(idx, mt.Key())
 355  					l := &SSALookup{X: x, Index: idx, CommaOk: true}
 356  					l.typ = types.NewTuple(
 357  						types.NewTCVar(nil, "v", mt.Elem()),
 358  						types.NewTCVar(nil, "ok", types.Typ[types.Bool]),
 359  					)
 360  					l.name = fb.nextName()
 361  					fb.emit(l)
 362  					for i, le := range lhsList.ElemList {
 363  						if n, ok := le.(*syntax.Name); ok && n.Value == "_" {
 364  							continue
 365  						}
 366  						ext := &SSAExtract{Tuple: l, Index: i}
 367  						ext.typ = ssaTupleElemType(l.typ, i)
 368  						ext.name = fb.nextName()
 369  						fb.emit(ext)
 370  						fb.buildStore(le, ext)
 371  					}
 372  					return
 373  				}
 374  			}
 375  		}
 376  	}
 377  	rhs := fb.buildExpr(s.Rhs)
 378  	if rhs == nil {
 379  		return
 380  	}
 381  	fb.buildStore(s.Lhs, rhs)
 382  }
 383  
 384  func (fb *ssaFuncBuilder) coerceToInterface(val SSAValue, targetType types.Type) (s SSAValue) {
 385  	if targetType == nil || val == nil || val.SSAType() == nil {
 386  		return val
 387  	}
 388  	if _, isIface := types.SafeUnderlying(targetType).(*types.TCInterface); isIface {
 389  		if _, alreadyIface := types.SafeUnderlying(val.SSAType()).(*types.TCInterface); !alreadyIface {
 390  			mi := &SSAMakeInterface{X: val}
 391  			mi.typ = targetType
 392  			mi.name = fb.nextName()
 393  			fb.emit(mi)
 394  			return mi
 395  		}
 396  	}
 397  	return val
 398  }
 399  
 400  func (fb *ssaFuncBuilder) buildStore(lhs syntax.Expr, val SSAValue) {
 401  	if lhs == nil {
 402  		panic("buildStore: nil lhs")
 403  	}
 404  	switch lhs := lhs.(type) {
 405  	case *syntax.Name:
 406  		if lhs == nil {
 407  			panic("buildStore: nil *syntax.Name after type switch")
 408  		}
 409  		if lhs.Value == "_" {
 410  			return
 411  		}
 412  		obj := fb.lookupObject(lhs.Value)
 413  		if obj == nil {
 414  			return
 415  		}
 416  		if alloc, ok := fb.vars[obj]; ok {
 417  			val = fb.coerceToInterface(val, obj.Type())
 418  			fb.emitStore(alloc, val)
 419  		} else if fv, ok := fb.freeVarPtrs[lhs.Value]; ok {
 420  			val = fb.coerceToInterface(val, obj.Type())
 421  			fb.emitStore(fv, val)
 422  		} else if g, ok := fb.fn.Pkg.Members[lhs.Value].(*SSAGlobal); ok {
 423  			if p, ok2 := g.SSAType().(*types.Pointer); ok2 && p.Elem() != nil {
 424  				val = fb.coerceToInterface(val, p.Elem())
 425  			}
 426  			fb.emitStore(g, val)
 427  		}
 428  	case *syntax.Operation:
 429  		if lhs.Y == nil && lhs.Op == token.Mul {
 430  			ptr := fb.buildExpr(lhs.X)
 431  			if ptr != nil {
 432  				if p, ok2 := ptr.SSAType().(*types.Pointer); ok2 && p.Elem() != nil {
 433  					val = fb.coerceToInterface(val, p.Elem())
 434  				}
 435  				fb.emitStore(ptr, val)
 436  			}
 437  		}
 438  	case *syntax.SelectorExpr:
 439  		addr := fb.buildSelectorAddr(lhs)
 440  		if addr == nil {
 441  			return
 442  		}
 443  		if p, ok2 := addr.SSAType().(*types.Pointer); ok2 && p.Elem() != nil {
 444  			val = fb.coerceToInterface(val, p.Elem())
 445  		}
 446  		fb.emitStore(addr, val)
 447  	case *syntax.IndexExpr:
 448  		base := fb.buildExpr(lhs.X)
 449  		idx := fb.buildExpr(lhs.Index)
 450  		if base == nil || idx == nil {
 451  			return
 452  		}
 453  		if m, isMap := types.SafeUnderlying(base.SSAType()).(*types.TCMap); isMap {
 454  			idx = fb.coerceToInterface(idx, m.Key())
 455  			val = fb.coerceToInterface(val, m.Elem())
 456  			fb.emit(&SSAMapUpdate{Map: base, Key: idx, Value: val})
 457  			return
 458  		}
 459  		// For array LHS assignment, resolve the address of the base directly
 460  		// so SSAIndexAddr GEPs into the original, not a stack copy.
 461  		var baseAddr SSAValue
 462  		if n, ok := lhs.X.(*syntax.Name); ok {
 463  			obj := fb.lookupObject(n.Value)
 464  			if obj != nil {
 465  				if alloc, ok2 := fb.vars[obj]; ok2 {
 466  					baseAddr = alloc
 467  				} else if g, ok2 := fb.fn.Pkg.Members[n.Value].(*SSAGlobal); ok2 {
 468  					baseAddr = g
 469  				}
 470  			}
 471  		} else if sel, ok := lhs.X.(*syntax.SelectorExpr); ok {
 472  			baseAddr = fb.buildSelectorAddr(sel)
 473  		}
 474  		if baseAddr != nil {
 475  			if _, isArr := types.SafeUnderlying(base.SSAType()).(*types.Array); isArr {
 476  				ia := &SSAIndexAddr{X: baseAddr, Index: idx}
 477  				ia.typ = types.NewPointer(ssaElemType(base.SSAType()))
 478  				ia.name = fb.nextName()
 479  				fb.emit(ia)
 480  				if p, ok2 := ia.SSAType().(*types.Pointer); ok2 && p.Elem() != nil {
 481  					val = fb.coerceToInterface(val, p.Elem())
 482  				}
 483  				fb.emitStore(ia, val)
 484  				return
 485  			}
 486  		}
 487  		ia := &SSAIndexAddr{X: base, Index: idx}
 488  		ia.typ = types.NewPointer(ssaElemType(base.SSAType()))
 489  		ia.name = fb.nextName()
 490  		fb.emit(ia)
 491  		if p, ok2 := ia.SSAType().(*types.Pointer); ok2 && p.Elem() != nil {
 492  			val = fb.coerceToInterface(val, p.Elem())
 493  		}
 494  		fb.emitStore(ia, val)
 495  	case *syntax.ListExpr:
 496  		for i, e := range lhs.ElemList {
 497  			ext := &SSAExtract{Tuple: val, Index: i}
 498  			ext.typ = ssaTupleElemType(val.SSAType(), i)
 499  			ext.name = fb.nextName()
 500  			fb.emit(ext)
 501  			fb.buildStore(e, ext)
 502  		}
 503  	}
 504  }
 505  
 506  func (fb *ssaFuncBuilder) buildShortVarDecl(s *syntax.AssignStmt) {
 507  	names := ssaExprNames(s.Lhs)
 508  	if len(names) == 2 {
 509  		if ae, ok := s.Rhs.(*syntax.AssertExpr); ok {
 510  			x := fb.buildExpr(ae.X)
 511  			assertedType := fb.resolveType(ae.Type)
 512  			if assertedType != nil {
 513  				if x == nil {
 514  					x = &SSAConst{typ: types.NewTCInterface(nil, nil)}
 515  				}
 516  				ta := &SSATypeAssert{X: x, AssertedType: assertedType, CommaOk: true}
 517  				ta.typ = types.NewTuple(
 518  					types.NewTCVar(nil, "val", assertedType),
 519  					types.NewTCVar(nil, "ok", types.Typ[types.Bool]),
 520  				)
 521  				ta.name = fb.nextName()
 522  				fb.emit(ta)
 523  				for i, name := range names {
 524  					if name.Value == "_" {
 525  						continue
 526  					}
 527  					ext := &SSAExtract{Tuple: ta, Index: i}
 528  					ext.typ = ssaTupleElemType(ta.typ, i)
 529  					ext.name = fb.nextName()
 530  					fb.emit(ext)
 531  					fb.removeVar(name.Value)
 532  					obj := types.NewTCVar(fb.fn.Pkg.Pkg, name.Value, ext.typ)
 533  					alloc := fb.emitAlloc(ext.typ, 0)
 534  					fb.vars[obj] = alloc
 535  					fb.emitStore(alloc, ext)
 536  				}
 537  				return
 538  			}
 539  		}
 540  		if ie, ok := s.Rhs.(*syntax.IndexExpr); ok {
 541  			x := fb.buildExpr(ie.X)
 542  			idx := fb.buildExpr(ie.Index)
 543  			if x != nil && idx != nil {
 544  				if mt, ok2 := types.SafeUnderlying(x.SSAType()).(*types.TCMap); ok2 {
 545  					idx = fb.coerceToInterface(idx, mt.Key())
 546  					l := &SSALookup{X: x, Index: idx, CommaOk: true}
 547  					l.typ = types.NewTuple(
 548  						types.NewTCVar(nil, "v", mt.Elem()),
 549  						types.NewTCVar(nil, "ok", types.Typ[types.Bool]),
 550  					)
 551  					l.name = fb.nextName()
 552  					fb.emit(l)
 553  					for i, name := range names {
 554  						if name.Value == "_" {
 555  							continue
 556  						}
 557  						ext := &SSAExtract{Tuple: l, Index: i}
 558  						ext.typ = ssaTupleElemType(l.typ, i)
 559  						ext.name = fb.nextName()
 560  						fb.emit(ext)
 561  						fb.removeVar(name.Value)
 562  						var obj types.Object
 563  						if fb.info != nil {
 564  							obj = fb.info.Defs[name]
 565  						}
 566  						if obj == nil {
 567  							obj = types.NewTCVar(fb.fn.Pkg.Pkg, name.Value, ext.typ)
 568  						}
 569  						alloc := fb.emitAlloc(ext.typ, 0)
 570  						fb.vars[obj] = alloc
 571  						fb.emitStore(alloc, ext)
 572  					}
 573  					return
 574  				}
 575  			}
 576  		}
 577  	}
 578  	if rhsList, ok := s.Rhs.(*syntax.ListExpr); ok && len(rhsList.ElemList) == len(names) {
 579  		vals := []SSAValue{:len(rhsList.ElemList)}
 580  		for i, re := range rhsList.ElemList {
 581  			vals[i] = fb.buildExpr(re)
 582  		}
 583  		for i, name := range names {
 584  			if name.Value == "_" {
 585  				continue
 586  			}
 587  			var typ types.Type
 588  			if vals[i] != nil {
 589  				typ = vals[i].SSAType()
 590  			}
 591  			fb.removeVar(name.Value)
 592  			var obj types.Object
 593  			if fb.info != nil {
 594  				obj = fb.info.Defs[name]
 595  			}
 596  			if obj == nil {
 597  				obj = types.NewTCVar(fb.fn.Pkg.Pkg, name.Value, typ)
 598  			}
 599  			alloc := fb.emitAlloc(typ, 0)
 600  			fb.vars[obj] = alloc
 601  			if vals[i] != nil {
 602  				fb.emitStore(alloc, vals[i])
 603  			}
 604  		}
 605  		return
 606  	}
 607  
 608  	rhs := fb.buildExpr(s.Rhs)
 609  
 610  	isTuple := false
 611  	var tup *types.Tuple
 612  	if rhs != nil {
 613  		tup, isTuple = rhs.SSAType().(*types.Tuple)
 614  	}
 615  	for i, name := range names {
 616  		if name.Value == "_" {
 617  			continue
 618  		}
 619  		var typ types.Type
 620  		if rhs != nil {
 621  			if isTuple && i < tup.Len() {
 622  				typ = tup.At(i).Type()
 623  			} else if i == 0 {
 624  				typ = rhs.SSAType()
 625  			}
 626  		}
 627  		if typ == nil {
 628  			rhsInfo := "nil"
 629  			if rhs != nil {
 630  				if rhs.SSAType() == nil {
 631  					rhsInfo = "ssatype=nil"
 632  				} else if tup2, ok2 := rhs.SSAType().(*types.Tuple); ok2 {
 633  					rhsInfo = "tuple[" | simpleItoa(tup2.Len()) | "] i=" | simpleItoa(i)
 634  					if i < tup2.Len() {
 635  						if tup2.At(i).Type() == nil {
 636  							rhsInfo = rhsInfo | " At.Type=nil"
 637  						} else {
 638  							rhsInfo = rhsInfo | " At.Type=set"
 639  						}
 640  					}
 641  				} else {
 642  					rhsInfo = "non-tuple"
 643  				}
 644  			}
 645  			typ = types.Typ[types.Int32]
 646  		}
 647  		fb.removeVar(name.Value)
 648  		var obj types.Object
 649  		if fb.info != nil {
 650  			obj = fb.info.Defs[name]
 651  		}
 652  		if obj == nil {
 653  			obj = types.NewTCVar(fb.fn.Pkg.Pkg, name.Value, typ)
 654  		}
 655  		alloc := fb.emitAlloc(typ, 0)
 656  		fb.vars[obj] = alloc
 657  		var initVal SSAValue
 658  		if rhs != nil {
 659  			if isTuple {
 660  				ext := &SSAExtract{Tuple: rhs, Index: i}
 661  				ext.typ = typ
 662  				ext.name = fb.nextName()
 663  				fb.emit(ext)
 664  				initVal = ext
 665  			} else if i == 0 {
 666  				initVal = rhs
 667  			}
 668  		}
 669  		if initVal != nil {
 670  			fb.emitStore(alloc, initVal)
 671  		}
 672  	}
 673  }
 674  
 675  func (fb *ssaFuncBuilder) buildLocalDecl(d syntax.Decl) {
 676  	switch d := d.(type) {
 677  	case *syntax.VarDecl:
 678  		var typ types.Type
 679  		if fb.info != nil && len(d.NameList) > 0 {
 680  			if obj := fb.info.Defs[d.NameList[0]]; obj != nil {
 681  				typ = obj.Type()
 682  			}
 683  		}
 684  		if typ == nil && d.Type != nil {
 685  			typ = fb.resolveType(d.Type)
 686  		}
 687  		initVal := fb.buildExpr(d.Values)
 688  		for _, name := range d.NameList {
 689  			if name.Value == "_" {
 690  				continue
 691  			}
 692  			fb.removeVar(name.Value)
 693  			var obj types.Object
 694  			if fb.info != nil {
 695  				obj = fb.info.Defs[name]
 696  			}
 697  			if obj == nil {
 698  				obj = types.NewTCVar(fb.fn.Pkg.Pkg, name.Value, typ)
 699  			}
 700  			alloc := fb.emitAlloc(obj.Type(), 0)
 701  			fb.vars[obj] = alloc
 702  			fb.fn.Locals = append(fb.fn.Locals, alloc)
 703  			if initVal != nil {
 704  				initVal = fb.coerceToInterface(initVal, obj.Type())
 705  				fb.emitStore(alloc, initVal)
 706  			} else {
 707  				fb.emitStore(alloc, &SSAConst{typ: obj.Type(), val: nil})
 708  			}
 709  		}
 710  	case *syntax.ConstDecl:
 711  		if d.Values != nil && len(d.NameList) > 0 {
 712  			if fb.localConsts == nil {
 713  				fb.localConsts = map[string]*SSAConst{}
 714  			}
 715  			if list, ok := d.Values.(*syntax.ListExpr); ok {
 716  				for i, name := range d.NameList {
 717  					if i < len(list.ElemList) {
 718  						if v := fb.buildExpr(list.ElemList[i]); v != nil {
 719  							if c := ssaExtractConst(v); c != nil {
 720  								fb.localConsts[name.Value] = c
 721  							}
 722  						}
 723  					}
 724  				}
 725  			} else if len(d.NameList) == 1 {
 726  				if v := fb.buildExpr(d.Values); v != nil {
 727  					if c := ssaExtractConst(v); c != nil {
 728  						fb.localConsts[d.NameList[0].Value] = c
 729  					}
 730  				}
 731  			}
 732  		}
 733  	case *types.TypeDecl:
 734  		if d.Name != nil {
 735  			resolved := fb.resolveTypeAST(d.Type)
 736  			if resolved != nil {
 737  				fb.localTypes[d.Name.Value] = resolved
 738  			}
 739  		}
 740  	}
 741  }
 742  
 743  func (fb *ssaFuncBuilder) evalLocalConstExpr(e syntax.Expr) (c types.ConstVal) {
 744  	if e == nil {
 745  		return nil
 746  	}
 747  	switch e := e.(type) {
 748  	case *syntax.BasicLit:
 749  		return evalBasicLitLocal(e)
 750  	case *syntax.Name:
 751  		if c, ok := fb.localConsts[e.Value]; ok && c != nil && c.val != nil {
 752  			return c.val
 753  		}
 754  		return tcEvalConstExpr(e, fb.fn.Pkg.Pkg.Scope(), -1)
 755  	case *syntax.Operation:
 756  		if e.Y == nil {
 757  			xr := fb.evalLocalConstExpr(e.X)
 758  			if xr == nil {
 759  				return nil
 760  			}
 761  			return evalUnaryLocal(e.Op, xr)
 762  		}
 763  		xr := fb.evalLocalConstExpr(e.X)
 764  		yr := fb.evalLocalConstExpr(e.Y)
 765  		if xr == nil || yr == nil {
 766  			return nil
 767  		}
 768  		return evalBinaryLocal(e.Op, xr, yr)
 769  	case *syntax.ParenExpr:
 770  		return fb.evalLocalConstExpr(e.X)
 771  	}
 772  	return nil
 773  }
 774