From: Matthew Dempsky Date: Thu, 28 Sep 2017 06:52:04 +0000 (-0700) Subject: cmd/compile: allow := to shadow dot-imported names X-Git-Tag: go1.10beta1~863 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=f22ef70254a6a79834ed3fa21c4d1ccadeb758bd;p=gostls13.git cmd/compile: allow := to shadow dot-imported names Historically, gc optimistically parsed the left-hand side of assignments as expressions. Later, if it discovered a ":=" assignment, it rewrote the parsed expressions as declarations. This failed in the presence of dot imports though, because we lost information about whether an imported object was named via a bare identifier "Foo" or a normal qualified "pkg.Foo". This CL fixes the issue by specially noding the left-hand side of ":=" assignments. Fixes #22076. Change-Id: I18190ecdb863112e7d009e1687e6112eec559921 Reviewed-on: https://go-review.googlesource.com/66810 Run-TryBot: Matthew Dempsky Reviewed-by: Daniel Martí Reviewed-by: Robert Griesemer TryBot-Result: Gobot Gobot --- diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index 4db50117c4..0e14de9066 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -758,15 +758,10 @@ func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) *Node { return n } - lhs := p.exprList(stmt.Lhs) - rhs := p.exprList(stmt.Rhs) - n := p.nod(stmt, OAS, nil, nil) // assume common case - if stmt.Op == syntax.Def { - n.SetColas(true) - colasdefn(lhs, n) // modifies lhs, call before using lhs[0] in common case - } + rhs := p.exprList(stmt.Rhs) + lhs := p.assignList(stmt.Lhs, n, stmt.Op == syntax.Def) if len(lhs) == 1 && len(rhs) == 1 { // common case @@ -845,6 +840,66 @@ func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) *Node { panic("unhandled Stmt") } +func (p *noder) assignList(expr syntax.Expr, defn *Node, colas bool) []*Node { + if !colas { + return p.exprList(expr) + } + + defn.SetColas(true) + + var exprs []syntax.Expr + if list, ok := expr.(*syntax.ListExpr); ok { + exprs = list.ElemList + } else { + exprs = []syntax.Expr{expr} + } + + res := make([]*Node, len(exprs)) + seen := make(map[*types.Sym]bool, len(exprs)) + + newOrErr := false + for i, expr := range exprs { + p.lineno(expr) + res[i] = nblank + + name, ok := expr.(*syntax.Name) + if !ok { + yyerrorpos(expr.Pos(), "non-name %v on left side of :=", p.expr(expr)) + newOrErr = true + continue + } + + sym := p.name(name) + if sym.IsBlank() { + continue + } + + if seen[sym] { + yyerrorpos(expr.Pos(), "%v repeated on left side of :=", sym) + newOrErr = true + continue + } + seen[sym] = true + + if sym.Block == types.Block { + res[i] = oldname(sym) + continue + } + + newOrErr = true + n := newname(sym) + declare(n, dclcontext) + n.Name.Defn = defn + defn.Ninit.Append(nod(ODCL, n, nil)) + res[i] = n + } + + if !newOrErr { + yyerrorl(defn.Pos, "no new variables on left side of :=") + } + return res +} + func (p *noder) blockStmt(stmt *syntax.BlockStmt) []*Node { p.openScope(stmt.Pos()) nodes := p.stmts(stmt.List) @@ -884,12 +939,7 @@ func (p *noder) forStmt(stmt *syntax.ForStmt) *Node { n = p.nod(r, ORANGE, nil, p.expr(r.X)) if r.Lhs != nil { - lhs := p.exprList(r.Lhs) - n.List.Set(lhs) - if r.Def { - n.SetColas(true) - colasdefn(lhs, n) - } + n.List.Set(p.assignList(r.Lhs, n, r.Def)) } } else { n = p.nod(stmt, OFOR, nil, nil) diff --git a/test/fixedbugs/bug388.go b/test/fixedbugs/bug388.go index af0c9d9c57..2d508501e0 100644 --- a/test/fixedbugs/bug388.go +++ b/test/fixedbugs/bug388.go @@ -14,7 +14,7 @@ func foo(runtime.UintType, i int) { // ERROR "cannot declare name runtime.UintT } func bar(i int) { - runtime.UintType := i // ERROR "cannot declare name runtime.UintType|non-name on left side|undefined identifier" + runtime.UintType := i // ERROR "non-name runtime.UintType|non-name on left side|undefined identifier" println(runtime.UintType) // GCCGO_ERROR "invalid use of type|undefined identifier" } diff --git a/test/fixedbugs/issue20250.go b/test/fixedbugs/issue20250.go index f24710a0c3..28c85ff130 100644 --- a/test/fixedbugs/issue20250.go +++ b/test/fixedbugs/issue20250.go @@ -16,8 +16,8 @@ type T struct { func f(a T) { // ERROR "live at entry to f: a" var e interface{} - func() { // ERROR "live at entry to f.func1: &e a" - e = a.s // ERROR "live at call to convT2Estring: &e a" "live at call to writebarrierptr: a" + func() { // ERROR "live at entry to f.func1: a &e" + e = a.s // ERROR "live at call to convT2Estring: a &e" "live at call to writebarrierptr: a" }() // ERROR "live at call to f.func1: e$" // Before the fix, both a and e were live at the previous line. _ = e diff --git a/test/fixedbugs/issue22076.go b/test/fixedbugs/issue22076.go new file mode 100644 index 0000000000..5d628b96bd --- /dev/null +++ b/test/fixedbugs/issue22076.go @@ -0,0 +1,25 @@ +// compile + +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 22076: Couldn't use ":=" to declare names that refer to +// dot-imported symbols. + +package p + +import . "bytes" + +var _ Reader // use "bytes" import + +func _() { + Buffer := 0 + _ = Buffer +} + +func _() { + for Buffer := range []int{} { + _ = Buffer + } +}