Commit f22ef702 authored by Matthew Dempsky's avatar Matthew Dempsky

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 <mdempsky@google.com>
Reviewed-by: 's avatarDaniel Martí <mvdan@mvdan.cc>
Reviewed-by: 's avatarRobert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 3b8a0315
......@@ -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)
......
......@@ -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"
}
......
......@@ -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
......
// 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
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment