github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/typeparams/example/README.md (about) 1 <!-- Autogenerated by weave; DO NOT EDIT --> 2 <!-- To regenerate the readme, run: --> 3 <!-- go run golang.org/x/example/gotypes@latest generic-go-types.md --> 4 5 # Updating tools to support type parameters. 6 7 This guide is maintained by Rob Findley (`rfindley@google.com`). 8 9 **status**: this document is currently a work-in-progress. See 10 [golang/go#50447](https://go.dev/issues/50447) for more details. 11 12 1. [Introduction](#introduction) 13 1. [Summary of new language features and their APIs](#summary-of-new-language-features-and-their-apis) 14 1. [Examples](#examples) 15 1. [Generic types](#generic-types) 16 1. [Constraint Interfaces](#constraint-interfaces) 17 1. [Instantiation](#instantiation) 18 1. [Updating tools while building at older Go versions](#updating-tools-while-building-at-older-go-versions) 19 1. [Further help](#further-help) 20 21 # Introduction 22 23 With Go 1.18, Go now supports generic programming via type parameters. This 24 document is intended to serve as a guide for tool authors that want to update 25 their tools to support the new language constructs introduced for generic Go. 26 27 This guide assumes some knowledge of the language changes to support generics. 28 See the following references for more information: 29 30 - The [original proposal](https://go.dev/issue/43651) for type parameters. 31 - The [addendum for type sets](https://go.dev/issue/45346). 32 - The [latest language specfication](https://tip.golang.org/ref/spec) (still in-progress as of 2021-01-11). 33 - The proposals for new APIs in 34 [go/token and go/ast](https://go.dev/issue/47781), and in 35 [go/types](https://go.dev/issue/47916). 36 37 It also assumes existing knowledge of `go/ast` and `go/types`. If you're just 38 getting started, 39 [x/example/gotypes](https://github.com/golang/example/tree/master/gotypes) is 40 a great introduction (and was the inspiration for this guide). 41 42 # Summary of new language features and their APIs 43 44 While generic Go programming is a large change to the language, at a high level 45 it introduces only a few new concepts. Specifically, we can break down our 46 discussion into the following three broad categories. In each category, the 47 relevant new APIs are listed (some constructors and getters/setters may be 48 elided where they are trivial). 49 50 **Generic types**. Types and functions may be _generic_, meaning their 51 declaration has a non-empty _type parameter list_: as in `type List[T any] 52 ...` or `func f[T1, T2 any]() { ... }`. Type parameter lists define placeholder 53 types (_type parameters_), scoped to the declaration, which may be substituted 54 by any type satisfying their corresponding _constraint interface_ to 55 _instantiate_ a new type or function. 56 57 Generic types may have methods, which declare `receiver type parameters` via 58 their receiver type expression: `func (r T[P1, ..., PN]) method(...) (...) 59 {...}`. 60 61 _New APIs_: 62 - The field `ast.TypeSpec.TypeParams` holds the type parameter list syntax for 63 type declarations. 64 - The field `ast.FuncType.TypeParams` holds the type parameter list syntax for 65 function declarations. 66 - The type `types.TypeParam` is a `types.Type` representing a type parameter. 67 On this type, the `Constraint` and `SetConstraint` methods allow 68 getting/setting the constraint, the `Index` method returns the index of the 69 type parameter in the type parameter list that declares it, and the `Obj` 70 method returns the object declared in the declaration scope for the type 71 parameter (a `types.TypeName`). 72 - The type `types.TypeParamList` holds a list of type parameters. 73 - The method `types.Named.TypeParams` returns the type parameters for a type 74 declaration. 75 - The method `types.Named.SetTypeParams` sets type parameters on a defined 76 type. 77 - The function `types.NewSignatureType` creates a new (possibly generic) 78 signature type. 79 - The method `types.Signature.RecvTypeParams` returns the receiver type 80 parameters for a method. 81 - The method `types.Signature.TypeParams` returns the type parameters for 82 a function. 83 84 **Constraint Interfaces**: type parameter constraints are interfaces, expressed 85 via an interface type expression. Interfaces that are only used in constraint 86 position are permitted new embedded elements composed of tilde expressions 87 (`~T`) and unions (`A | B | ~C`). The new builtin interface type `comparable` 88 is implemented by types for which `==` and `!=` are valid. As a special case, 89 the `interface` keyword may be omitted from constraint expressions if it may be 90 implied (in which case we say the interface is _implicit_). 91 92 _New APIs_: 93 - The constant `token.TILDE` is used to represent tilde expressions as an 94 `ast.UnaryExpr`. 95 - Union expressions are represented as an `ast.BinaryExpr` using `|`. This 96 means that `ast.BinaryExpr` may now be both a type and value expression. 97 - The method `types.Interface.IsImplicit` reports whether the `interface` 98 keyword was elided from this interface. 99 - The method `types.Interface.MarkImplicit` marks an interface as being 100 implicit. 101 - The method `types.Interface.IsComparable` reports whether every type in an 102 interface's type set is comparable. 103 - The method `types.Interface.IsMethodSet` reports whether an interface is 104 defined entirely by its methods (has no _specific types_). 105 - The type `types.Union` is a type that represents an embedded union 106 expression in an interface. May only appear as an embedded element in 107 interfaces. 108 - The type `types.Term` represents a (possibly tilde) term of a union. 109 110 **Instantiation**: generic types and functions may be _instantiated_ to create 111 non-generic types and functions by providing _type arguments_ (`var x T[int]`). 112 Function type arguments may be _inferred_ via function arguments, or via 113 type parameter constraints. 114 115 _New APIs_: 116 - The type `ast.IndexListExpr` holds index expressions with multiple indices, 117 as occurs in instantiation expressions with multiple type arguments, or in 118 receivers with multiple type parameters. 119 - The function `types.Instantiate` instantiates a generic type with type arguments. 120 - The type `types.Context` is an opaque instantiation context that may be 121 shared to reduce duplicate instances. 122 - The field `types.Config.Context` holds a shared `Context` to use for 123 instantiation while type-checking. 124 - The type `types.TypeList` holds a list of types. 125 - The type `types.ArgumentError` holds an error associated with a specific 126 argument index. Used to represent instantiation errors. 127 - The field `types.Info.Instances` maps instantiated identifiers to information 128 about the resulting type instance. 129 - The type `types.Instance` holds information about a type or function 130 instance. 131 - The method `types.Named.TypeArgs` reports the type arguments used to 132 instantiate a named type. 133 134 # Examples 135 136 The following examples demonstrate the new APIs above, and discuss their 137 properties. All examples are runnable, contained in subdirectories of the 138 directory holding this README. 139 140 ## Generic types 141 142 ### Type parameter lists 143 144 Suppose we want to understand the generic library below, which defines a generic 145 `Pair`, a constraint interface `Constraint`, and a generic function `MakePair`. 146 147 ``` 148 package main 149 150 type Constraint interface { 151 Value() interface{} 152 } 153 154 type Pair[L, R any] struct { 155 left L 156 right R 157 } 158 159 func MakePair[L, R Constraint](l L, r R) Pair[L, R] { 160 return Pair[L, R]{l, r} 161 } 162 ``` 163 164 We can use the new `TypeParams` fields in `ast.TypeSpec` and `ast.FuncType` to 165 access the syntax of the type parameter list. From there, we can access type 166 parameter types in at least three ways: 167 - by looking up type parameter definitions in `types.Info` 168 - by calling `TypeParams()` on `types.Named` or `types.Signature` 169 - by looking up type parameter objects in the declaration scope. Note that 170 there now may be a scope associated with an `ast.TypeSpec` node. 171 172 ``` 173 func PrintTypeParams(fset *token.FileSet, file *ast.File) error { 174 conf := types.Config{Importer: importer.Default()} 175 info := &types.Info{ 176 Scopes: make(map[ast.Node]*types.Scope), 177 Defs: make(map[*ast.Ident]types.Object), 178 } 179 _, err := conf.Check("hello", fset, []*ast.File{file}, info) 180 if err != nil { 181 return err 182 } 183 184 // For convenience, we can use ast.Inspect to find the nodes we want to 185 // investigate. 186 ast.Inspect(file, func(n ast.Node) bool { 187 var name *ast.Ident // the name of the generic object, or nil 188 var tparamSyntax *ast.FieldList // the list of type parameter fields 189 var tparamTypes *types.TypeParamList // the list of type parameter types 190 var scopeNode ast.Node // the node associated with the declaration scope 191 192 switch n := n.(type) { 193 case *ast.TypeSpec: 194 name = n.Name 195 tparamSyntax = n.TypeParams 196 tparamTypes = info.Defs[name].Type().(*types.Named).TypeParams() 197 name = n.Name 198 scopeNode = n 199 case *ast.FuncDecl: 200 name = n.Name 201 tparamSyntax = n.Type.TypeParams 202 tparamTypes = info.Defs[name].Type().(*types.Signature).TypeParams() 203 scopeNode = n.Type 204 } 205 206 if name == nil { 207 return true // not a generic object 208 } 209 210 // Option 1: find type parameters by looking at their declaring field list. 211 if tparamSyntax != nil { 212 fmt.Printf("%s has a type parameter field list with %d fields\n", name.Name, tparamSyntax.NumFields()) 213 for _, field := range tparamSyntax.List { 214 for _, name := range field.Names { 215 tparam := info.Defs[name] 216 fmt.Printf(" field %s defines an object %q\n", name.Name, tparam) 217 } 218 } 219 } else { 220 fmt.Printf("%s does not have a type parameter list\n", name.Name) 221 } 222 223 // Option 2: find type parameters via the TypeParams() method on the 224 // generic type. 225 fmt.Printf("%s has %d type parameters:\n", name.Name, tparamTypes.Len()) 226 for i := 0; i < tparamTypes.Len(); i++ { 227 tparam := tparamTypes.At(i) 228 fmt.Printf(" %s has constraint %s\n", tparam, tparam.Constraint()) 229 } 230 231 // Option 3: find type parameters by looking in the declaration scope. 232 scope, ok := info.Scopes[scopeNode] 233 if ok { 234 fmt.Printf("%s has a scope with %d objects:\n", name.Name, scope.Len()) 235 for _, name := range scope.Names() { 236 fmt.Printf(" %s is a %T\n", name, scope.Lookup(name)) 237 } 238 } else { 239 fmt.Printf("%s does not have a scope\n", name.Name) 240 } 241 242 return true 243 }) 244 return nil 245 } 246 ``` 247 248 This program produces the following output. Note that not every type spec has 249 a scope. 250 251 ``` 252 > go run github.com/powerman/golang-tools/internal/typeparams/example/findtypeparams 253 Constraint does not have a type parameter list 254 Constraint has 0 type parameters: 255 Constraint does not have a scope 256 Pair has a type parameter field list with 2 fields 257 field L defines an object "type parameter L any" 258 field R defines an object "type parameter R any" 259 Pair has 2 type parameters: 260 L has constraint any 261 R has constraint any 262 Pair has a scope with 2 objects: 263 L is a *types.TypeName 264 R is a *types.TypeName 265 MakePair has a type parameter field list with 2 fields 266 field L defines an object "type parameter L hello.Constraint" 267 field R defines an object "type parameter R hello.Constraint" 268 MakePair has 2 type parameters: 269 L has constraint hello.Constraint 270 R has constraint hello.Constraint 271 MakePair has a scope with 4 objects: 272 L is a *types.TypeName 273 R is a *types.TypeName 274 l is a *types.Var 275 r is a *types.Var 276 ``` 277 278 ### Methods on generic types 279 280 **TODO** 281 282 ## Constraint Interfaces 283 284 ### New interface elements 285 286 **TODO** 287 288 ### Implicit interfaces 289 290 **TODO** 291 292 ### Type sets 293 294 **TODO** 295 296 ## Instantiation 297 298 ### Finding instantiated types 299 300 **TODO** 301 302 ### Creating new instantiated types 303 304 **TODO** 305 306 ### Using a shared context 307 308 **TODO** 309 310 # Updating tools while building at older Go versions 311 312 In the examples above, we can see how a lot of the new APIs integrate with 313 existing usage of `go/ast` or `go/types`. However, most tools still need to 314 build at older Go versions, and handling the new language constructs in-line 315 will break builds at older Go versions. 316 317 For this purpose, the `x/exp/typeparams` package provides functions and types 318 that proxy the new APIs (with stub implementations at older Go versions). 319 **NOTE**: does not yet exist -- see 320 [golang/go#50447](https://go.dev/issues/50447) for more information. 321 322 # Further help 323 324 If you're working on updating a tool to support generics, and need help, please 325 feel free to reach out for help in any of the following ways: 326 - Via the [golang-tools](https://groups.google.com/g/golang-tools) mailing list. 327 - Directly to me via email (`rfindley@google.com`). 328 - For bugs, you can [file an issue](https://github.com/golang/go/issues/new/choose).