exp/typeparams: add a guide to the new APIs

Add a rough draft of a guide to the new go/ast and go/types APIs
introduced with Go 1.18, including explanation of how the
x/exp/typeparams package helps bridge gaps.

For golang/go#50447

Change-Id: I2a5f9a5f0801a71466a4faa3e42efca75b3c7d3c
Reviewed-on: https://go-review.googlesource.com/c/exp/+/388274
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/typeparams/example/README.md b/typeparams/example/README.md
new file mode 100644
index 0000000..afe5bfb
--- /dev/null
+++ b/typeparams/example/README.md
@@ -0,0 +1,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).
diff --git a/typeparams/example/findtypeparams/main.go b/typeparams/example/findtypeparams/main.go
new file mode 100644
index 0000000..fad6f0a
--- /dev/null
+++ b/typeparams/example/findtypeparams/main.go
@@ -0,0 +1,157 @@
+// 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 main
+
+import (
+	"fmt"
+	"go/ast"
+	"go/importer"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"log"
+)
+
+const hello = `
+//!+input
+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}
+}
+//!-input
+`
+
+//!+print
+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
+}
+
+//!-print
+
+/*
+//!+output
+> 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
+//!-output
+*/
+
+func main() {
+	// Parse one file.
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "hello.go", hello, 0)
+	if err != nil {
+		log.Fatal(err)
+	}
+	if err := PrintTypeParams(fset, f); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/typeparams/example/generic-go-types.md b/typeparams/example/generic-go-types.md
new file mode 100644
index 0000000..797dcd4
--- /dev/null
+++ b/typeparams/example/generic-go-types.md
@@ -0,0 +1,448 @@
+<!-- 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.
+
+%toc
+
+# 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`.
+
+%include findtypeparams/main.go input -
+
+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.
+
+%include findtypeparams/main.go print -
+
+This program produces the following output. Note that not every type spec has
+a scope.
+
+%include findtypeparams/main.go output -
+
+## 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:
+
+%include interfaces/main.go input -
+
+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:
+
+%include interfaces/main.go printsyntax -
+
+Output:
+
+%include interfaces/main.go outputsyntax -
+
+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.
+
+%include interfaces/main.go printtypes -
+
+Output:
+
+%include interfaces/main.go outputtypes -
+
+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:
+
+%include implicit/main.go input -
+
+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:
+
+%include implicit/main.go show -
+
+Output:
+
+%include implicit/main.go output -
+
+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:
+
+%include typesets/main.go input -
+
+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:
+
+%include typesets/main.go print -
+
+which outputs:
+
+%include typesets/main.go output -
+
+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:
+
+%include instantiation/main.go input -
+
+We can find instances by type-checking with the `types.Info.Instances` map
+initialized:
+
+%include instantiation/main.go check -
+
+Output:
+
+%include instantiation/main.go checkoutput -
+
+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.
+
+%include instantiation/main.go instantiate -
+
+Output:
+
+%include instantiation/main.go instantiateoutput -
+
+### 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:
+
+%include genericmethods/main.go input -
+
+Let's inspect the method sets of the types in this library:
+
+%include genericmethods/main.go printmethods -
+
+Output:
+
+%include genericmethods/main.go printoutput -
+
+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:
+
+%include genericmethods/main.go compareorigins -
+
+Output:
+
+%include genericmethods/main.go compareoutput -
+
+### 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:
+
+%include predicates/main.go ordinary -
+
+Output:
+
+%include predicates/main.go ordinaryoutput -
+
+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:
+
+%include predicates/main.go generic -
+
+Output:
+
+%include predicates/main.go genericoutput -
+
+# 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).
diff --git a/typeparams/example/genericmethods/main.go b/typeparams/example/genericmethods/main.go
new file mode 100644
index 0000000..7673691
--- /dev/null
+++ b/typeparams/example/genericmethods/main.go
@@ -0,0 +1,123 @@
+// Copyright 2022 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 main
+
+import (
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"log"
+
+	"golang.org/x/exp/typeparams"
+)
+
+const src = `
+//!+input
+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]
+//!-input
+`
+
+//!+printmethods
+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()
+}
+
+//!-printmethods
+
+/*
+//!+printoutput
+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() _
+
+//!-printoutput
+*/
+
+//!+compareorigins
+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)
+}
+
+//!-compareorigins
+
+/*
+//!+compareoutput
+Pair.Left == Pair[int, int].Left: false
+Pair.Left == OriginMethod(Pair[int, int].Left): true
+//!-compareoutput
+*/
+
+func main() {
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "hello.go", src, 0)
+	if err != nil {
+		log.Fatal(err)
+	}
+	conf := types.Config{}
+	pkg, err := conf.Check("p", fset, []*ast.File{f}, nil)
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println("=== PrintMethods ===")
+	PrintMethods(pkg)
+	fmt.Println("=== CompareOrigins ===")
+	CompareOrigins(pkg)
+}
diff --git a/typeparams/example/implicit/main.go b/typeparams/example/implicit/main.go
new file mode 100644
index 0000000..8894de5
--- /dev/null
+++ b/typeparams/example/implicit/main.go
@@ -0,0 +1,52 @@
+package main
+
+import (
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"log"
+)
+
+const src = `
+//!+input
+package p
+
+func Square[N ~int|~float64](n N) N {
+	return n*n
+}
+//!-input
+`
+
+//!+show
+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())
+}
+
+//!-show
+
+/*
+//!+output
+~int|~float64
+IsImplicit: true
+//!-output
+*/
+
+func main() {
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "p.go", src, 0)
+	if err != nil {
+		log.Fatal(err)
+	}
+	conf := types.Config{}
+	pkg, err := conf.Check("typesets", fset, []*ast.File{f}, nil)
+	if err != nil {
+		log.Fatal(err)
+	}
+	ShowImplicit(pkg)
+}
diff --git a/typeparams/example/instantiation/main.go b/typeparams/example/instantiation/main.go
new file mode 100644
index 0000000..66c7be2
--- /dev/null
+++ b/typeparams/example/instantiation/main.go
@@ -0,0 +1,159 @@
+// Copyright 2022 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 main
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"log"
+)
+
+const src = `
+//!+input
+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]
+//!-input
+`
+
+//!+check
+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
+}
+
+//!-check
+
+/*
+//!+checkoutput
+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]
+
+//!-checkoutput
+*/
+
+func FormatTypeList(list *types.TypeList) string {
+	var buf bytes.Buffer
+	buf.WriteRune('[')
+	for i := 0; i < list.Len(); i++ {
+		if i > 0 {
+			buf.WriteString(", ")
+		}
+		buf.WriteString(list.At(i).String())
+	}
+	buf.WriteRune(']')
+	return buf.String()
+}
+
+//!+instantiate
+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)
+}
+
+//!-instantiate
+
+/*
+//!+instantiateoutput
+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
+//!-instantiateoutput
+*/
+
+func main() {
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "hello.go", src, 0)
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println("=== CheckInstances ===")
+	pkg, err := CheckInstances(fset, f)
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println("=== Instantiate ===")
+	if err := Instantiate(pkg); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/typeparams/example/interfaces/main.go b/typeparams/example/interfaces/main.go
new file mode 100644
index 0000000..c19a210
--- /dev/null
+++ b/typeparams/example/interfaces/main.go
@@ -0,0 +1,139 @@
+// Copyright 2022 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 main
+
+import (
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"log"
+)
+
+const src = `
+//!+input
+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
+}
+//!-input
+`
+
+//!+printsyntax
+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)
+}
+
+//!-printsyntax
+
+/*
+//!+outputsyntax
+     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  }
+//!-outputsyntax
+*/
+
+//!+printtypes
+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())
+}
+
+//!-printtypes
+
+/*
+//!+outputtypes
+type hello.Numeric interface{~int|~float64}
+        embeded: ~int|~float64
+        IsComparable(): true
+        IsMethodSet(): false
+type hello.Findable interface{comparable}
+        embeded: comparable
+        IsComparable(): true
+        IsMethodSet(): false
+//!-outputtypes
+*/
+
+func main() {
+	// Parse one file.
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "p.go", src, 0)
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println("=== PrintNumericSyntax ==")
+	PrintNumericSyntax(fset, f)
+	fmt.Println("=== PrintInterfaceTypes ==")
+	if err := PrintInterfaceTypes(fset, f); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/typeparams/example/methoddecls/main.go b/typeparams/example/methoddecls/main.go
new file mode 100644
index 0000000..00779c1
--- /dev/null
+++ b/typeparams/example/methoddecls/main.go
@@ -0,0 +1,138 @@
+// Copyright 2022 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 main
+
+import (
+	"fmt"
+	"go/ast"
+	"go/importer"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"log"
+)
+
+const methods = `
+//!+input
+package methods
+
+type List[E any] []E
+
+func (l List[_]) Len() int {
+	return len(l)
+}
+
+func (l *List[E]) Append(v E) {
+	*l = append(*l, v)
+}
+
+type Pair[L, R comparable] struct {
+	left  L
+	right R
+}
+
+func (p Pair[L, _]) Left() L {
+	return p.left
+}
+
+func (p Pair[_, R]) Right() R {
+	return p.right
+}
+
+func (p Pair[L, R]) Equal(other Pair[L, R]) bool {
+	return p.Left() == other.Left() && p.Right() == other.Right()
+}
+//!-input
+`
+
+//!+describe
+func Describe(fset *token.FileSet, file *ast.File) error {
+	conf := types.Config{Importer: importer.Default()}
+	info := &types.Info{
+		Defs: make(map[*ast.Ident]types.Object),
+	}
+	pkg, err := conf.Check("pair", fset, []*ast.File{file}, info)
+	if err != nil {
+		return err
+	}
+	for _, name := range pkg.Scope().Names() {
+		obj := pkg.Scope().Lookup(name)
+		typ := obj.Type().(*types.Named)
+
+		fmt.Printf("type %s has %d methods\n", name, typ.NumMethods())
+		for i := 0; i < typ.NumMethods(); i++ {
+			m := typ.Method(i)
+			sig := m.Type().(*types.Signature)
+			recv := sig.Recv().Type()
+			fmt.Printf("  %s has %d receiver type parameters\n", m.Name(), sig.RecvTypeParams().Len())
+			fmt.Printf("  ...and receiver type %+v\n", recv)
+		}
+	}
+	// }
+
+	// func foo() {}
+	for _, decl := range file.Decls {
+		fdecl, ok := decl.(*ast.FuncDecl)
+		if !ok {
+			continue
+		}
+		fmt.Printf("Declaration of %q has receiver node type %T\n", fdecl.Name, fdecl.Recv.List[0].Type)
+		declObj := info.Defs[fdecl.Name]
+		recvIdent := receiverTypeName(fdecl.Recv.List[0].Type)
+		typ := pkg.Scope().Lookup(recvIdent.Name).Type().(*types.Named)
+		// ptr := types.NewPointer(typ)
+		name := fdecl.Name.Name
+		methodObj, _, _ := types.LookupFieldOrMethod(typ, false, pkg, name)
+		if declObj == methodObj {
+			fmt.Printf("  info.Uses[%s] == types.LookupFieldOrMethod(%s, ..., %q)\n", fdecl.Name, typ, name)
+		}
+	}
+	return nil
+}
+
+func receiverTypeName(e ast.Expr) *ast.Ident {
+	if s, ok := e.(*ast.StarExpr); ok {
+		e = s.X
+	}
+	switch e := e.(type) {
+	case *ast.IndexExpr:
+		return e.X.(*ast.Ident)
+	case *ast.IndexListExpr:
+		return e.X.(*ast.Ident)
+	}
+	panic("unexpected receiver node type")
+}
+
+//!-describe
+
+/*
+//!+output
+> go run golang.org/x/tools/internal/typeparams/example/methoddecls
+Pair has 2 methods
+  Left has 2 receiver type parameters
+  ...and receiver type pair.Pair[L, _]
+  Right has 2 receiver type parameters
+  ...and receiver type pair.Pair[_, R]
+Declaration of "Left" has receiver node type *ast.IndexListExpr
+  info.Uses[Left] == types.LookupFieldOrMethod(pair.Pair[L, R any], ..., "Left")
+Declaration of "Right" has receiver node type *ast.IndexListExpr
+  info.Uses[Right] == types.LookupFieldOrMethod(pair.Pair[L, R any], ..., "Right")
+//!-output
+*/
+
+func main() {
+	// Parse one file.
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "methods.go", methods, 0)
+	if err != nil {
+		log.Fatal(err)
+	}
+	if err := Describe(fset, f); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/typeparams/example/predicates/main.go b/typeparams/example/predicates/main.go
new file mode 100644
index 0000000..c22b4a0
--- /dev/null
+++ b/typeparams/example/predicates/main.go
@@ -0,0 +1,114 @@
+// Copyright 2022 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 main
+
+import (
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"log"
+
+	"golang.org/x/exp/typeparams"
+)
+
+const src = `
+//!+input
+package p
+
+type Pair[L, R comparable] struct {
+	left  L
+	right R
+}
+
+func (p Pair[L, _]) Left() L {
+	return p.left
+}
+
+func (p Pair[_, R]) Right() R {
+	return p.right
+}
+
+// M does not use type parameters, and therefore implements the Mer interface.
+func (p Pair[_, _]) M() int { return 0 }
+
+type LeftRighter[L, R comparable] interface {
+	Left() L
+	Right() R
+}
+
+type Mer interface {
+	M() int
+}
+
+// F and G have identical signatures "modulo renaming", H does not.
+func F[P any](P) int { return 0 }
+func G[Q any](Q) int { return 1 }
+func H[R ~int](R) int { return 2 }
+//!-input
+`
+
+//!+ordinary
+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))
+}
+
+//!-ordinary
+
+/*
+//!+ordinaryoutput
+AssignableTo(Pair, LeftRighter) false
+AssignableTo(Pair, Mer):  true
+Identical(F, G) true
+Identical(F, H) false
+//!-ordinaryoutput
+*/
+
+//!+generic
+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))
+}
+
+//!-generic
+
+/*
+//!+genericoutput
+GenericAssignableTo(Pair, LeftRighter) true
+//!-genericoutput
+*/
+
+func main() {
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "hello.go", src, 0)
+	if err != nil {
+		log.Fatal(err)
+	}
+	conf := types.Config{}
+	pkg, err := conf.Check("p", fset, []*ast.File{f}, nil)
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println("=== ordinary ===")
+	OrdinaryPredicates(pkg)
+	fmt.Println("=== generic ===")
+	GenericPredicates(pkg)
+}
diff --git a/typeparams/example/typesets/main.go b/typeparams/example/typesets/main.go
new file mode 100644
index 0000000..b26dbc7
--- /dev/null
+++ b/typeparams/example/typesets/main.go
@@ -0,0 +1,71 @@
+// Copyright 2022 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 main
+
+import (
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"log"
+
+	"golang.org/x/exp/typeparams"
+)
+
+const src = `
+//!+input
+package complex
+
+type A interface{ ~string|~[]byte }
+
+type B interface{ int|string }
+
+type C interface { ~string|~int }
+
+type D interface{ A|B; C }
+//!-input
+`
+
+//!+print
+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
+}
+
+//!-print
+
+/*
+//!+output
+~string|int
+//!-output
+*/
+
+func main() {
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "p.go", src, 0)
+	if err != nil {
+		log.Fatal(err)
+	}
+	conf := types.Config{}
+	pkg, err := conf.Check("typesets", fset, []*ast.File{f}, nil)
+	if err != nil {
+		log.Fatal(err)
+	}
+	if err := PrintNormalTerms(pkg); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/typeparams/normalize.go b/typeparams/normalize.go
index 55c788a..ffb4e2b 100644
--- a/typeparams/normalize.go
+++ b/typeparams/normalize.go
@@ -21,15 +21,15 @@
 // NormalTerms returns a slice of terms representing the normalized structural
 // type restrictions of a type, if any.
 //
-// For all types other than *types.TypeParam, *types.Interface, and
-// *types.Union, this is just a single term with Tilde() == false and
-// Type() == typ. For *types.TypeParam, *types.Interface, and *types.Union, see
-// below.
+// For all types whose underlying type is not *types.TypeParam,
+// *types.Interface, or *types.Union, this is just a single term with Tilde()
+// == false and Type() == typ. For types whose underlying type is
+// *types.TypeParam, *types.Interface, and *types.Union, see below.
 //
 // Structural type restrictions of a type parameter are created via
 // non-interface types embedded in its constraint interface (directly, or via a
-// chain of interface embeddings). For example, in the declaration type
-// T[P interface{~int; m()}] int the structural restriction of the type
+// chain of interface embeddings). For example, in the declaration type T[P
+// interface{~int; m()}] int is the structural restriction of the type
 // parameter P is ~int.
 //
 // With interface embedding and unions, the specification of structural type