diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go index 286b8a6347..3c76aafde3 100644 --- a/src/go/internal/gcimporter/gcimporter_test.go +++ b/src/go/internal/gcimporter/gcimporter_test.go @@ -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) } diff --git a/src/go/internal/gcimporter/iimport.go b/src/go/internal/gcimporter/iimport.go index dbc9b3a83e..444cf3b0d4 100644 --- a/src/go/internal/gcimporter/iimport.go +++ b/src/go/internal/gcimporter/iimport.go @@ -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 +}