mirror of https://github.com/golang/go.git
gcimporters: allow reusing empty interfaces on the RHS of type decls
We guard against caching or reusing interfaces on the RHS of a type declaration, because for such interfaces the base type is used as the interface method receiver type. However, we don't need to do this for empty interfaces. By refining our guard, we can allow importing the predeclared 'any' type on the RHS of a type declaration. Update tests to add more coverage for importing generic export data. Some accomodation had to be made for the unified builder, which does not yet fully support generics in export data. Fixes #49888 Change-Id: I51f329de464fc7309f95991b839ab55868c2924f Reviewed-on: https://go-review.googlesource.com/c/go/+/367851 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
6c4bf8fb8a
commit
0103fd2b8b
|
|
@ -8,6 +8,7 @@ import (
|
|||
"bytes"
|
||||
"cmd/compile/internal/types2"
|
||||
"fmt"
|
||||
"internal/goexperiment"
|
||||
"internal/testenv"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
|
@ -107,25 +108,29 @@ func TestImportTestdata(t *testing.T) {
|
|||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
tmpdir := mktmpdir(t)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
testfiles := map[string][]string{
|
||||
"exports.go": {"go/ast", "go/token"},
|
||||
}
|
||||
if !goexperiment.Unified {
|
||||
testfiles["generics.go"] = nil
|
||||
}
|
||||
|
||||
compile(t, "testdata", "exports.go", filepath.Join(tmpdir, "testdata"))
|
||||
for testfile, wantImports := range testfiles {
|
||||
tmpdir := mktmpdir(t)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
if pkg := testPath(t, "./testdata/exports", tmpdir); pkg != nil {
|
||||
// The package's Imports list must include all packages
|
||||
// explicitly imported by exports.go, plus all packages
|
||||
// referenced indirectly via exported objects in exports.go.
|
||||
// With the textual export format, the list may also include
|
||||
// additional packages that are not strictly required for
|
||||
// import processing alone (they are exported to err "on
|
||||
// the safe side").
|
||||
// TODO(gri) update the want list to be precise, now that
|
||||
// the textual export data is gone.
|
||||
got := fmt.Sprint(pkg.Imports())
|
||||
for _, want := range []string{"go/ast", "go/token"} {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
|
||||
compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"))
|
||||
path := "./testdata/" + strings.TrimSuffix(testfile, ".go")
|
||||
|
||||
if pkg := testPath(t, path, tmpdir); pkg != nil {
|
||||
// The package's Imports list must include all packages
|
||||
// explicitly imported by testfile, plus all packages
|
||||
// referenced indirectly via exported objects in testfile.
|
||||
got := fmt.Sprint(pkg.Imports())
|
||||
for _, want := range wantImports {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ func (p *iimporter) posBaseAt(off uint64) *syntax.PosBase {
|
|||
}
|
||||
|
||||
func (p *iimporter) typAt(off uint64, base *types2.Named) types2.Type {
|
||||
if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) {
|
||||
if t, ok := p.typCache[off]; ok && canReuse(base, t) {
|
||||
return t
|
||||
}
|
||||
|
||||
|
|
@ -274,12 +274,30 @@ func (p *iimporter) typAt(off uint64, base *types2.Named) types2.Type {
|
|||
r.declReader = *strings.NewReader(p.declData[off-predeclReserved:])
|
||||
t := r.doType(base)
|
||||
|
||||
if base == nil || !isInterface(t) {
|
||||
if canReuse(base, t) {
|
||||
p.typCache[off] = t
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// canReuse reports whether the type rhs on the RHS of the declaration for def
|
||||
// may be re-used.
|
||||
//
|
||||
// Specifically, if def is non-nil and rhs is an interface type with methods, it
|
||||
// may not be re-used because we have a convention of setting the receiver type
|
||||
// for interface methods to def.
|
||||
func canReuse(def *types2.Named, rhs types2.Type) bool {
|
||||
if def == nil {
|
||||
return true
|
||||
}
|
||||
iface, _ := rhs.(*types2.Interface)
|
||||
if iface == nil {
|
||||
return true
|
||||
}
|
||||
// Don't use iface.Empty() here as iface may not be complete.
|
||||
return iface.NumEmbeddeds() == 0 && iface.NumExplicitMethods() == 0
|
||||
}
|
||||
|
||||
type importReader struct {
|
||||
p *iimporter
|
||||
declReader strings.Reader
|
||||
|
|
|
|||
|
|
@ -15,14 +15,17 @@ const init1 = 0
|
|||
func init() {}
|
||||
|
||||
const (
|
||||
C0 int = 0
|
||||
C1 = 3.14159265
|
||||
C2 = 2.718281828i
|
||||
C3 = -123.456e-789
|
||||
C4 = +123.456e+789
|
||||
C5 = 1234i
|
||||
C6 = "foo\n"
|
||||
C7 = `bar\n`
|
||||
C0 int = 0
|
||||
C1 = 3.14159265
|
||||
C2 = 2.718281828i
|
||||
C3 = -123.456e-789
|
||||
C4 = +123.456e+789
|
||||
C5 = 1234i
|
||||
C6 = "foo\n"
|
||||
C7 = `bar\n`
|
||||
C8 = 42
|
||||
C9 int = 42
|
||||
C10 float64 = 42
|
||||
)
|
||||
|
||||
type (
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
// This file is used to generate an object file which
|
||||
// serves as test file for gcimporter_test.go.
|
||||
|
||||
package generics
|
||||
|
||||
type Any any
|
||||
|
||||
var x any
|
||||
|
||||
type T[A, B any] struct {
|
||||
Left A
|
||||
Right B
|
||||
}
|
||||
|
||||
var X T[int, string] = T[int, string]{1, "hi"}
|
||||
|
||||
func ToInt[P interface{ ~int }](p P) int { return int(p) }
|
||||
|
||||
var IntID = ToInt[int]
|
||||
|
||||
type G[C comparable] int
|
||||
|
||||
func ImplicitFunc[T ~int]() {}
|
||||
|
||||
type ImplicitType[T ~int] int
|
||||
|
|
@ -118,25 +118,29 @@ func TestImportTestdata(t *testing.T) {
|
|||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
tmpdir := mktmpdir(t)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
testfiles := map[string][]string{
|
||||
"exports.go": {"go/ast", "go/token"},
|
||||
}
|
||||
if !goexperiment.Unified {
|
||||
testfiles["generics.go"] = nil
|
||||
}
|
||||
|
||||
compile(t, "testdata", "exports.go", filepath.Join(tmpdir, "testdata"))
|
||||
for testfile, wantImports := range testfiles {
|
||||
tmpdir := mktmpdir(t)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
if pkg := testPath(t, "./testdata/exports", tmpdir); pkg != nil {
|
||||
// The package's Imports list must include all packages
|
||||
// explicitly imported by exports.go, plus all packages
|
||||
// referenced indirectly via exported objects in exports.go.
|
||||
// With the textual export format, the list may also include
|
||||
// additional packages that are not strictly required for
|
||||
// import processing alone (they are exported to err "on
|
||||
// the safe side").
|
||||
// TODO(gri) update the want list to be precise, now that
|
||||
// the textual export data is gone.
|
||||
got := fmt.Sprint(pkg.Imports())
|
||||
for _, want := range []string{"go/ast", "go/token"} {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
|
||||
compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"))
|
||||
path := "./testdata/" + strings.TrimSuffix(testfile, ".go")
|
||||
|
||||
if pkg := testPath(t, path, tmpdir); pkg != nil {
|
||||
// The package's Imports list must include all packages
|
||||
// explicitly imported by testfile, plus all packages
|
||||
// referenced indirectly via exported objects in testfile.
|
||||
got := fmt.Sprint(pkg.Imports())
|
||||
for _, want := range wantImports {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ func (p *iimporter) pkgAt(off uint64) *types.Package {
|
|||
}
|
||||
|
||||
func (p *iimporter) typAt(off uint64, base *types.Named) types.Type {
|
||||
if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) {
|
||||
if t, ok := p.typCache[off]; ok && canReuse(base, t) {
|
||||
return t
|
||||
}
|
||||
|
||||
|
|
@ -267,12 +267,30 @@ func (p *iimporter) typAt(off uint64, base *types.Named) types.Type {
|
|||
r.declReader.Reset(p.declData[off-predeclReserved:])
|
||||
t := r.doType(base)
|
||||
|
||||
if base == nil || !isInterface(t) {
|
||||
if canReuse(base, t) {
|
||||
p.typCache[off] = t
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// canReuse reports whether the type rhs on the RHS of the declaration for def
|
||||
// may be re-used.
|
||||
//
|
||||
// Specifically, if def is non-nil and rhs is an interface type with methods, it
|
||||
// may not be re-used because we have a convention of setting the receiver type
|
||||
// for interface methods to def.
|
||||
func canReuse(def *types.Named, rhs types.Type) bool {
|
||||
if def == nil {
|
||||
return true
|
||||
}
|
||||
iface, _ := rhs.(*types.Interface)
|
||||
if iface == nil {
|
||||
return true
|
||||
}
|
||||
// Don't use iface.Empty() here as iface may not be complete.
|
||||
return iface.NumEmbeddeds() == 0 && iface.NumExplicitMethods() == 0
|
||||
}
|
||||
|
||||
type importReader struct {
|
||||
p *iimporter
|
||||
declReader bytes.Reader
|
||||
|
|
|
|||
|
|
@ -15,14 +15,17 @@ const init1 = 0
|
|||
func init() {}
|
||||
|
||||
const (
|
||||
C0 int = 0
|
||||
C1 = 3.14159265
|
||||
C2 = 2.718281828i
|
||||
C3 = -123.456e-789
|
||||
C4 = +123.456e+789
|
||||
C5 = 1234i
|
||||
C6 = "foo\n"
|
||||
C7 = `bar\n`
|
||||
C0 int = 0
|
||||
C1 = 3.14159265
|
||||
C2 = 2.718281828i
|
||||
C3 = -123.456e-789
|
||||
C4 = +123.456e+789
|
||||
C5 = 1234i
|
||||
C6 = "foo\n"
|
||||
C7 = `bar\n`
|
||||
C8 = 42
|
||||
C9 int = 42
|
||||
C10 float64 = 42
|
||||
)
|
||||
|
||||
type (
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
// This file is used to generate an object file which
|
||||
// serves as test file for gcimporter_test.go.
|
||||
|
||||
package generics
|
||||
|
||||
type Any any
|
||||
|
||||
var x any
|
||||
|
||||
type T[A, B any] struct {
|
||||
Left A
|
||||
Right B
|
||||
}
|
||||
|
||||
var X T[int, string] = T[int, string]{1, "hi"}
|
||||
|
||||
func ToInt[P interface{ ~int }](p P) int { return int(p) }
|
||||
|
||||
var IntID = ToInt[int]
|
||||
|
||||
type G[C comparable] int
|
||||
|
||||
func ImplicitFunc[T ~int]() {}
|
||||
|
||||
type ImplicitType[T ~int] int
|
||||
Loading…
Reference in New Issue