File: README.md

package info (click to toggle)
golang-golang-x-exp 0.0~git20230522.2e198f4-1~bpo12%2B1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-backports
  • size: 6,404 kB
  • sloc: ansic: 1,900; objc: 276; sh: 272; asm: 48; makefile: 26
file content (898 lines) | stat: -rw-r--r-- 31,178 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
<!-- Autogenerated by weave; DO NOT EDIT -->
<!-- To regenerate the readme, run: -->
<!-- go run golang.org/x/example/gotypes@latest generic-go-types.md -->

# Updating tools to support type parameters.

This guide is maintained by Rob Findley (`rfindley@google.com`).

**status**: this document is currently a rough-draft. See [golang/go#50447](https://go.dev/issues/50447) for more details.

1. [Who should read this guide](#who-should-read-this-guide)
1. [Introduction](#introduction)
1. [Summary of new language features and their APIs](#summary-of-new-language-features-and-their-apis)
1. [Examples](#examples)
	1. [Generic types: type parameters](#generic-types:-type-parameters)
	1. [Constraint Interfaces](#constraint-interfaces)
	1. [Instantiation](#instantiation)
	1. [Generic types continued: method sets and predicates](#generic-types-continued:-method-sets-and-predicates)
1. [Updating tools while building at older Go versions](#updating-tools-while-building-at-older-go-versions)
1. [Further help](#further-help)

# Who should read this guide

Read this guide if you are a tool author seeking to update your tools to
support generics Go code. Generics introduce significant new complexity to the
Go type system, because types can now be _parameterized_. While the
fundamentals of the `go/types` APIs remain the same, some previously valid
assumptions no longer hold. For example:

 - Type declarations need not correspond 1:1 with the types they define.
 - Interfaces are no longer determined entirely by their method set.
 - The set of concrete types implementing `types.Type` has grown to include
   `types.TypeParam` and `types.Union`.

# Introduction

With Go 1.18, Go now supports generic programming via type parameters. This
document is a guide for tool authors that want to update their tools to support
the new language constructs.

This guide assumes knowledge of the language changes to support generics. See
the following references for more information:

- The [original proposal](https://go.dev/issue/43651) for type parameters.
- The [addendum for type sets](https://go.dev/issue/45346).
- The [latest language specfication](https://tip.golang.org/ref/spec) (still in-progress as of 2021-01-11).
- The proposals for new APIs in
  [go/token and go/ast](https://go.dev/issue/47781), and in
  [go/types](https://go.dev/issue/47916).

It also assumes knowledge of `go/ast` and `go/types`. If you're just getting
started,
[x/example/gotypes](https://github.com/golang/example/tree/master/gotypes) is
a great introduction (and was the inspiration for this guide).

# Summary of new language features and their APIs

The introduction of of generic features appears as a large change to the
language, but a high level introduces only a few new concepts. We can break
down our discussion into the following three broad categories: generic types,
constraint interfaces, and instantiation. In each category below, the relevant
new APIs are listed (some constructors and getters/setters may be elided where
they are trivial):

**Generic types**. Types and functions may be _generic_, meaning their
declaration may have a non-empty _type parameter list_, as in
`type  List[T any] ...` or `func f[T1, T2 any]() { ... }`. Type parameter lists
define placeholder types (_type parameters_), scoped to the declaration, which
may be substituted by any type satisfying their corresponding _constraint
interface_ to _instantiate_ a new type or function.

Generic types may have methods, which declare `receiver type parameters` via
their receiver type expression: `func (r T[P1, ..., PN]) method(...) (...)
{...}`.

_New APIs_:
 - The field `ast.TypeSpec.TypeParams` holds the type parameter list syntax for
   type declarations.
 - The field `ast.FuncType.TypeParams` holds the type parameter list syntax for
   function declarations.
 - The type `types.TypeParam` is a `types.Type` representing a type parameter.
   On this type, the `Constraint` and `SetConstraint` methods allow
   getting/setting the constraint, the `Index` method returns the numeric index
   of the type parameter in the type parameter list that declares it, and the
   `Obj` method returns the object in the scope a for the type parameter (a
   `types.TypeName`). Generic type declarations have a new `*types.Scope` for
   type parameter declarations.
 - The type `types.TypeParamList` holds a list of type parameters.
 - The method `types.Named.TypeParams` returns the type parameters for a type
   declaration.
 - The method `types.Named.SetTypeParams` sets type parameters on a defined
   type.
 - The function `types.NewSignatureType` creates a new (possibly generic)
   signature type.
 - The method `types.Signature.RecvTypeParams` returns the receiver type
   parameters for a method.
 - The method `types.Signature.TypeParams` returns the type parameters for
   a function.

**Constraint Interfaces**: type parameter constraints are interfaces, expressed
by an interface type expression. Interfaces that are only used in constraint
position are permitted new embedded elements composed of tilde expressions
(`~T`) and unions (`A | B | ~C`). The new builtin interface type `comparable`
is implemented by types for which `==` and `!=` are valid (note that interfaces
must be statically comparable in this case, i.e., each type in the interface's
type set must be comparable). As a special case, the `interface` keyword may be
omitted from constraint expressions if it may be implied (in which case we say
the interface is _implicit_).

_New APIs_:
 - The constant `token.TILDE` is used to represent tilde expressions as an
   `ast.UnaryExpr`.
 - Union expressions are represented as an `ast.BinaryExpr` using `|`. This
   means that `ast.BinaryExpr` may now be both a type and value expression.
 - The method `types.Interface.IsImplicit` reports whether the `interface`
   keyword was elided from this interface.
 - The method `types.Interface.MarkImplicit` marks an interface as being
   implicit.
 - The method `types.Interface.IsComparable` reports whether every type in an
   interface's type set is comparable.
 - The method `types.Interface.IsMethodSet` reports whether an interface is
   defined entirely by its methods (has no _specific types_).
 - The type `types.Union` is a type that represents an embedded union
   expression in an interface. May only appear as an embedded element in
   interfaces.
 - The type `types.Term` represents a (possibly tilde) term of a union.

**Instantiation**: generic types and functions may be _instantiated_ to create
non-generic types and functions by providing _type arguments_ (`var x T[int]`).
Function type arguments may be _inferred_ via function arguments, or via
type parameter constraints.

_New APIs_:
 - The type `ast.IndexListExpr` holds index expressions with multiple indices,
   as in instantiation expressions with multiple type arguments or in receivers
   declaring multiple type parameters.
 - The function `types.Instantiate` instantiates a generic type with type arguments.
 - The type `types.Context` is an opaque instantiation context that may be
   shared to reduce duplicate instances.
 - The field `types.Config.Context` holds a shared `Context` to use for
   instantiation while type-checking.
 - The type `types.TypeList` holds a list of types.
 - The type `types.ArgumentError` holds an error associated with a specific
   type argument index. Used to represent instantiation errors.
 - The field `types.Info.Instances` maps instantiated identifiers to information
   about the resulting type instance.
 - The type `types.Instance` holds information about a type or function
   instance.
 - The method `types.Named.TypeArgs` reports the type arguments used to
   instantiate a named type.

# Examples

The following examples demonstrate the new APIs, and discuss their properties.
All examples are runnable, contained in subdirectories of the directory holding
this README.

## Generic types: type parameters

We say that a type is _generic_ if it has type parameters but no type
arguments. This section explains how we can inspect generic types with the new
`go/types` APIs.

### Type parameter lists

Suppose we want to understand the generic library below, which defines a generic
`Pair`, a constraint interface `Constraint`, and a generic function `MakePair`.

```
package main

type Constraint interface {
	Value() any
}

type Pair[L, R any] struct {
	left  L
	right R
}

func MakePair[L, R Constraint](l L, r R) Pair[L, R] {
	return Pair[L, R]{l, r}
}
```

We can use the new `TypeParams` fields in `ast.TypeSpec` and `ast.FuncType` to
access the type parameter list. From there, we can access type parameter types
in at least three ways:
 - by looking up type parameter definitions in `types.Info`
 - by calling `TypeParams()` on `types.Named` or `types.Signature`
 - by looking up type parameter objects in the declaration scope. Note that
   there now may be a scope associated with an `ast.TypeSpec` node.

```
func PrintTypeParams(fset *token.FileSet, file *ast.File) error {
	conf := types.Config{Importer: importer.Default()}
	info := &types.Info{
		Scopes: make(map[ast.Node]*types.Scope),
		Defs:   make(map[*ast.Ident]types.Object),
	}
	_, err := conf.Check("hello", fset, []*ast.File{file}, info)
	if err != nil {
		return err
	}

	// For convenience, we can use ast.Inspect to find the nodes we want to
	// investigate.
	ast.Inspect(file, func(n ast.Node) bool {
		var name *ast.Ident              // the name of the generic object, or nil
		var tparamFields *ast.FieldList  // the list of type parameter fields
		var tparams *types.TypeParamList // the list of type parameter types
		var scopeNode ast.Node           // the node associated with the declaration scope

		switch n := n.(type) {
		case *ast.TypeSpec:
			name = n.Name
			tparamFields = n.TypeParams
			tparams = info.Defs[name].Type().(*types.Named).TypeParams()
			scopeNode = n
		case *ast.FuncDecl:
			name = n.Name
			tparamFields = n.Type.TypeParams
			tparams = info.Defs[name].Type().(*types.Signature).TypeParams()
			scopeNode = n.Type
		default:
			// Not a type or function declaration.
			return true
		}

		// Option 1: find type parameters by looking at their declaring field list.
		if tparamFields != nil {
			fmt.Printf("%s has a type parameter field list with %d fields\n", name.Name, tparamFields.NumFields())
			for _, field := range tparamFields.List {
				for _, name := range field.Names {
					tparam := info.Defs[name]
					fmt.Printf("  field %s defines an object %q\n", name.Name, tparam)
				}
			}
		} else {
			fmt.Printf("%s does not have a type parameter list\n", name.Name)
		}

		// Option 2: find type parameters via the TypeParams() method on the
		// generic type.
		if tparams.Len() > 0 {
			fmt.Printf("%s has %d type parameters:\n", name.Name, tparams.Len())
			for i := 0; i < tparams.Len(); i++ {
				tparam := tparams.At(i)
				fmt.Printf("  %s has constraint %s\n", tparam, tparam.Constraint())
			}
		} else {
			fmt.Printf("%s does not have type parameters\n", name.Name)
		}

		// Option 3: find type parameters by looking in the declaration scope.
		scope, ok := info.Scopes[scopeNode]
		if ok {
			fmt.Printf("%s has a scope with %d objects:\n", name.Name, scope.Len())
			for _, name := range scope.Names() {
				fmt.Printf("  %s is a %T\n", name, scope.Lookup(name))
			}
		} else {
			fmt.Printf("%s does not have a scope\n", name.Name)
		}

		return true
	})
	return nil
}
```

This program produces the following output. Note that not every type spec has
a scope.

```
> go run golang.org/x/tools/internal/typeparams/example/findtypeparams
Constraint does not have a type parameter list
Constraint does not have type parameters
Constraint does not have a scope
Pair has a type parameter field list with 2 fields
  field L defines an object "type parameter L any"
  field R defines an object "type parameter R any"
Pair has 2 type parameters:
  L has constraint any
  R has constraint any
Pair has a scope with 2 objects:
  L is a *types.TypeName
  R is a *types.TypeName
MakePair has a type parameter field list with 2 fields
  field L defines an object "type parameter L hello.Constraint"
  field R defines an object "type parameter R hello.Constraint"
MakePair has 2 type parameters:
  L has constraint hello.Constraint
  R has constraint hello.Constraint
MakePair has a scope with 4 objects:
  L is a *types.TypeName
  R is a *types.TypeName
  l is a *types.Var
  r is a *types.Var
```

## Constraint Interfaces

In order to allow operations on type parameters, Go 1.18 introduces the notion
of [_type sets_](https://tip.golang.org/ref/spec#Interface_types), which is
abstractly the set of types that implement an interface. This section discusses
the new syntax for restrictions on interface type sets, and the APIs we can use
to understand them.

### New interface elements

Consider the generic library below:

```
package p

type Numeric interface{
	~int | ~float64 // etc...
}

func Square[N Numeric](n N) N {
	return n*n
}

type Findable interface {
	comparable
}

func Find[T Findable](s []T, v T) int {
	for i, v2 := range s {
		if v2 == v {
			return i
		}
	}
	return -1
}
```

In this library, we can see a few new features added in Go 1.18. The first is
the new syntax in the `Numeric` type: unions of tilde-terms, specifying that
the numeric type may only be satisfied by types whose underlying type is `int`
or `float64`.

The `go/ast` package parses this new syntax as a combination of unary and
binary expressions, which we can see using the following program:

```
func PrintNumericSyntax(fset *token.FileSet, file *ast.File) {
	// node is the AST node corresponding to the declaration for "Numeric."
	node := file.Scope.Lookup("Numeric").Decl.(*ast.TypeSpec)
	// Find the embedded syntax node.
	embedded := node.Type.(*ast.InterfaceType).Methods.List[0].Type
	// Use go/ast's built-in Print function to inspect the parsed syntax.
	ast.Print(fset, embedded)
}
```

Output:

```
     0  *ast.BinaryExpr {
     1  .  X: *ast.UnaryExpr {
     2  .  .  OpPos: p.go:6:2
     3  .  .  Op: ~
     4  .  .  X: *ast.Ident {
     5  .  .  .  NamePos: p.go:6:3
     6  .  .  .  Name: "int"
     7  .  .  }
     8  .  }
     9  .  OpPos: p.go:6:7
    10  .  Op: |
    11  .  Y: *ast.UnaryExpr {
    12  .  .  OpPos: p.go:6:9
    13  .  .  Op: ~
    14  .  .  X: *ast.Ident {
    15  .  .  .  NamePos: p.go:6:10
    16  .  .  .  Name: "float64"
    17  .  .  }
    18  .  }
    19  }
```

Once type-checked, these embedded expressions are represented using the new
`types.Union` type, which flattens the expression into a list of `*types.Term`.
We can also investigate two new methods of interface:
`types.Interface.IsComparable`, which reports whether the type set of an
interface is comparable, and `types.Interface.IsMethodSet`, which reports
whether an interface is expressable using methods alone.

```
func PrintInterfaceTypes(fset *token.FileSet, file *ast.File) error {
	conf := types.Config{}
	pkg, err := conf.Check("hello", fset, []*ast.File{file}, nil)
	if err != nil {
		return err
	}

	PrintIface(pkg, "Numeric")
	PrintIface(pkg, "Findable")

	return nil
}

func PrintIface(pkg *types.Package, name string) {
	obj := pkg.Scope().Lookup(name)
	fmt.Println(obj)
	iface := obj.Type().Underlying().(*types.Interface)
	for i := 0; i < iface.NumEmbeddeds(); i++ {
		fmt.Printf("\tembeded: %s\n", iface.EmbeddedType(i))
	}
	for i := 0; i < iface.NumMethods(); i++ {
		fmt.Printf("\tembeded: %s\n", iface.EmbeddedType(i))
	}
	fmt.Printf("\tIsComparable(): %t\n", iface.IsComparable())
	fmt.Printf("\tIsMethodSet(): %t\n", iface.IsMethodSet())
}
```

Output:

```
type hello.Numeric interface{~int|~float64}
        embeded: ~int|~float64
        IsComparable(): true
        IsMethodSet(): false
type hello.Findable interface{comparable}
        embeded: comparable
        IsComparable(): true
        IsMethodSet(): false
```

The `Findable` type demonstrates another new feature of Go 1.18: the comparable
built-in. Comparable is a special interface type, not expressable using
ordinary Go syntax, whose type-set consists of all comparable types.

### Implicit interfaces

For interfaces that do not have methods, we can inline them in constraints and
elide the `interface` keyword. In the example above, we could have done this
for the `Square` function:

```
package p

func Square[N ~int|~float64](n N) N {
	return n*n
}
```

In such cases, the `types.Interface.IsImplicit` method reports whether the
interface type was implicit. This does not affect the behavior of the
interface, but is captured for more accurate type strings:

```
func ShowImplicit(pkg *types.Package) {
	Square := pkg.Scope().Lookup("Square").Type().(*types.Signature)
	N := Square.TypeParams().At(0)
	constraint := N.Constraint().(*types.Interface)
	fmt.Println(constraint)
	fmt.Println("IsImplicit:", constraint.IsImplicit())
}
```

Output:

```
~int|~float64
IsImplicit: true
```

The `types.Interface.MarkImplicit` method is used to mark interfaces as
implicit by the importer.

### Type sets

The examples above demonstrate the new APIs for _accessing_ information about
the new interface elements, but how do we understand
[_type sets_](https://tip.golang.org/ref/spec#Interface_types), the new
abstraction that these elements help define? Type sets may be arbitrarily
complex, as in the following example:

```
package complex

type A interface{ ~string|~[]byte }

type B interface{ int|string }

type C interface { ~string|~int }

type D interface{ A|B; C }
```

Here, the type set of `D` simplifies to `~string|int`, but the current
`go/types` APIs do not expose this information. This will likely be added to
`go/types` in future versions of Go, but in the meantime we can use the
`typeparams.NormalTerms` helper:

```
func PrintNormalTerms(pkg *types.Package) error {
	D := pkg.Scope().Lookup("D").Type()
	terms, err := typeparams.NormalTerms(D)
	if err != nil {
		return err
	}
	for i, term := range terms {
		if i > 0 {
			fmt.Print("|")
		}
		fmt.Print(term)
	}
	fmt.Println()
	return nil
}
```

which outputs:

```
~string|int
```

See the documentation for `typeparams.NormalTerms` for more information on how
this calculation proceeds.

## Instantiation

We say that a type is _instantiated_ if it is created from a generic type by
substituting type arguments for type parameters. Instantiation can occur via
explicitly provided type arguments, as in the expression `T[A_1, ..., A_n]`, or
implicitly, through type inference.. This section describes how to find and
understand instantiated types.

### Finding instantiated types

Certain applications may find it useful to locate all instantiated types in
a package. For this purpose, `go/types` provides a new `types.Info.Instances`
field that maps instantiated identifiers to information about their instance.

For example, consider the following code:

```
package p

type Pair[L, R comparable] struct {
	left  L
	right R
}

func (p Pair[L, _]) Left() L {
	return p.left
}

func Equal[L, R comparable](x, y Pair[L, R]) bool {
	return x.left == y.left && x.right == y.right
}

var X Pair[int, string]
var Y Pair[string, int]

var E = Equal[int, string]
```

We can find instances by type-checking with the `types.Info.Instances` map
initialized:

```
func CheckInstances(fset *token.FileSet, file *ast.File) (*types.Package, error) {
	conf := types.Config{}
	info := &types.Info{
		Instances: make(map[*ast.Ident]types.Instance),
	}
	pkg, err := conf.Check("p", fset, []*ast.File{file}, info)
	for id, inst := range info.Instances {
		posn := fset.Position(id.Pos())
		fmt.Printf("%s: %s instantiated with %s: %s\n", posn, id.Name, FormatTypeList(inst.TypeArgs), inst.Type)
	}
	return pkg, err
}
```

Output:

```
hello.go:21:9: Equal instantiated with [int, string]: func(x p.Pair[int, string], y p.Pair[int, string]) bool
hello.go:10:9: Pair instantiated with [L, _]: p.Pair[L, _]
hello.go:14:34: Pair instantiated with [L, R]: p.Pair[L, R]
hello.go:18:7: Pair instantiated with [int, string]: p.Pair[int, string]
hello.go:19:7: Pair instantiated with [string, int]: p.Pair[string, int]
```

The `types.Instance` type provides information about the (possibly inferred)
type arguments that were used to instantiate the generic type, and the
resulting type. Notably, it does not include the _generic_ type that was
instantiated, because this type can be found using `types.Info.Uses[id].Type()`
(where `id` is the identifier node being instantiated).

Note that the receiver type of method `Left` also appears in the `Instances`
map. This may be counterintuitive -- more on this below.

### Creating new instantiated types

`go/types` also provides an API for creating type instances:
`types.Instantiate`. This function accepts a generic type and type arguments,
and returns an instantiated type (or an error). The resulting instance may be
a newly constructed type, or a previously created instance with the same type
identity. To facilitate the reuse of frequently used instances,
`types.Instantiate` accepts a `types.Context` as its first argument, which
records instances.

If the final `validate` argument to `types.Instantiate` is set, the provided
type arguments will be verified against their corresponding type parameter
constraint; i.e., `types.Instantiate` will check that each type arguments
implements the corresponding type parameter constraint. If a type arguments
does not implement the respective constraint, the resulting error will wrap
a new `ArgumentError` type indicating which type argument index was bad.

```
func Instantiate(pkg *types.Package) error {
	Pair := pkg.Scope().Lookup("Pair").Type()
	X := pkg.Scope().Lookup("X").Type()
	Y := pkg.Scope().Lookup("Y").Type()

	// X and Y have different types, because their type arguments are different.
	Compare("X", "Y", X, Y)

	// Create a shared context for the subsequent instantiations.
	ctxt := types.NewContext()

	// Instantiating with [int, string] yields an instance that is identical (but
	// not equal) to X.
	Int, String := types.Typ[types.Int], types.Typ[types.String]
	inst1, _ := types.Instantiate(ctxt, Pair, []types.Type{Int, String}, true)
	Compare("X", "inst1", X, inst1)

	// Instantiating again returns the same exact instance, because of the shared
	// Context.
	inst2, _ := types.Instantiate(ctxt, Pair, []types.Type{Int, String}, true)
	Compare("inst1", "inst2", inst1, inst2)

	// Instantiating with 'any' is an error, because any is not comparable.
	Any := types.Universe.Lookup("any").Type()
	_, err := types.Instantiate(ctxt, Pair, []types.Type{Int, Any}, true)
	var argErr *types.ArgumentError
	if errors.As(err, &argErr) {
		fmt.Printf("Argument %d: %v\n", argErr.Index, argErr.Err)
	}

	return nil
}

func Compare(leftName, rightName string, left, right types.Type) {
	fmt.Printf("Identical(%s, %s) : %t\n", leftName, rightName, types.Identical(left, right))
	fmt.Printf("%s == %s : %t\n\n", leftName, rightName, left == right)
}
```

Output:

```
Identical(p.Pair[int, string], p.Pair[string, int]) : false
p.Pair[int, string] == p.Pair[string, int] : false

Identical(p.Pair[int, string], p.Pair[int, string]) : true
p.Pair[int, string] == p.Pair[int, string] : false

Identical(p.Pair[string, int], p.Pair[int, string]) : false
p.Pair[string, int] == p.Pair[int, string] : false

Identical(p.Pair[int, string], p.Pair[int, string]) : true
p.Pair[int, string] == p.Pair[int, string] : true

Argument 1: any does not implement comparable
```

### Using a shared context while type checking

To share a common `types.Context` argument with a type-checking pass, set the
new `types.Config.Context` field.

## Generic types continued: method sets and predicates

Generic types are fundamentally different from ordinary types, in that they may
not be used without instantiation. In some senses they are not really types:
the go spec defines [types](https://tip.golang.org/ref/spec#Types) as "a set of
values, together with operations and methods", but uninstantiated generic types
do not define a set of values. Rather, they define a set of _types_. In that
sense, they are a "meta type", or a "type template" (disclaimer: I am using
these terms imprecisely).

However, for the purposes of `go/types` it is convenient to treat generic types
as a `types.Type`. This section explains how generic types behave in existing
`go/types` APIs.

### Method Sets

Methods on uninstantiated generic types are different from methods on an
ordinary type. Consider that for an ordinary type `T`, the receiver base type
of each method in its method set is `T`. However, this can't be the case for
a generic type: generic types cannot be used without instantation, and neither
can the type of the receiver variable. Instead, the receiver base type is an
_instantiated_ type, instantiated with the method's receiver type parameters.

This has some surprising consequences, which we observed in the section on
instantiation above: for a generic type `G`, each of its methods will define
a unique instantiation of `G`, as each method has distinct receiver type
parameters.

To see this, consider the following example:

```
package p

type Pair[L, R any] struct {
	left  L
	right R
}

func (p Pair[L, _]) Left() L {
	return p.left
}

func (p Pair[_, R]) Right() R {
	return p.right
}

var IntPair Pair[int, int]
```

Let's inspect the method sets of the types in this library:

```
func PrintMethods(pkg *types.Package) {
	// Look up *Named types in the package scope.
	lookup := func(name string) *types.Named {
		return pkg.Scope().Lookup(name).Type().(*types.Named)
	}

	Pair := lookup("Pair")
	IntPair := lookup("IntPair")
	PrintMethodSet("Pair", Pair)
	PrintMethodSet("Pair[int, int]", IntPair)
	LeftObj, _, _ := types.LookupFieldOrMethod(Pair, false, pkg, "Left")
	LeftRecvType := LeftObj.Type().(*types.Signature).Recv().Type()
	PrintMethodSet("Pair[L, _]", LeftRecvType)
}

func PrintMethodSet(name string, typ types.Type) {
	fmt.Println(name + ":")
	methodSet := types.NewMethodSet(typ)
	for i := 0; i < methodSet.Len(); i++ {
		method := methodSet.At(i).Obj()
		fmt.Println(method)
	}
	fmt.Println()
}
```

Output:

```
Pair:
func (p.Pair[L, _]).Left() L
func (p.Pair[_, R]).Right() R

Pair[int, int]:
func (p.Pair[int, int]).Left() int
func (p.Pair[int, int]).Right() int

Pair[L, _]:
func (p.Pair[L, _]).Left() L
func (p.Pair[L, _]).Right() _
```

In this example, we can see that all of `Pair`, `Pair[int, int]`, and
`Pair[L, _]` have distinct method sets, though the method set of `Pair` and
`Pair[L, _]` intersect in the `Left` method.

Only the objects in `Pair`'s method set are recorded in `types.Info.Defs`. To
get back to this "canonical" method object, the `typeparams` package provides
the `OriginMethod` helper:

```
func CompareOrigins(pkg *types.Package) {
	Pair := pkg.Scope().Lookup("Pair").Type().(*types.Named)
	IntPair := pkg.Scope().Lookup("IntPair").Type().(*types.Named)
	Left, _, _ := types.LookupFieldOrMethod(Pair, false, pkg, "Left")
	LeftInt, _, _ := types.LookupFieldOrMethod(IntPair, false, pkg, "Left")

	fmt.Println("Pair.Left == Pair[int, int].Left:", Left == LeftInt)
	origin := typeparams.OriginMethod(LeftInt.(*types.Func))
	fmt.Println("Pair.Left == OriginMethod(Pair[int, int].Left):", Left == origin)
}
```

Output:

```
Pair.Left == Pair[int, int].Left: false
Pair.Left == OriginMethod(Pair[int, int].Left): true
```

### Predicates

Predicates on generic types are not defined by the spec. As a consequence,
using e.g. `types.AssignableTo` with operands of generic types leads to an
undefined result.

The behavior of predicates on generic `*types.Named` types may generally be
derived from the fact that type parameters bound to different names are
different types. This means that most predicates involving generic types will
return `false`.

`*types.Signature` types are treated differently. Two signatures are considered
identical if they are identical after substituting one's set of type parameters
for the other's, including having identical type parameter constraints. This is
analogous to the treatment of ordinary value parameters, whose names do not
affect type identity.

Consider the following code:

```
func OrdinaryPredicates(pkg *types.Package) {
	var (
		Pair        = pkg.Scope().Lookup("Pair").Type()
		LeftRighter = pkg.Scope().Lookup("LeftRighter").Type()
		Mer         = pkg.Scope().Lookup("Mer").Type()
		F           = pkg.Scope().Lookup("F").Type()
		G           = pkg.Scope().Lookup("G").Type()
		H           = pkg.Scope().Lookup("H").Type()
	)

	fmt.Println("AssignableTo(Pair, LeftRighter)", types.AssignableTo(Pair, LeftRighter))
	fmt.Println("AssignableTo(Pair, Mer): ", types.AssignableTo(Pair, Mer))
	fmt.Println("Identical(F, G)", types.Identical(F, G))
	fmt.Println("Identical(F, H)", types.Identical(F, H))
}
```

Output:

```
AssignableTo(Pair, LeftRighter) false
AssignableTo(Pair, Mer):  true
Identical(F, G) true
Identical(F, H) false
```

In this example, we see that despite their similarity the generic `Pair` type
is not assignable to the generic `LeftRighter` type. We also see the rules for
signature identity in practice.

This begs the question: how does one ask questions about the relationship
between generic types? In order to phrase such questions we need more
information: how does one relate the type parameters of `Pair` to the type
parameters of `LeftRighter`? Does it suffice for the predicate to hold for one
element of the type sets, or must it hold for all elements of the type sets?

We can use instantiation to answer some of these questions. In particular, by
instantiating both `Pair` and `LeftRighter` with the type parameters of `Pair`,
we can determine if, for all type arguments `[X, Y]` that are valid for `Pair`,
`[X, Y]` are also valid type arguments of `LeftRighter`, and `Pair[X, Y]` is
assignable to `LeftRighter[X, Y]`. The `typeparams.GenericAssignableTo`
function implements exactly this predicate:

```
func GenericPredicates(pkg *types.Package) {
	var (
		Pair        = pkg.Scope().Lookup("Pair").Type()
		LeftRighter = pkg.Scope().Lookup("LeftRighter").Type()
	)
	fmt.Println("GenericAssignableTo(Pair, LeftRighter)", typeparams.GenericAssignableTo(nil, Pair, LeftRighter))
}
```

Output:

```
GenericAssignableTo(Pair, LeftRighter) true
```

# Updating tools while building at older Go versions

In the examples above, we can see how a lot of the new APIs integrate with
existing usage of `go/ast` or `go/types`. However, most tools still need to
build at older Go versions, and handling the new language constructs in-line
will break builds at older Go versions.

For this purpose, the `x/exp/typeparams` package provides functions and types
that proxy the new APIs (with stub implementations at older Go versions).

# Further help

If you're working on updating a tool to support generics, and need help, please
feel free to reach out for help in any of the following ways:
 - By mailing the [golang-tools](https://groups.google.com/g/golang-tools) mailing list.
 - Directly to me via email (`rfindley@google.com`).
 - For bugs, you can [file an issue](https://github.com/golang/go/issues/new/choose).