mirror of
https://github.com/golang/go
synced 2024-11-02 11:50:30 +00:00
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:
parent
4591f49938
commit
b8420baf46
2 changed files with 144 additions and 16 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue