Skip to content

Commit 0cffec9

Browse files
committed
go/internal/gcimporter: update iimport.go to support type parameters
This CL pulls in the latest changes from go/internal/gcimporter, while avoiding breaking the build on older go versions. To help maintain compatibility with older Go versions while minimizing the diff with the standard library importer, the internal/typeparams package was significantly expanded. I decided to use type aliases in the internal/typeparams package on Go version >= go1.18, and placeholder types on Go version < go1.18. This reduces the amount of copying needed in the APIs, though it might not be the best decision if we ever decide to export this package. Documentation was also updated to be more concise and specific to the Go version being used. In order to actually fix the x/tools Trybot for packages using generics in the standard library, we need to switch from the 'typeparams' build constraint to the 'go1.18' build constraint. This means if we make any additional API changes in go/types we'll have to submit them with a broken x/tools Trybot and then immediately fix the x/tools build. Change-Id: Ifa0b1c37b89dc549ee295fa3a959f03deda86e56 Reviewed-on: https://go-review.googlesource.com/c/tools/+/349949 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent 5492d01 commit 0cffec9

15 files changed

+633
-243
lines changed

go/internal/gcimporter/bimport.go

+1
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,7 @@ func predeclared() []types.Type {
10291029
// used internally by gc; never used by this package or in .a files
10301030
anyType{},
10311031
}
1032+
predecl = append(predecl, additionalPredeclared()...)
10321033
})
10331034
return predecl
10341035
}

go/internal/gcimporter/iimport.go

+175-12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
"go/types"
1919
"io"
2020
"sort"
21+
22+
"golang.org/x/tools/internal/typeparams"
2123
)
2224

2325
type intReader struct {
@@ -41,6 +43,21 @@ func (r *intReader) uint64() uint64 {
4143
return i
4244
}
4345

46+
// Keep this in sync with constants in iexport.go.
47+
const (
48+
iexportVersionGo1_11 = 0
49+
iexportVersionPosCol = 1
50+
// TODO: before release, change this back to 2.
51+
iexportVersionGenerics = iexportVersionPosCol
52+
53+
iexportVersionCurrent = iexportVersionGenerics
54+
)
55+
56+
type ident struct {
57+
pkg string
58+
name string
59+
}
60+
4461
const predeclReserved = 32
4562

4663
type itag uint64
@@ -56,6 +73,9 @@ const (
5673
signatureType
5774
structType
5875
interfaceType
76+
typeParamType
77+
instType
78+
unionType
5979
)
6080

6181
// IImportData imports a package from the serialized package data
@@ -101,9 +121,13 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
101121

102122
version = int64(r.uint64())
103123
switch version {
104-
case currentVersion, 0:
124+
case /* iexportVersionGenerics, */ iexportVersionPosCol, iexportVersionGo1_11:
105125
default:
106-
errorf("unknown iexport format version %d", version)
126+
if version > iexportVersionGenerics {
127+
errorf("unstable iexport format version %d, just rebuild compiler and std library", version)
128+
} else {
129+
errorf("unknown iexport format version %d", version)
130+
}
107131
}
108132

109133
sLen := int64(r.uint64())
@@ -115,8 +139,9 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
115139
r.Seek(sLen+dLen, io.SeekCurrent)
116140

117141
p := iimporter{
118-
ipath: path,
119-
version: int(version),
142+
exportVersion: version,
143+
ipath: path,
144+
version: int(version),
120145

121146
stringData: stringData,
122147
stringCache: make(map[uint64]string),
@@ -125,6 +150,9 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
125150
declData: declData,
126151
pkgIndex: make(map[*types.Package]map[string]uint64),
127152
typCache: make(map[uint64]types.Type),
153+
// Separate map for typeparams, keyed by their package and unique
154+
// name (name with subscript).
155+
tparamIndex: make(map[ident]types.Type),
128156

129157
fake: fakeFileSet{
130158
fset: fset,
@@ -216,16 +244,18 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
216244
}
217245

218246
type iimporter struct {
219-
ipath string
220-
version int
247+
exportVersion int64
248+
ipath string
249+
version int
221250

222251
stringData []byte
223252
stringCache map[uint64]string
224253
pkgCache map[uint64]*types.Package
225254

226-
declData []byte
227-
pkgIndex map[*types.Package]map[string]uint64
228-
typCache map[uint64]types.Type
255+
declData []byte
256+
pkgIndex map[*types.Package]map[string]uint64
257+
typCache map[uint64]types.Type
258+
tparamIndex map[ident]types.Type
229259

230260
fake fakeFileSet
231261
interfaceList []*types.Interface
@@ -315,17 +345,27 @@ func (r *importReader) obj(name string) {
315345

316346
r.declare(types.NewConst(pos, r.currPkg, name, typ, val))
317347

318-
case 'F':
348+
case 'F', 'G':
349+
var tparams []*typeparams.TypeParam
350+
if tag == 'G' {
351+
tparams = r.tparamList()
352+
}
319353
sig := r.signature(nil)
320-
354+
typeparams.SetForSignature(sig, tparams)
321355
r.declare(types.NewFunc(pos, r.currPkg, name, sig))
322356

323-
case 'T':
357+
case 'T', 'U':
324358
// Types can be recursive. We need to setup a stub
325359
// declaration before recursing.
326360
obj := types.NewTypeName(pos, r.currPkg, name, nil)
327361
named := types.NewNamed(obj, nil, nil)
362+
// Declare obj before calling r.tparamList, so the new type name is recognized
363+
// if used in the constraint of one of its own typeparams (see #48280).
328364
r.declare(obj)
365+
if tag == 'U' {
366+
tparams := r.tparamList()
367+
typeparams.SetForNamed(named, tparams)
368+
}
329369

330370
underlying := r.p.typAt(r.uint64(), named).Underlying()
331371
named.SetUnderlying(underlying)
@@ -337,10 +377,46 @@ func (r *importReader) obj(name string) {
337377
recv := r.param()
338378
msig := r.signature(recv)
339379

380+
// If the receiver has any targs, set those as the
381+
// rparams of the method (since those are the
382+
// typeparams being used in the method sig/body).
383+
targs := typeparams.NamedTypeArgs(baseType(msig.Recv().Type()))
384+
if len(targs) > 0 {
385+
rparams := make([]*typeparams.TypeParam, len(targs))
386+
for i := range rparams {
387+
rparams[i], _ = targs[i].(*typeparams.TypeParam)
388+
}
389+
typeparams.SetRecvTypeParams(msig, rparams)
390+
}
391+
340392
named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig))
341393
}
342394
}
343395

396+
case 'P':
397+
// We need to "declare" a typeparam in order to have a name that
398+
// can be referenced recursively (if needed) in the type param's
399+
// bound.
400+
if r.p.exportVersion < iexportVersionGenerics {
401+
errorf("unexpected type param type")
402+
}
403+
name0, sub := parseSubscript(name)
404+
tn := types.NewTypeName(pos, r.currPkg, name0, nil)
405+
t := typeparams.NewTypeParam(tn, nil)
406+
if sub == 0 {
407+
errorf("missing subscript")
408+
}
409+
410+
// TODO(rfindley): can we use a different, stable ID?
411+
// t.SetId(sub)
412+
413+
// To handle recursive references to the typeparam within its
414+
// bound, save the partial type in tparamIndex before reading the bounds.
415+
id := ident{r.currPkg.Name(), name}
416+
r.p.tparamIndex[id] = t
417+
418+
typeparams.SetTypeParamConstraint(t, r.typ())
419+
344420
case 'V':
345421
typ := r.typ()
346422

@@ -618,6 +694,49 @@ func (r *importReader) doType(base *types.Named) types.Type {
618694
typ := newInterface(methods, embeddeds)
619695
r.p.interfaceList = append(r.p.interfaceList, typ)
620696
return typ
697+
698+
case typeParamType:
699+
if r.p.exportVersion < iexportVersionGenerics {
700+
errorf("unexpected type param type")
701+
}
702+
pkg, name := r.qualifiedIdent()
703+
id := ident{pkg.Name(), name}
704+
if t, ok := r.p.tparamIndex[id]; ok {
705+
// We're already in the process of importing this typeparam.
706+
return t
707+
}
708+
// Otherwise, import the definition of the typeparam now.
709+
r.p.doDecl(pkg, name)
710+
return r.p.tparamIndex[id]
711+
712+
case instType:
713+
if r.p.exportVersion < iexportVersionGenerics {
714+
errorf("unexpected instantiation type")
715+
}
716+
// pos does not matter for instances: they are positioned on the original
717+
// type.
718+
_ = r.pos()
719+
len := r.uint64()
720+
targs := make([]types.Type, len)
721+
for i := range targs {
722+
targs[i] = r.typ()
723+
}
724+
baseType := r.typ()
725+
// The imported instantiated type doesn't include any methods, so
726+
// we must always use the methods of the base (orig) type.
727+
// TODO provide a non-nil *Environment
728+
t, _ := typeparams.Instantiate(nil, baseType, targs, false)
729+
return t
730+
731+
case unionType:
732+
if r.p.exportVersion < iexportVersionGenerics {
733+
errorf("unexpected instantiation type")
734+
}
735+
terms := make([]*typeparams.Term, r.uint64())
736+
for i := range terms {
737+
terms[i] = typeparams.NewTerm(r.bool(), r.typ())
738+
}
739+
return typeparams.NewUnion(terms)
621740
}
622741
}
623742

@@ -632,6 +751,20 @@ func (r *importReader) signature(recv *types.Var) *types.Signature {
632751
return types.NewSignature(recv, params, results, variadic)
633752
}
634753

754+
func (r *importReader) tparamList() []*typeparams.TypeParam {
755+
n := r.uint64()
756+
if n == 0 {
757+
return nil
758+
}
759+
xs := make([]*typeparams.TypeParam, n)
760+
for i := range xs {
761+
// Note: the standard library importer is tolerant of nil types here,
762+
// though would panic in SetTypeParams.
763+
xs[i] = r.typ().(*typeparams.TypeParam)
764+
}
765+
return xs
766+
}
767+
635768
func (r *importReader) paramList() *types.Tuple {
636769
xs := make([]*types.Var, r.uint64())
637770
for i := range xs {
@@ -674,3 +807,33 @@ func (r *importReader) byte() byte {
674807
}
675808
return x
676809
}
810+
811+
func baseType(typ types.Type) *types.Named {
812+
// pointer receivers are never types.Named types
813+
if p, _ := typ.(*types.Pointer); p != nil {
814+
typ = p.Elem()
815+
}
816+
// receiver base types are always (possibly generic) types.Named types
817+
n, _ := typ.(*types.Named)
818+
return n
819+
}
820+
821+
func parseSubscript(name string) (string, uint64) {
822+
// Extract the subscript value from the type param name. We export
823+
// and import the subscript value, so that all type params have
824+
// unique names.
825+
sub := uint64(0)
826+
startsub := -1
827+
for i, r := range name {
828+
if '₀' <= r && r < '₀'+10 {
829+
if startsub == -1 {
830+
startsub = i
831+
}
832+
sub = sub*10 + uint64(r-'₀')
833+
}
834+
}
835+
if startsub >= 0 {
836+
name = name[:startsub]
837+
}
838+
return name, sub
839+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build !go1.18
6+
// +build !go1.18
7+
8+
package gcimporter
9+
10+
import "go/types"
11+
12+
func additionalPredeclared() []types.Type {
13+
return nil
14+
}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build go1.18
6+
// +build go1.18
7+
8+
package gcimporter
9+
10+
import "go/types"
11+
12+
// additionalPredeclared returns additional predeclared types in go.1.18.
13+
func additionalPredeclared() []types.Type {
14+
return []types.Type{
15+
// comparable
16+
types.Universe.Lookup("comparable").Type(),
17+
}
18+
}

internal/lsp/source/hover.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ func moduleAtVersion(path string, i *IdentifierInfo) (string, string, bool) {
406406
func objectString(obj types.Object, qf types.Qualifier, inferred *types.Signature) string {
407407
// If the signature type was inferred, prefer the preferred signature with a
408408
// comment showing the generic signature.
409-
if sig, _ := obj.Type().(*types.Signature); sig != nil && len(typeparams.ForSignature(sig)) > 0 && inferred != nil {
409+
if sig, _ := obj.Type().(*types.Signature); sig != nil && typeparams.ForSignature(sig).Len() > 0 && inferred != nil {
410410
obj2 := types.NewFunc(obj.Pos(), obj.Pkg(), obj.Name(), inferred)
411411
str := types.ObjectString(obj2, qf)
412412
// Try to avoid overly long lines.

internal/lsp/source/identifier.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ func inferredSignature(info *types.Info, path []ast.Node) *types.Signature {
377377
// If the IndexExpr is fully instantiated, we consider that 'inference' for
378378
// gopls' purposes.
379379
sig, _ := info.TypeOf(e).(*types.Signature)
380-
if sig != nil && len(typeparams.ForSignature(sig)) == 0 {
380+
if sig != nil && typeparams.ForSignature(sig).Len() == 0 {
381381
return sig
382382
}
383383
_, sig = typeparams.GetInferred(info, e)

internal/lsp/testdata/summary_generics.txt.golden renamed to internal/lsp/testdata/summary_go1.18.txt.golden

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ DefinitionsCount = 99
2020
TypeDefinitionsCount = 18
2121
HighlightsCount = 69
2222
ReferencesCount = 27
23-
RenamesCount = 37
23+
RenamesCount = 41
2424
PrepareRenamesCount = 7
2525
SymbolsCount = 5
2626
WorkspaceSymbolsCount = 20

internal/lsp/tests/tests.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ var summaryFile = "summary.txt"
4747

4848
func init() {
4949
if typeparams.Enabled {
50-
summaryFile = "summary_generics.txt"
50+
summaryFile = "summary_go1.18.txt"
5151
}
5252
}
5353

internal/typeparams/enabled_go117.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build !go1.18
6+
// +build !go1.18
7+
8+
package typeparams
9+
10+
// Enabled reports whether type parameters are enabled in the current build
11+
// environment.
12+
const Enabled = false

internal/typeparams/enabled_go118.go

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build go1.18
6+
// +build go1.18
7+
8+
package typeparams
9+
10+
// Note: this constant is in a separate file as this is the only acceptable
11+
// diff between the <1.18 API of this package and the 1.18 API.
12+
13+
// Enabled reports whether type parameters are enabled in the current build
14+
// environment.
15+
const Enabled = true

0 commit comments

Comments
 (0)