]> Cypherpunks repositories - gostls13.git/commitdiff
go/internal/gcimporter: add support for importing parameterized types
authorRobert Findley <rfindley@google.com>
Wed, 1 Sep 2021 18:15:05 +0000 (14:15 -0400)
committerRobert Findley <rfindley@google.com>
Thu, 2 Sep 2021 14:02:42 +0000 (14:02 +0000)
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>
src/go/internal/gcimporter/gcimporter_test.go
src/go/internal/gcimporter/iimport.go

index 286b8a63471e6851dd14d06dc8650e859adf881c..3c76aafde39947ef49d9e42dd8107b5d753c210b 100644 (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)
        }
index dbc9b3a83e8ddeccec4a6c4687779536293b4a15..444cf3b0d409d15fd5565feff3decae0c0ad04ad 100644 (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
+}