From c3e0dc23fbca3ad168502f688e7c3f1882f62b73 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 1 Nov 2016 14:46:59 -0700 Subject: [PATCH] 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/32538 Reviewed-by: Matthew Dempsky Run-TryBot: Robert Griesemer --- src/go/internal/gcimporter/bimport.go | 62 ++++++++++++++++++++------- src/go/types/api_test.go | 11 +++-- src/go/types/testdata/alias.go | 3 +- 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go index 2d6133a31b7..9e2fbd3c684 100644 --- a/src/go/internal/gcimporter/bimport.go +++ b/src/go/internal/gcimporter/bimport.go @@ -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) diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index 4db9760ba59..17a98f91a87 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -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 diff --git a/src/go/types/testdata/alias.go b/src/go/types/testdata/alias.go index c74aeaa48ed..40111fb060a 100644 --- a/src/go/types/testdata/alias.go +++ b/src/go/types/testdata/alias.go @@ -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