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 {
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()
if alt := pkg.Scope().Insert(obj); alt != nil {
// This could 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
// b) we ignore compiler-specific export data which may contain functions
// whose inlined function bodies refer to other functions that were already
// imported.
// (See also the comment in cmd/compile/internal/gc/bimport.go importer.obj,
// switch case importing functions).
errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", alt, obj)
// This can only trigger if we import a (non-type) object a second time.
// Excluding aliases, this cannot happen because 1) we only import a package
// once; and b) we ignore compiler-specific export data which may contain
// functions whose inlined function bodies refer to other functions that
// were already imported.
// However, if a package exports multiple aliases referring to the same
// original object, that object is currently exported multiple times.
// 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) {
......@@ -237,8 +272,7 @@ func (p *importer) obj(tag int) {
pkg, name := p.qualifiedName()
typ := p.typ(nil)
val := p.value()
obj = types.NewConst(pos, pkg, name, typ, val)
p.declare(obj)
obj = p.declare(types.NewConst(pos, pkg, name, typ, val))
case typeTag:
obj = p.typ(nil).(*types.Named).Obj()
......@@ -247,8 +281,7 @@ func (p *importer) obj(tag int) {
pos := p.pos()
pkg, name := p.qualifiedName()
typ := p.typ(nil)
obj = types.NewVar(pos, pkg, name, typ)
p.declare(obj)
obj = p.declare(types.NewVar(pos, pkg, name, typ))
case funcTag:
pos := p.pos()
......@@ -256,8 +289,7 @@ func (p *importer) obj(tag int) {
params, isddd := p.paramList()
result, _ := p.paramList()
sig := types.NewSignature(nil, params, result, isddd)
obj = types.NewFunc(pos, pkg, name, sig)
p.declare(obj)
obj = p.declare(types.NewFunc(pos, pkg, name, sig))
default:
errorf("unexpected object tag %d", tag)
......
......@@ -1308,12 +1308,14 @@ package b
import (
"./testdata/alias"
a "./testdata/alias"
// "math" // TODO(gri) does not work yet - fix importer (issue #17726)
"math"
)
const (
c1 = alias.Pi
c2 => a.Pi
c1 = alias.Pi1
c2 => a.Pi1
c3 => a.Pi2
c4 => math.Pi
)
var (
......@@ -1331,7 +1333,8 @@ func f1 => alias.Sin
func f2 => a.Sin
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
var _ *t1 = new(t2) // must be assignable
var _ t2 = alias.Default
......
......@@ -11,7 +11,8 @@ import (
"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
......
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