Commit c3e0dc23 authored by Robert Griesemer's avatar Robert Griesemer

go/importer: handle multiple imports of the same object

Before aliases, and because we chose a simple export format for them,
a package may now export the same object more than once if there are
multiple exported aliases referring to that object. The go/importer
made the assumption this couldn't happen. Adjust it.

Fixes #17726.

Change-Id: Ibb9fc669a8748200b45ad78934d7453e5a5aad82
Reviewed-on: https://go-review.googlesource.com/32538Reviewed-by: default avatarMatthew Dempsky <mdempsky@google.com>
Run-TryBot: Robert Griesemer <gri@golang.org>
parent 012fec38
...@@ -207,18 +207,53 @@ func (p *importer) pkg() *types.Package { ...@@ -207,18 +207,53 @@ func (p *importer) pkg() *types.Package {
return pkg return pkg
} }
func (p *importer) declare(obj types.Object) { // objTag returns the tag value for each object kind.
// obj must not be a *types.Alias.
func objTag(obj types.Object) int {
switch obj.(type) {
case *types.Const:
return constTag
case *types.TypeName:
return typeTag
case *types.Var:
return varTag
case *types.Func:
return funcTag
// Aliases are not exported multiple times, thus we should not see them here.
default:
errorf("unexpected object: %v (%T)", obj, obj)
panic("unreachable")
}
}
func sameObj(a, b types.Object) bool {
// Because unnamed types are not canonicalized, we cannot simply compare types for
// (pointer) identity.
// Ideally we'd check equality of constant values as well, but this is good enough.
return objTag(a) == objTag(b) && types.Identical(a.Type(), b.Type())
}
func (p *importer) declare(obj types.Object) types.Object {
pkg := obj.Pkg() pkg := obj.Pkg()
if alt := pkg.Scope().Insert(obj); alt != nil { if alt := pkg.Scope().Insert(obj); alt != nil {
// This could only trigger if we import a (non-type) object a second time. // This can only trigger if we import a (non-type) object a second time.
// This should never happen because 1) we only import a package once; and // Excluding aliases, this cannot happen because 1) we only import a package
// b) we ignore compiler-specific export data which may contain functions // once; and b) we ignore compiler-specific export data which may contain
// whose inlined function bodies refer to other functions that were already // functions whose inlined function bodies refer to other functions that
// imported. // were already imported.
// (See also the comment in cmd/compile/internal/gc/bimport.go importer.obj, // However, if a package exports multiple aliases referring to the same
// switch case importing functions). // original object, that object is currently exported multiple times.
errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", alt, obj) // Check for that specific case and accept it if the aliases correspond
// (see also the comment in cmd/compile/internal/gc/bimport.go, method
// importer.obj, switch case importing functions).
// Note that the original itself cannot be an alias.
// TODO(gri) We can avoid doing this once objects are exported only once
// per package again (issue #17636).
if !sameObj(obj, alt) {
errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", alt, obj)
}
obj = alt // use object that was imported first
} }
return obj
} }
func (p *importer) obj(tag int) { func (p *importer) obj(tag int) {
...@@ -237,8 +272,7 @@ func (p *importer) obj(tag int) { ...@@ -237,8 +272,7 @@ func (p *importer) obj(tag int) {
pkg, name := p.qualifiedName() pkg, name := p.qualifiedName()
typ := p.typ(nil) typ := p.typ(nil)
val := p.value() val := p.value()
obj = types.NewConst(pos, pkg, name, typ, val) obj = p.declare(types.NewConst(pos, pkg, name, typ, val))
p.declare(obj)
case typeTag: case typeTag:
obj = p.typ(nil).(*types.Named).Obj() obj = p.typ(nil).(*types.Named).Obj()
...@@ -247,8 +281,7 @@ func (p *importer) obj(tag int) { ...@@ -247,8 +281,7 @@ func (p *importer) obj(tag int) {
pos := p.pos() pos := p.pos()
pkg, name := p.qualifiedName() pkg, name := p.qualifiedName()
typ := p.typ(nil) typ := p.typ(nil)
obj = types.NewVar(pos, pkg, name, typ) obj = p.declare(types.NewVar(pos, pkg, name, typ))
p.declare(obj)
case funcTag: case funcTag:
pos := p.pos() pos := p.pos()
...@@ -256,8 +289,7 @@ func (p *importer) obj(tag int) { ...@@ -256,8 +289,7 @@ func (p *importer) obj(tag int) {
params, isddd := p.paramList() params, isddd := p.paramList()
result, _ := p.paramList() result, _ := p.paramList()
sig := types.NewSignature(nil, params, result, isddd) sig := types.NewSignature(nil, params, result, isddd)
obj = types.NewFunc(pos, pkg, name, sig) obj = p.declare(types.NewFunc(pos, pkg, name, sig))
p.declare(obj)
default: default:
errorf("unexpected object tag %d", tag) errorf("unexpected object tag %d", tag)
......
...@@ -1308,12 +1308,14 @@ package b ...@@ -1308,12 +1308,14 @@ package b
import ( import (
"./testdata/alias" "./testdata/alias"
a "./testdata/alias" a "./testdata/alias"
// "math" // TODO(gri) does not work yet - fix importer (issue #17726) "math"
) )
const ( const (
c1 = alias.Pi c1 = alias.Pi1
c2 => a.Pi c2 => a.Pi1
c3 => a.Pi2
c4 => math.Pi
) )
var ( var (
...@@ -1331,7 +1333,8 @@ func f1 => alias.Sin ...@@ -1331,7 +1333,8 @@ func f1 => alias.Sin
func f2 => a.Sin func f2 => a.Sin
func _() { func _() {
assert(c1 == c2 && c1 == alias.Pi && c2 == a.Pi) assert(c1 == alias.Pi1 && c2 == a.Pi1 && c3 == a.Pi2 && c4 == math.Pi)
assert(c2 == c2 && c2 == c3 && c3 == c4)
v1 = v2 // must be assignable v1 = v2 // must be assignable
var _ *t1 = new(t2) // must be assignable var _ *t1 = new(t2) // must be assignable
var _ t2 = alias.Default var _ t2 = alias.Default
......
...@@ -11,7 +11,8 @@ import ( ...@@ -11,7 +11,8 @@ import (
"math" "math"
) )
const Pi => math.Pi const Pi1 => math.Pi
const Pi2 => math.Pi // cause the same object to be exported multiple times (issue 17726)
var Default => build.Default var Default => build.Default
......
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