go/internal/gcimporter: add support for importing parameterized types

Port the necessary logic to go/internal/gcimporter from
cmd/compile/internal/importer/iimport.go to support type parameters.

This is a partial port of several compiler CLs: at least CL 319930,
CL 322609, CL 323029, CL 338192, CL 340251, and CL 340989. Because these
ports were not interleaved with the corresponding go/types API changes,
it is easier to just take the latest importer logic.

Notably, the equivalent of types2.AsTypeParam is not used. It should be
unnecessary.

Updates #48101

Change-Id: I938bd8debc3f6a68a3ad8d44c61ef9c5038be7e5
Reviewed-on: https://go-review.googlesource.com/c/go/+/347069
Trust: Robert Findley <rfindley@google.com>
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Robert Findley 2021-09-01 14:15:05 -04:00
parent 4591f49938
commit b8420baf46
2 changed files with 144 additions and 16 deletions

View file

@ -138,7 +138,6 @@ func TestVersionHandling(t *testing.T) {
skipSpecialPlatforms(t)
// This package only handles gc export data.
// Disable test until we put in the new export version.
if runtime.Compiler != "gc" {
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
}

View file

@ -51,6 +51,11 @@ const (
iexportVersionCurrent = iexportVersionGenerics
)
type ident struct {
pkg string
name string
}
const predeclReserved = 32
type itag uint64
@ -68,6 +73,7 @@ const (
interfaceType
typeParamType
instType
unionType
)
// iImportData imports a package from the serialized package data
@ -122,6 +128,9 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataRea
declData: declData,
pkgIndex: make(map[*types.Package]map[string]uint64),
typCache: make(map[uint64]types.Type),
// Separate map for typeparams, keyed by their package and unique
// name (name with subscript).
tparamIndex: make(map[ident]types.Type),
fake: fakeFileSet{
fset: fset,
@ -197,9 +206,10 @@ type iimporter struct {
stringCache map[uint64]string
pkgCache map[uint64]*types.Package
declData []byte
pkgIndex map[*types.Package]map[string]uint64
typCache map[uint64]types.Type
declData []byte
pkgIndex map[*types.Package]map[string]uint64
typCache map[uint64]types.Type
tparamIndex map[ident]types.Type
fake fakeFileSet
interfaceList []*types.Interface
@ -289,19 +299,28 @@ func (r *importReader) obj(name string) {
r.declare(types.NewConst(pos, r.currPkg, name, typ, val))
case 'F':
case 'F', 'G':
var tparams []*types.TypeParam
if tag == 'G' {
tparams = r.tparamList()
}
sig := r.signature(nil)
sig.SetTParams(tparams)
r.declare(types.NewFunc(pos, r.currPkg, name, sig))
case 'G':
errorf("unexpected parameterized function/method")
case 'T':
case 'T', 'U':
var tparams []*types.TypeParam
if tag == 'U' {
tparams = r.tparamList()
}
// Types can be recursive. We need to setup a stub
// declaration before recursing.
obj := types.NewTypeName(pos, r.currPkg, name, nil)
named := types.NewNamed(obj, nil, nil)
// TODO(rfindley): guarding on tag == 'U' should not be necessary here.
if tag == 'U' {
named.SetTParams(tparams)
}
r.declare(obj)
underlying := r.p.typAt(r.uint64(), named).Underlying()
@ -314,12 +333,45 @@ func (r *importReader) obj(name string) {
recv := r.param()
msig := r.signature(recv)
// If the receiver has any targs, set those as the
// rparams of the method (since those are the
// typeparams being used in the method sig/body).
targs := baseType(msig.Recv().Type()).TArgs()
if targs.Len() > 0 {
rparams := make([]*types.TypeParam, targs.Len())
for i := range rparams {
rparams[i], _ = targs.At(i).(*types.TypeParam)
}
msig.SetRParams(rparams)
}
named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig))
}
}
case 'U':
errorf("unexpected parameterized type")
case 'P':
// We need to "declare" a typeparam in order to have a name that
// can be referenced recursively (if needed) in the type param's
// bound.
if r.p.exportVersion < iexportVersionGenerics {
errorf("unexpected type param type")
}
name0, sub := parseSubscript(name)
tn := types.NewTypeName(pos, r.currPkg, name0, nil)
t := (*types.Checker)(nil).NewTypeParam(tn, nil)
if sub == 0 {
errorf("missing subscript")
}
// TODO(rfindley): can we use a different, stable ID?
// t.SetId(sub)
// To handle recursive references to the typeparam within its
// bound, save the partial type in tparamIndex before reading the bounds.
id := ident{r.currPkg.Name(), name}
r.p.tparamIndex[id] = t
t.SetConstraint(r.typ())
case 'V':
typ := r.typ()
@ -575,12 +627,47 @@ func (r *importReader) doType(base *types.Named) types.Type {
return typ
case typeParamType:
errorf("do not handle type param types yet")
return nil
if r.p.exportVersion < iexportVersionGenerics {
errorf("unexpected type param type")
}
pkg, name := r.qualifiedIdent()
id := ident{pkg.Name(), name}
if t, ok := r.p.tparamIndex[id]; ok {
// We're already in the process of importing this typeparam.
return t
}
// Otherwise, import the definition of the typeparam now.
r.p.doDecl(pkg, name)
return r.p.tparamIndex[id]
case instType:
errorf("do not handle instantiated types yet")
return nil
if r.p.exportVersion < iexportVersionGenerics {
errorf("unexpected instantiation type")
}
// pos does not matter for instances: they are positioned on the original
// type.
_ = r.pos()
len := r.uint64()
targs := make([]types.Type, len)
for i := range targs {
targs[i] = r.typ()
}
baseType := r.typ()
// The imported instantiated type doesn't include any methods, so
// we must always use the methods of the base (orig) type.
// TODO provide a non-nil *Checker
t, _ := types.Instantiate(nil, baseType, targs, false)
return t
case unionType:
if r.p.exportVersion < iexportVersionGenerics {
errorf("unexpected instantiation type")
}
terms := make([]*types.Term, r.uint64())
for i := range terms {
terms[i] = types.NewTerm(r.bool(), r.typ())
}
return types.NewUnion(terms)
}
}
@ -595,6 +682,18 @@ func (r *importReader) signature(recv *types.Var) *types.Signature {
return types.NewSignature(recv, params, results, variadic)
}
func (r *importReader) tparamList() []*types.TypeParam {
n := r.uint64()
if n == 0 {
return nil
}
xs := make([]*types.TypeParam, n)
for i := range xs {
xs[i], _ = r.typ().(*types.TypeParam)
}
return xs
}
func (r *importReader) paramList() *types.Tuple {
xs := make([]*types.Var, r.uint64())
for i := range xs {
@ -637,3 +736,33 @@ func (r *importReader) byte() byte {
}
return x
}
func baseType(typ types.Type) *types.Named {
// pointer receivers are never types.Named types
if p, _ := typ.(*types.Pointer); p != nil {
typ = p.Elem()
}
// receiver base types are always (possibly generic) types.Named types
n, _ := typ.(*types.Named)
return n
}
func parseSubscript(name string) (string, uint64) {
// Extract the subscript value from the type param name. We export
// and import the subscript value, so that all type params have
// unique names.
sub := uint64(0)
startsub := -1
for i, r := range name {
if '₀' <= r && r < '₀'+10 {
if startsub == -1 {
startsub = i
}
sub = sub*10 + uint64(r-'₀')
}
}
if startsub >= 0 {
name = name[:startsub]
}
return name, sub
}