diff --git a/go/internal/gcimporter/bimport.go b/go/internal/gcimporter/bimport.go index e9f73d14a1..b023120001 100644 --- a/go/internal/gcimporter/bimport.go +++ b/go/internal/gcimporter/bimport.go @@ -1029,6 +1029,7 @@ func predeclared() []types.Type { // used internally by gc; never used by this package or in .a files anyType{}, } + predecl = append(predecl, additionalPredeclared()...) }) return predecl } diff --git a/go/internal/gcimporter/iimport.go b/go/internal/gcimporter/iimport.go index 8ed8bc62d6..869c13281b 100644 --- a/go/internal/gcimporter/iimport.go +++ b/go/internal/gcimporter/iimport.go @@ -18,6 +18,8 @@ import ( "go/types" "io" "sort" + + "golang.org/x/tools/internal/typeparams" ) type intReader struct { @@ -41,6 +43,21 @@ func (r *intReader) uint64() uint64 { return i } +// Keep this in sync with constants in iexport.go. +const ( + iexportVersionGo1_11 = 0 + iexportVersionPosCol = 1 + // TODO: before release, change this back to 2. + iexportVersionGenerics = iexportVersionPosCol + + iexportVersionCurrent = iexportVersionGenerics +) + +type ident struct { + pkg string + name string +} + const predeclReserved = 32 type itag uint64 @@ -56,6 +73,9 @@ const ( signatureType structType interfaceType + typeParamType + instType + unionType ) // IImportData imports a package from the serialized package data @@ -101,9 +121,13 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data version = int64(r.uint64()) switch version { - case currentVersion, 0: + case /* iexportVersionGenerics, */ iexportVersionPosCol, iexportVersionGo1_11: default: - errorf("unknown iexport format version %d", version) + if version > iexportVersionGenerics { + errorf("unstable iexport format version %d, just rebuild compiler and std library", version) + } else { + errorf("unknown iexport format version %d", version) + } } sLen := int64(r.uint64()) @@ -115,8 +139,9 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data r.Seek(sLen+dLen, io.SeekCurrent) p := iimporter{ - ipath: path, - version: int(version), + exportVersion: version, + ipath: path, + version: int(version), stringData: stringData, stringCache: make(map[uint64]string), @@ -125,6 +150,9 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data 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, @@ -216,16 +244,18 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data } type iimporter struct { - ipath string - version int + exportVersion int64 + ipath string + version int stringData []byte 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 @@ -315,17 +345,27 @@ func (r *importReader) obj(name string) { r.declare(types.NewConst(pos, r.currPkg, name, typ, val)) - case 'F': + case 'F', 'G': + var tparams []*typeparams.TypeParam + if tag == 'G' { + tparams = r.tparamList() + } sig := r.signature(nil) - + typeparams.SetForSignature(sig, tparams) r.declare(types.NewFunc(pos, r.currPkg, name, sig)) - case 'T': + case 'T', 'U': // 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) + // Declare obj before calling r.tparamList, so the new type name is recognized + // if used in the constraint of one of its own typeparams (see #48280). r.declare(obj) + if tag == 'U' { + tparams := r.tparamList() + typeparams.SetForNamed(named, tparams) + } underlying := r.p.typAt(r.uint64(), named).Underlying() named.SetUnderlying(underlying) @@ -337,10 +377,46 @@ 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 := typeparams.NamedTypeArgs(baseType(msig.Recv().Type())) + if len(targs) > 0 { + rparams := make([]*typeparams.TypeParam, len(targs)) + for i := range rparams { + rparams[i], _ = targs[i].(*typeparams.TypeParam) + } + typeparams.SetRecvTypeParams(msig, rparams) + } + named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig)) } } + 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 := typeparams.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 + + typeparams.SetTypeParamConstraint(t, r.typ()) + case 'V': typ := r.typ() @@ -618,6 +694,49 @@ func (r *importReader) doType(base *types.Named) types.Type { typ := newInterface(methods, embeddeds) r.p.interfaceList = append(r.p.interfaceList, typ) return typ + + case typeParamType: + 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: + 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 *Environment + t, _ := typeparams.Instantiate(nil, baseType, targs, false) + return t + + case unionType: + if r.p.exportVersion < iexportVersionGenerics { + errorf("unexpected instantiation type") + } + terms := make([]*typeparams.Term, r.uint64()) + for i := range terms { + terms[i] = typeparams.NewTerm(r.bool(), r.typ()) + } + return typeparams.NewUnion(terms) } } @@ -632,6 +751,20 @@ func (r *importReader) signature(recv *types.Var) *types.Signature { return types.NewSignature(recv, params, results, variadic) } +func (r *importReader) tparamList() []*typeparams.TypeParam { + n := r.uint64() + if n == 0 { + return nil + } + xs := make([]*typeparams.TypeParam, n) + for i := range xs { + // Note: the standard library importer is tolerant of nil types here, + // though would panic in SetTypeParams. + xs[i] = r.typ().(*typeparams.TypeParam) + } + return xs +} + func (r *importReader) paramList() *types.Tuple { xs := make([]*types.Var, r.uint64()) for i := range xs { @@ -674,3 +807,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 +} diff --git a/go/internal/gcimporter/support_go117.go b/go/internal/gcimporter/support_go117.go new file mode 100644 index 0000000000..e6403e18a9 --- /dev/null +++ b/go/internal/gcimporter/support_go117.go @@ -0,0 +1,14 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.18 +// +build !go1.18 + +package gcimporter + +import "go/types" + +func additionalPredeclared() []types.Type { + return nil +} diff --git a/go/internal/gcimporter/support_go118.go b/go/internal/gcimporter/support_go118.go new file mode 100644 index 0000000000..a5c74856a7 --- /dev/null +++ b/go/internal/gcimporter/support_go118.go @@ -0,0 +1,18 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.18 +// +build go1.18 + +package gcimporter + +import "go/types" + +// additionalPredeclared returns additional predeclared types in go.1.18. +func additionalPredeclared() []types.Type { + return []types.Type{ + // comparable + types.Universe.Lookup("comparable").Type(), + } +} diff --git a/internal/lsp/source/hover.go b/internal/lsp/source/hover.go index 56666d352e..a2c731a71b 100644 --- a/internal/lsp/source/hover.go +++ b/internal/lsp/source/hover.go @@ -406,7 +406,7 @@ func moduleAtVersion(path string, i *IdentifierInfo) (string, string, bool) { func objectString(obj types.Object, qf types.Qualifier, inferred *types.Signature) string { // If the signature type was inferred, prefer the preferred signature with a // comment showing the generic signature. - if sig, _ := obj.Type().(*types.Signature); sig != nil && len(typeparams.ForSignature(sig)) > 0 && inferred != nil { + if sig, _ := obj.Type().(*types.Signature); sig != nil && typeparams.ForSignature(sig).Len() > 0 && inferred != nil { obj2 := types.NewFunc(obj.Pos(), obj.Pkg(), obj.Name(), inferred) str := types.ObjectString(obj2, qf) // Try to avoid overly long lines. diff --git a/internal/lsp/source/identifier.go b/internal/lsp/source/identifier.go index 155f7c4858..2bc3431d7d 100644 --- a/internal/lsp/source/identifier.go +++ b/internal/lsp/source/identifier.go @@ -377,7 +377,7 @@ func inferredSignature(info *types.Info, path []ast.Node) *types.Signature { // If the IndexExpr is fully instantiated, we consider that 'inference' for // gopls' purposes. sig, _ := info.TypeOf(e).(*types.Signature) - if sig != nil && len(typeparams.ForSignature(sig)) == 0 { + if sig != nil && typeparams.ForSignature(sig).Len() == 0 { return sig } _, sig = typeparams.GetInferred(info, e) diff --git a/internal/lsp/testdata/summary_generics.txt.golden b/internal/lsp/testdata/summary_go1.18.txt.golden similarity index 97% rename from internal/lsp/testdata/summary_generics.txt.golden rename to internal/lsp/testdata/summary_go1.18.txt.golden index f93110a6f3..aaeb2622b6 100644 --- a/internal/lsp/testdata/summary_generics.txt.golden +++ b/internal/lsp/testdata/summary_go1.18.txt.golden @@ -20,7 +20,7 @@ DefinitionsCount = 99 TypeDefinitionsCount = 18 HighlightsCount = 69 ReferencesCount = 27 -RenamesCount = 37 +RenamesCount = 41 PrepareRenamesCount = 7 SymbolsCount = 5 WorkspaceSymbolsCount = 20 diff --git a/internal/lsp/tests/tests.go b/internal/lsp/tests/tests.go index 5d6af9ed1b..13da55994c 100644 --- a/internal/lsp/tests/tests.go +++ b/internal/lsp/tests/tests.go @@ -47,7 +47,7 @@ var summaryFile = "summary.txt" func init() { if typeparams.Enabled { - summaryFile = "summary_generics.txt" + summaryFile = "summary_go1.18.txt" } } diff --git a/internal/typeparams/enabled_go117.go b/internal/typeparams/enabled_go117.go new file mode 100644 index 0000000000..18212390e1 --- /dev/null +++ b/internal/typeparams/enabled_go117.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.18 +// +build !go1.18 + +package typeparams + +// Enabled reports whether type parameters are enabled in the current build +// environment. +const Enabled = false diff --git a/internal/typeparams/enabled_go118.go b/internal/typeparams/enabled_go118.go new file mode 100644 index 0000000000..d67148823c --- /dev/null +++ b/internal/typeparams/enabled_go118.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.18 +// +build go1.18 + +package typeparams + +// Note: this constant is in a separate file as this is the only acceptable +// diff between the <1.18 API of this package and the 1.18 API. + +// Enabled reports whether type parameters are enabled in the current build +// environment. +const Enabled = true diff --git a/internal/typeparams/notypeparams.go b/internal/typeparams/notypeparams.go deleted file mode 100644 index e975e476f6..0000000000 --- a/internal/typeparams/notypeparams.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !typeparams || !go1.18 -// +build !typeparams !go1.18 - -package typeparams - -import ( - "go/ast" - "go/types" -) - -// NOTE: doc comments must be kept in sync with typeparams.go. - -// Enabled reports whether type parameters are enabled in the current build -// environment. -const Enabled = false - -// GetIndexExprData extracts data from AST nodes that represent index -// expressions. -// -// For an ast.IndexExpr, the resulting IndexExprData will have exactly one -// index expression. For an ast.MultiIndexExpr (go1.18+), it may have a -// variable number of index expressions. -// -// For nodes that don't represent index expressions, GetIndexExprData returns -// nil. -func GetIndexExprData(n ast.Node) *IndexExprData { - if e, _ := n.(*ast.IndexExpr); e != nil { - return &IndexExprData{ - X: e.X, - Lbrack: e.Lbrack, - Indices: []ast.Expr{e.Index}, - Rbrack: e.Rbrack, - } - } - return nil -} - -// ForTypeDecl extracts the (possibly nil) type parameter node list from n. -func ForTypeDecl(*ast.TypeSpec) *ast.FieldList { - return nil -} - -// ForFuncDecl extracts the (possibly nil) type parameter node list from n. -func ForFuncDecl(*ast.FuncDecl) *ast.FieldList { - return nil -} - -// ForSignature extracts the (possibly empty) type parameter object list from -// sig. -func ForSignature(*types.Signature) []*types.TypeName { - return nil -} - -// IsComparable reports if iface is the comparable interface. -func IsComparable(*types.Interface) bool { - return false -} - -// IsConstraint reports whether iface may only be used as a type parameter -// constraint (i.e. has a type set or is the comparable interface). -func IsConstraint(*types.Interface) bool { - return false -} - -// ForNamed extracts the (possibly empty) type parameter object list from -// named. -func ForNamed(*types.Named) []*types.TypeName { - return nil -} - -// NamedTArgs extracts the (possibly empty) type argument list from named. -func NamedTArgs(*types.Named) []types.Type { - return nil -} - -// InitInferred initializes info to record inferred type information. -func InitInferred(*types.Info) { -} - -// GetInferred extracts inferred type information from info for e. -// -// The expression e may have an inferred type if it is an *ast.IndexExpr -// representing partial instantiation of a generic function type for which type -// arguments have been inferred using constraint type inference, or if it is an -// *ast.CallExpr for which type type arguments have be inferred using both -// constraint type inference and function argument inference. -func GetInferred(*types.Info, ast.Expr) ([]types.Type, *types.Signature) { - return nil, nil -} diff --git a/internal/typeparams/typeparams.go b/internal/typeparams/typeparams.go deleted file mode 100644 index 12a4e6056f..0000000000 --- a/internal/typeparams/typeparams.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build typeparams && go1.18 -// +build typeparams,go1.18 - -package typeparams - -import ( - "go/ast" - "go/types" -) - -// NOTE: doc comments must be kept in sync with notypeparams.go. - -// Enabled reports whether type parameters are enabled in the current build -// environment. -const Enabled = true - -// GetIndexExprData extracts data from AST nodes that represent index -// expressions. -// -// For an ast.IndexExpr, the resulting IndexExprData will have exactly one -// index expression. For an ast.IndexListExpr (go1.18+), it may have a -// variable number of index expressions. -// -// For nodes that don't represent index expressions, GetIndexExprData returns -// nil. -func GetIndexExprData(n ast.Node) *IndexExprData { - switch e := n.(type) { - case *ast.IndexExpr: - return &IndexExprData{ - X: e.X, - Lbrack: e.Lbrack, - Indices: []ast.Expr{e.Index}, - Rbrack: e.Rbrack, - } - case *ast.IndexListExpr: - return (*IndexExprData)(e) - } - return nil -} - -// ForTypeDecl extracts the (possibly nil) type parameter node list from n. -func ForTypeDecl(n *ast.TypeSpec) *ast.FieldList { - return n.TypeParams -} - -// ForFuncDecl extracts the (possibly nil) type parameter node list from n. -func ForFuncDecl(n *ast.FuncDecl) *ast.FieldList { - if n.Type != nil { - return n.Type.TypeParams - } - return nil -} - -// ForSignature extracts the (possibly empty) type parameter object list from -// sig. -func ForSignature(sig *types.Signature) []*types.TypeName { - return tparamsSlice(sig.TypeParams()) -} - -// IsComparable reports if iface is the comparable interface. -func IsComparable(iface *types.Interface) bool { - return iface.IsComparable() -} - -// IsConstraint reports whether iface may only be used as a type parameter -// constraint (i.e. has a type set or is the comparable interface). -func IsConstraint(iface *types.Interface) bool { - return iface.IsConstraint() -} - -// ForNamed extracts the (possibly empty) type parameter object list from -// named. -func ForNamed(named *types.Named) []*types.TypeName { - return tparamsSlice(named.TypeParams()) -} - -func tparamsSlice(tparams *types.TypeParamList) []*types.TypeName { - length := tparams.Len() - if length == 0 { - return nil - } - - result := make([]*types.TypeName, length) - for i := 0; i < length; i++ { - result[i] = tparams.At(i).Obj() - } - - return result -} - -// NamedTArgs extracts the (possibly empty) type argument list from named. -func NamedTArgs(named *types.Named) []types.Type { - targs := named.TypeArgs() - numArgs := targs.Len() - - typs := make([]types.Type, numArgs) - for i := 0; i < numArgs; i++ { - typs[i] = targs.At(i) - } - - return typs -} - -// InitInferred initializes info to record inferred type information. -func InitInferred(info *types.Info) { - info.Inferred = make(map[ast.Expr]types.Inferred) -} - -// GetInferred extracts inferred type information from info for e. -// -// The expression e may have an inferred type if it is an *ast.IndexExpr -// representing partial instantiation of a generic function type for which type -// arguments have been inferred using constraint type inference, or if it is an -// *ast.CallExpr for which type type arguments have be inferred using both -// constraint type inference and function argument inference. -func GetInferred(info *types.Info, e ast.Expr) ([]types.Type, *types.Signature) { - if info.Inferred == nil { - return nil, nil - } - inf := info.Inferred[e] - - length := inf.TArgs.Len() - - typs := make([]types.Type, length) - for i := 0; i < length; i++ { - typs[i] = inf.TArgs.At(i) - } - - return typs, inf.Sig -} diff --git a/internal/typeparams/typeparams_go117.go b/internal/typeparams/typeparams_go117.go new file mode 100644 index 0000000000..d015ee1afa --- /dev/null +++ b/internal/typeparams/typeparams_go117.go @@ -0,0 +1,164 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.18 +// +build !go1.18 + +package typeparams + +import ( + "go/ast" + "go/types" +) + +func unsupported() { + panic("type parameters are unsupported at this go version") +} + +// GetIndexExprData extracts data from *ast.IndexExpr nodes. +// For other nodes, GetIndexExprData returns nil. +func GetIndexExprData(n ast.Node) *IndexExprData { + if e, _ := n.(*ast.IndexExpr); e != nil { + return &IndexExprData{ + X: e.X, + Lbrack: e.Lbrack, + Indices: []ast.Expr{e.Index}, + Rbrack: e.Rbrack, + } + } + return nil +} + +// ForTypeDecl returns an empty field list, as type parameters on not supported +// at this Go version. +func ForTypeDecl(*ast.TypeSpec) *ast.FieldList { + return nil +} + +// ForFuncDecl returns an empty field list, as type parameters are not +// supported at this Go version. +func ForFuncDecl(*ast.FuncDecl) *ast.FieldList { + return nil +} + +// TypeParam is a placeholder type, as type parameters are not supported at +// this Go version. Its methods panic on use. +type TypeParam struct{ types.Type } + +// TypeParamList is a placeholder for an empty type parameter list. +type TypeParamList struct{} + +func (*TypeParamList) Len() int { return 0 } +func (*TypeParamList) At(int) *TypeParam { unsupported(); return nil } + +// TypeList is a placeholder for an empty type list. +type TypeList struct{} + +func (*TypeList) Len() int { return 0 } +func (*TypeList) At(int) types.Type { unsupported(); return nil } + +// NewTypeParam is unsupported at this Go version, and panics. +func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { + unsupported() + return nil +} + +// SetTypeParamConstraint is unsupported at this Go version, and panics. +func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) { + unsupported() +} + +// ForSignature returns an empty slice. +func ForSignature(*types.Signature) *TypeParamList { + return nil +} + +// SetForSignature panics if tparams is non-empty. +func SetForSignature(_ *types.Signature, tparams []*TypeParam) { + if len(tparams) > 0 { + unsupported() + } +} + +// RecvTypeParams returns a nil slice. +func RecvTypeParams(sig *types.Signature) *TypeParamList { + return nil +} + +// SetRecvTypeParams panics if rparams is non-empty. +func SetRecvTypeParams(sig *types.Signature, rparams []*TypeParam) { + if len(rparams) > 0 { + unsupported() + } +} + +// IsComparable returns false, as no interfaces are type-restricted at this Go +// version. +func IsComparable(*types.Interface) bool { + return false +} + +// IsConstraint returns false, as no interfaces are type-restricted at this Go +// version. +func IsConstraint(*types.Interface) bool { + return false +} + +// ForNamed returns an empty type parameter list, as type parameters are not +// supported at this Go version. +func ForNamed(*types.Named) *TypeParamList { + return nil +} + +// SetForNamed panics if tparams is non-empty. +func SetForNamed(_ *types.Named, tparams []*TypeParam) { + if len(tparams) > 0 { + unsupported() + } +} + +// NamedTypeArgs extracts the (possibly empty) type argument list from named. +func NamedTypeArgs(*types.Named) []types.Type { + return nil +} + +// Term is a placeholder type, as type parameters are not supported at this Go +// version. Its methods panic on use. +type Term struct{ types.Type } + +// NewTerm is unsupported at this Go version, and panics. +func NewTerm(tilde bool, typ types.Type) *Term { + unsupported() + return nil +} + +// Union is a placeholder type, as type parameters are not supported at this Go +// version. Its methods panic on use. +type Union struct{ types.Type } + +// NewUnion is unsupported at this Go version, and panics. +func NewUnion(terms []*Term) *Union { + unsupported() + return nil +} + +// InitInferred is a noop at this Go version. +func InitInferred(*types.Info) { +} + +// GetInferred returns nothing, as type parameters are not supported at this Go +// version. +func GetInferred(*types.Info, ast.Expr) ([]types.Type, *types.Signature) { + return nil, nil +} + +// Environment is a placeholder type, as type parameters are not supported at +// this Go version. +type Environment struct{} + +// Instantiate is unsupported on this Go version, and panics. +func Instantiate(env *Environment, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { + unsupported() + return nil, nil +} diff --git a/internal/typeparams/typeparams_go118.go b/internal/typeparams/typeparams_go118.go new file mode 100644 index 0000000000..3e808e774d --- /dev/null +++ b/internal/typeparams/typeparams_go118.go @@ -0,0 +1,176 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.18 +// +build go1.18 + +package typeparams + +import ( + "go/ast" + "go/types" +) + +// GetIndexExprData extracts data from AST nodes that represent index +// expressions. +// +// For an ast.IndexExpr, the resulting IndexExprData will have exactly one +// index expression. For an ast.IndexListExpr (go1.18+), it may have a +// variable number of index expressions. +// +// For nodes that don't represent index expressions, GetIndexExprData returns +// nil. +func GetIndexExprData(n ast.Node) *IndexExprData { + switch e := n.(type) { + case *ast.IndexExpr: + return &IndexExprData{ + X: e.X, + Lbrack: e.Lbrack, + Indices: []ast.Expr{e.Index}, + Rbrack: e.Rbrack, + } + case *ast.IndexListExpr: + return (*IndexExprData)(e) + } + return nil +} + +// ForTypeDecl returns n.TypeParams. +func ForTypeDecl(n *ast.TypeSpec) *ast.FieldList { + return n.TypeParams +} + +// ForFuncDecl returns n.Type.TypeParams. +func ForFuncDecl(n *ast.FuncDecl) *ast.FieldList { + if n.Type != nil { + return n.Type.TypeParams + } + return nil +} + +// TypeParam is an alias for types.TypeParam +type TypeParam = types.TypeParam + +// TypeParamList is an alias for types.TypeParamList +type TypeParamList = types.TypeParamList + +// TypeList is an alias for types.TypeList +type TypeList = types.TypeList + +// NewTypeParam calls types.NewTypeParam. +func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { + return types.NewTypeParam(name, constraint) +} + +// SetTypeParamConstraint calls tparam.SetConstraint(constraint). +func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) { + tparam.SetConstraint(constraint) +} + +// ForSignature returns sig.TypeParams() +func ForSignature(sig *types.Signature) *TypeParamList { + return sig.TypeParams() +} + +// SetForSignature calls sig.SetTypeParams(tparams) +func SetForSignature(sig *types.Signature, tparams []*TypeParam) { + sig.SetTypeParams(tparams) +} + +// RecvTypeParams returns sig.RecvTypeParams(). +func RecvTypeParams(sig *types.Signature) *TypeParamList { + return sig.RecvTypeParams() +} + +// SetRecvTypeParams calls sig.SetRecvTypeParams(rparams). +func SetRecvTypeParams(sig *types.Signature, rparams []*TypeParam) { + sig.SetRecvTypeParams(rparams) +} + +// IsComparable calls iface.IsComparable(). +func IsComparable(iface *types.Interface) bool { + return iface.IsComparable() +} + +// IsConstraint calls iface.IsConstraint(). +func IsConstraint(iface *types.Interface) bool { + return iface.IsConstraint() +} + +// ForNamed extracts the (possibly empty) type parameter object list from +// named. +func ForNamed(named *types.Named) *TypeParamList { + return named.TypeParams() +} + +// SetForNamed sets the type params tparams on n. Each tparam must be of +// dynamic type *types.TypeParam. +func SetForNamed(n *types.Named, tparams []*TypeParam) { + n.SetTypeParams(tparams) +} + +// NamedTypeArgs extracts the (possibly empty) type argument list from named. +func NamedTypeArgs(named *types.Named) []types.Type { + targs := named.TypeArgs() + numArgs := targs.Len() + + typs := make([]types.Type, numArgs) + for i := 0; i < numArgs; i++ { + typs[i] = targs.At(i) + } + + return typs +} + +// Term is an alias for types.Term. +type Term = types.Term + +// NewTerm calls types.NewTerm. +func NewTerm(tilde bool, typ types.Type) *Term { + return types.NewTerm(tilde, typ) +} + +// Union is an alias for types.Union +type Union = types.Union + +// NewUnion calls types.NewUnion. +func NewUnion(terms []*Term) *Union { + return types.NewUnion(terms) +} + +// InitInferred initializes info to record inferred type information. +func InitInferred(info *types.Info) { + info.Inferred = make(map[ast.Expr]types.Inferred) +} + +// GetInferred extracts inferred type information from info for e. +// +// The expression e may have an inferred type if it is an *ast.IndexExpr +// representing partial instantiation of a generic function type for which type +// arguments have been inferred using constraint type inference, or if it is an +// *ast.CallExpr for which type type arguments have be inferred using both +// constraint type inference and function argument inference. +func GetInferred(info *types.Info, e ast.Expr) ([]types.Type, *types.Signature) { + if info.Inferred == nil { + return nil, nil + } + inf := info.Inferred[e] + + length := inf.TArgs.Len() + + typs := make([]types.Type, length) + for i := 0; i < length; i++ { + typs[i] = inf.TArgs.At(i) + } + + return typs, inf.Sig +} + +// Environment is an alias for types.Environment. +type Environment = types.Environment + +// Instantiate calls types.Instantiate. +func Instantiate(env *Environment, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { + return types.Instantiate(env, typ, targs, validate) +} diff --git a/internal/typeparams/typeparams_test.go b/internal/typeparams/typeparams_test.go new file mode 100644 index 0000000000..9302ad75c1 --- /dev/null +++ b/internal/typeparams/typeparams_test.go @@ -0,0 +1,54 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.18 +// +build go1.18 + +package typeparams_test + +import ( + "go/ast" + "go/importer" + "go/parser" + "go/token" + "go/types" + "testing" + + "golang.org/x/tools/internal/apidiff" + "golang.org/x/tools/internal/testenv" +) + +func TestAPIConsistency(t *testing.T) { + testenv.NeedsGoBuild(t) // This is a lie. We actually need the source code. + + // The packages below exclude enabled_*.go, as typeparams.Enabled is + // permitted to change between versions. + old := typeCheck(t, []string{"common.go", "typeparams_go117.go"}) + new := typeCheck(t, []string{"common.go", "typeparams_go118.go"}) + + report := apidiff.Changes(old, new) + if len(report.Changes) > 0 { + t.Errorf("API diff:\n%s", report) + } +} + +func typeCheck(t *testing.T, filenames []string) *types.Package { + fset := token.NewFileSet() + var files []*ast.File + for _, name := range filenames { + f, err := parser.ParseFile(fset, name, nil, 0) + if err != nil { + t.Fatal(err) + } + files = append(files, f) + } + conf := types.Config{ + Importer: importer.Default(), + } + pkg, err := conf.Check("", fset, files, nil) + if err != nil { + t.Fatal(err) + } + return pkg +}