github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/typeparams/common.go (about) 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 // Package typeparams contains common utilities for writing tools that interact 6 // with generic Go code, as introduced with Go 1.18. 7 // 8 // Many of the types and functions in this package are proxies for the new APIs 9 // introduced in the standard library with Go 1.18. For example, the 10 // typeparams.Union type is an alias for go/types.Union, and the ForTypeSpec 11 // function returns the value of the go/ast.TypeSpec.TypeParams field. At Go 12 // versions older than 1.18 these helpers are implemented as stubs, allowing 13 // users of this package to write code that handles generic constructs inline, 14 // even if the Go version being used to compile does not support generics. 15 // 16 // Additionally, this package contains common utilities for working with the 17 // new generic constructs, to supplement the standard library APIs. Notably, 18 // the StructuralTerms API computes a minimal representation of the structural 19 // restrictions on a type parameter. In the future, this API may be available 20 // from go/types. 21 // 22 // See the example/README.md for a more detailed guide on how to update tools 23 // to support generics. 24 package typeparams 25 26 import ( 27 "go/ast" 28 "go/token" 29 "go/types" 30 ) 31 32 // UnpackIndexExpr extracts data from AST nodes that represent index 33 // expressions. 34 // 35 // For an ast.IndexExpr, the resulting indices slice will contain exactly one 36 // index expression. For an ast.IndexListExpr (go1.18+), it may have a variable 37 // number of index expressions. 38 // 39 // For nodes that don't represent index expressions, the first return value of 40 // UnpackIndexExpr will be nil. 41 func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) { 42 switch e := n.(type) { 43 case *ast.IndexExpr: 44 return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack 45 case *IndexListExpr: 46 return e.X, e.Lbrack, e.Indices, e.Rbrack 47 } 48 return nil, token.NoPos, nil, token.NoPos 49 } 50 51 // PackIndexExpr returns an *ast.IndexExpr or *ast.IndexListExpr, depending on 52 // the cardinality of indices. Calling PackIndexExpr with len(indices) == 0 53 // will panic. 54 func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) ast.Expr { 55 switch len(indices) { 56 case 0: 57 panic("empty indices") 58 case 1: 59 return &ast.IndexExpr{ 60 X: x, 61 Lbrack: lbrack, 62 Index: indices[0], 63 Rbrack: rbrack, 64 } 65 default: 66 return &IndexListExpr{ 67 X: x, 68 Lbrack: lbrack, 69 Indices: indices, 70 Rbrack: rbrack, 71 } 72 } 73 } 74 75 // IsTypeParam reports whether t is a type parameter. 76 func IsTypeParam(t types.Type) bool { 77 _, ok := t.(*TypeParam) 78 return ok 79 } 80 81 // OriginMethod returns the origin method associated with the method fn. 82 // For methods on a non-generic receiver base type, this is just 83 // fn. However, for methods with a generic receiver, OriginMethod returns the 84 // corresponding method in the method set of the origin type. 85 // 86 // As a special case, if fn is not a method (has no receiver), OriginMethod 87 // returns fn. 88 func OriginMethod(fn *types.Func) *types.Func { 89 recv := fn.Type().(*types.Signature).Recv() 90 if recv == nil { 91 92 return fn 93 } 94 base := recv.Type() 95 p, isPtr := base.(*types.Pointer) 96 if isPtr { 97 base = p.Elem() 98 } 99 named, isNamed := base.(*types.Named) 100 if !isNamed { 101 // Receiver is a *types.Interface. 102 return fn 103 } 104 if ForNamed(named).Len() == 0 { 105 // Receiver base has no type parameters, so we can avoid the lookup below. 106 return fn 107 } 108 orig := NamedTypeOrigin(named) 109 gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name()) 110 return gfn.(*types.Func) 111 } 112 113 // GenericAssignableTo is a generalization of types.AssignableTo that 114 // implements the following rule for uninstantiated generic types: 115 // 116 // If V and T are generic named types, then V is considered assignable to T if, 117 // for every possible instantation of V[A_1, ..., A_N], the instantiation 118 // T[A_1, ..., A_N] is valid and V[A_1, ..., A_N] implements T[A_1, ..., A_N]. 119 // 120 // If T has structural constraints, they must be satisfied by V. 121 // 122 // For example, consider the following type declarations: 123 // 124 // type Interface[T any] interface { 125 // Accept(T) 126 // } 127 // 128 // type Container[T any] struct { 129 // Element T 130 // } 131 // 132 // func (c Container[T]) Accept(t T) { c.Element = t } 133 // 134 // In this case, GenericAssignableTo reports that instantiations of Container 135 // are assignable to the corresponding instantiation of Interface. 136 func GenericAssignableTo(ctxt *Context, V, T types.Type) bool { 137 // If V and T are not both named, or do not have matching non-empty type 138 // parameter lists, fall back on types.AssignableTo. 139 140 VN, Vnamed := V.(*types.Named) 141 TN, Tnamed := T.(*types.Named) 142 if !Vnamed || !Tnamed { 143 return types.AssignableTo(V, T) 144 } 145 146 vtparams := ForNamed(VN) 147 ttparams := ForNamed(TN) 148 if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || NamedTypeArgs(VN).Len() != 0 || NamedTypeArgs(TN).Len() != 0 { 149 return types.AssignableTo(V, T) 150 } 151 152 // V and T have the same (non-zero) number of type params. Instantiate both 153 // with the type parameters of V. This must always succeed for V, and will 154 // succeed for T if and only if the type set of each type parameter of V is a 155 // subset of the type set of the corresponding type parameter of T, meaning 156 // that every instantiation of V corresponds to a valid instantiation of T. 157 158 // Minor optimization: ensure we share a context across the two 159 // instantiations below. 160 if ctxt == nil { 161 ctxt = NewContext() 162 } 163 164 var targs []types.Type 165 for i := 0; i < vtparams.Len(); i++ { 166 targs = append(targs, vtparams.At(i)) 167 } 168 169 vinst, err := Instantiate(ctxt, V, targs, true) 170 if err != nil { 171 panic("type parameters should satisfy their own constraints") 172 } 173 174 tinst, err := Instantiate(ctxt, T, targs, true) 175 if err != nil { 176 return false 177 } 178 179 return types.AssignableTo(vinst, tinst) 180 }