diff --git a/ssa/builder.go b/ssa/builder.go index c80024793c..0f5480d37f 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -1119,7 +1119,7 @@ func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { for i, id := range spec.Names { var lval lvalue = blank{} if !isBlankIdent(id) { - lval = address{addr: fn.addNamedLocal(fn.Pkg.objectOf(id))} + lval = address{addr: fn.addLocalForIdent(id)} } b.exprInPlace(fn, lval, spec.Values[i]) } @@ -1129,7 +1129,7 @@ func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { // Locals are implicitly zero-initialized. for _, id := range spec.Names { if !isBlankIdent(id) { - fn.addNamedLocal(fn.Pkg.objectOf(id)) + fn.addLocalForIdent(id) } } @@ -1139,7 +1139,7 @@ func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { result := tuple.Type().(*types.Tuple) for i, id := range spec.Names { if !isBlankIdent(id) { - lhs := fn.addNamedLocal(fn.Pkg.objectOf(id)) + lhs := fn.addLocalForIdent(id) emitStore(fn, lhs, emitExtract(fn, tuple, i, result.At(i).Type())) } } @@ -1446,16 +1446,13 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl b.stmt(fn, s.Init) } - var x, y Value - var id *ast.Ident + var x Value switch ass := s.Assign.(type) { case *ast.ExprStmt: // x.(type) x = b.expr(fn, unparen(ass.X).(*ast.TypeAssertExpr).X) case *ast.AssignStmt: // y := x.(type) x = b.expr(fn, unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X) - id = ass.Lhs[0].(*ast.Ident) - y = fn.addNamedLocal(fn.Pkg.objectOf(id)) - emitStore(fn, y, x) + emitStore(fn, fn.addLocalForIdent(ass.Lhs[0].(*ast.Ident)), x) } done := fn.newBasicBlock("typeswitch.done") @@ -1488,10 +1485,10 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl fn.currentBlock = next } fn.currentBlock = body - if id != nil && len(cc.List) == 1 && casetype != tUntypedNil { + if obj := fn.Pkg.info.TypeCaseVar(cc); obj != nil { // Declare a new shadow local variable of the // same name but a more specific type. - y2 := fn.addNamedLocal(fn.Pkg.info.TypeCaseVar(cc)) + y2 := fn.addNamedLocal(obj) y2.name += "'" // debugging aid y2.typ = pointer(casetype) emitStore(fn, y2, ti) @@ -1502,9 +1499,6 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl } b.stmtList(fn, cc.Body) fn.targets = fn.targets.tail - if id != nil { - fn.objects[fn.Pkg.objectOf(id)] = y // restore previous y binding - } emitJump(fn, done) fn.currentBlock = next } @@ -1634,12 +1628,18 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) { r++ case *ast.AssignStmt: // x := <-states[state].Chan - xdecl := fn.addNamedLocal(fn.Pkg.objectOf(comm.Lhs[0].(*ast.Ident))) - emitStore(fn, xdecl, emitExtract(fn, sel, r, vars[r].Type())) + if comm.Tok == token.DEFINE { + fn.addLocalForIdent(comm.Lhs[0].(*ast.Ident)) + } + x := b.addr(fn, comm.Lhs[0], false) // non-escaping + x.store(fn, emitExtract(fn, sel, r, vars[r].Type())) if len(comm.Lhs) == 2 { // x, ok := ... - okdecl := fn.addNamedLocal(fn.Pkg.objectOf(comm.Lhs[1].(*ast.Ident))) - emitStore(fn, okdecl, emitExtract(fn, sel, 1, okdecl.Type().Deref())) + if comm.Tok == token.DEFINE { + fn.addLocalForIdent(comm.Lhs[1].(*ast.Ident)) + } + ok := b.addr(fn, comm.Lhs[1], false) // non-escaping + ok.store(fn, emitExtract(fn, sel, 1, ok.typ().Deref())) } r++ } @@ -1929,10 +1929,10 @@ func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) { // always creates a new one. if s.Tok == token.DEFINE { if tk != nil { - fn.addNamedLocal(fn.Pkg.objectOf(s.Key.(*ast.Ident))) + fn.addLocalForIdent(s.Key.(*ast.Ident)) } if tv != nil { - fn.addNamedLocal(fn.Pkg.objectOf(s.Value.(*ast.Ident))) + fn.addLocalForIdent(s.Value.(*ast.Ident)) } } diff --git a/ssa/func.go b/ssa/func.go index 1bfa5a9c23..031427fbdd 100644 --- a/ssa/func.go +++ b/ssa/func.go @@ -256,7 +256,7 @@ func (f *Function) createSyntacticParams() { for _, field := range f.syntax.resultFields.List { // Implicit "var" decl of locals for named results. for _, n := range field.Names { - f.namedResults = append(f.namedResults, f.addNamedLocal(f.Pkg.objectOf(n))) + f.namedResults = append(f.namedResults, f.addLocalForIdent(n)) } } } @@ -386,6 +386,10 @@ func (f *Function) addNamedLocal(obj types.Object) *Alloc { return l } +func (f *Function) addLocalForIdent(id *ast.Ident) *Alloc { + return f.addNamedLocal(f.Pkg.objectOf(id)) +} + // addLocal creates an anonymous local variable of type typ, adds it // to function f and returns it. pos is the optional source location. // diff --git a/ssa/interp/testdata/coverage.go b/ssa/interp/testdata/coverage.go index 1a9685fbda..0338383f78 100644 --- a/ssa/interp/testdata/coverage.go +++ b/ssa/interp/testdata/coverage.go @@ -180,6 +180,14 @@ func main() { if <-ch != 1 { panic("couldn't receive") } + // A "receive" select-case that doesn't declare its vars. (regression test) + anint := 0 + ok := false + select { + case anint, ok = <-ch: + case anint = <-ch: + default: + } // Anon structs with methods. anon := struct{ T }{T: T{z: 1}} diff --git a/ssa/ssa.go b/ssa/ssa.go index 5900be6b38..b0cef1a0b0 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -1235,12 +1235,15 @@ func (c *CallCommon) Pos() token.Pos { return c.pos } // In either "call" or "invoke" mode, if the callee is a method, its // receiver is represented by sig.Recv, not sig.Params().At(0). // +// Signature returns nil for a call to a built-in function. +// func (c *CallCommon) Signature() *types.Signature { if c.Recv != nil { iface := c.Recv.Type().Underlying().(*types.Interface) return iface.Method(c.Method).Type().(*types.Signature) } - return c.Func.Type().Underlying().(*types.Signature) + sig, _ := c.Func.Type().Underlying().(*types.Signature) // nil for *Builtin + return sig } // StaticCallee returns the called function if this is a trivially