| // 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. | |
| // Package typeparams contains common utilities for writing tools that interact | |
| // with generic Go code, as introduced with Go 1.18. | |
| // | |
| // Many of the types and functions in this package are proxies for the new APIs | |
| // introduced in the standard library with Go 1.18. For example, the | |
| // typeparams.Union type is an alias for go/types.Union, and the ForTypeSpec | |
| // function returns the value of the go/ast.TypeSpec.TypeParams field. | |
| // | |
| // Additionally, this package contains common utilities for working with the | |
| // new generic constructs, to supplement the standard library APIs. Notably, | |
| // the NormalTerms API computes a minimal representation of the structural | |
| // restrictions on a type parameter. In the future, these supplemental APIs may | |
| // be available in the standard library.. | |
| package typeparams | |
| import ( | |
| "go/ast" | |
| "go/token" | |
| "go/types" | |
| ) | |
| // Enabled reports whether type parameters are enabled in the current build | |
| // environment. | |
| // | |
| // Deprecated: Type parameters are always enabled. Or use go fix (after go1.26). | |
| // | |
| //go:fix inline | |
| func Enabled() bool { | |
| return true | |
| } | |
| // UnpackIndexExpr extracts data from AST nodes that represent index | |
| // expressions. | |
| // | |
| // For an ast.IndexExpr, the resulting indices slice will contain 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, the first return value of | |
| // UnpackIndexExpr will be nil. | |
| func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) { | |
| switch e := n.(type) { | |
| case *ast.IndexExpr: | |
| return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack | |
| case *IndexListExpr: | |
| return e.X, e.Lbrack, e.Indices, e.Rbrack | |
| } | |
| return nil, token.NoPos, nil, token.NoPos | |
| } | |
| // PackIndexExpr returns an *ast.IndexExpr or *ast.IndexListExpr, depending on | |
| // the cardinality of indices. Calling PackIndexExpr with len(indices) == 0 | |
| // will panic. | |
| func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) ast.Expr { | |
| switch len(indices) { | |
| case 0: | |
| panic("empty indices") | |
| case 1: | |
| return &ast.IndexExpr{ | |
| X: x, | |
| Lbrack: lbrack, | |
| Index: indices[0], | |
| Rbrack: rbrack, | |
| } | |
| default: | |
| return &IndexListExpr{ | |
| X: x, | |
| Lbrack: lbrack, | |
| Indices: indices, | |
| Rbrack: rbrack, | |
| } | |
| } | |
| } | |
| // IsTypeParam reports whether t is a type parameter. | |
| func IsTypeParam(t types.Type) bool { | |
| _, ok := t.(*TypeParam) | |
| return ok | |
| } | |
| // OriginMethod returns the origin method associated with the method fn. For | |
| // methods on a non-generic receiver base type, this is just fn. However, for | |
| // methods with a generic receiver, OriginMethod returns the corresponding | |
| // method in the method set of the origin type. | |
| // | |
| // As a special case, if fn is not a method (has no receiver), OriginMethod | |
| // returns fn. | |
| func OriginMethod(fn *types.Func) *types.Func { | |
| recv := fn.Type().(*types.Signature).Recv() | |
| if recv == nil { | |
| return fn | |
| } | |
| base := recv.Type() | |
| p, isPtr := base.(*types.Pointer) | |
| if isPtr { | |
| base = p.Elem() | |
| } | |
| named, isNamed := base.(*types.Named) | |
| if !isNamed { | |
| // Receiver is a *types.Interface. | |
| return fn | |
| } | |
| if ForNamed(named).Len() == 0 { | |
| // Receiver base has no type parameters, so we can avoid the lookup below. | |
| return fn | |
| } | |
| orig := NamedTypeOrigin(named) | |
| gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name()) | |
| return gfn.(*types.Func) | |
| } | |
| // GenericAssignableTo is a generalization of types.AssignableTo that | |
| // implements the following rule for uninstantiated generic types: | |
| // | |
| // If V and T are generic named types, then V is considered assignable to T if, | |
| // for every possible instantiation of V[A_1, ..., A_N], the instantiation | |
| // T[A_1, ..., A_N] is valid and V[A_1, ..., A_N] implements T[A_1, ..., A_N]. | |
| // | |
| // If T has structural constraints, they must be satisfied by V. | |
| // | |
| // For example, consider the following type declarations: | |
| // | |
| // type Interface[T any] interface { | |
| // Accept(T) | |
| // } | |
| // | |
| // type Container[T any] struct { | |
| // Element T | |
| // } | |
| // | |
| // func (c Container[T]) Accept(t T) { c.Element = t } | |
| // | |
| // In this case, GenericAssignableTo reports that instantiations of Container | |
| // are assignable to the corresponding instantiation of Interface. | |
| func GenericAssignableTo(ctxt *Context, V, T types.Type) bool { | |
| // If V and T are not both named, or do not have matching non-empty type | |
| // parameter lists, fall back on types.AssignableTo. | |
| VN, Vnamed := V.(*types.Named) | |
| TN, Tnamed := T.(*types.Named) | |
| if !Vnamed || !Tnamed { | |
| return types.AssignableTo(V, T) | |
| } | |
| vtparams := ForNamed(VN) | |
| ttparams := ForNamed(TN) | |
| if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || NamedTypeArgs(VN).Len() != 0 || NamedTypeArgs(TN).Len() != 0 { | |
| return types.AssignableTo(V, T) | |
| } | |
| // V and T have the same (non-zero) number of type params. Instantiate both | |
| // with the type parameters of V. This must always succeed for V, and will | |
| // succeed for T if and only if the type set of each type parameter of V is a | |
| // subset of the type set of the corresponding type parameter of T, meaning | |
| // that every instantiation of V corresponds to a valid instantiation of T. | |
| // Minor optimization: ensure we share a context across the two | |
| // instantiations below. | |
| if ctxt == nil { | |
| ctxt = NewContext() | |
| } | |
| var targs []types.Type | |
| for i := 0; i < vtparams.Len(); i++ { | |
| targs = append(targs, vtparams.At(i)) | |
| } | |
| vinst, err := Instantiate(ctxt, V, targs, true) | |
| if err != nil { | |
| panic("type parameters should satisfy their own constraints") | |
| } | |
| tinst, err := Instantiate(ctxt, T, targs, true) | |
| if err != nil { | |
| return false | |
| } | |
| return types.AssignableTo(vinst, tinst) | |
| } |