or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

apidiff.mdconstraints.mdebnf.mderrors.mdevent.mdgorelease.mdindex.mdio-i2c.mdio-spi.mdjsonrpc2.mdmaps.mdmmap.mdmodgraphviz.mdrand.mdshiny.mdslices.mdslog.mdstats.mdsumdb.mdtrace.mdtxtar.mdtypeparams.mdutf8string.md
tile.json

typeparams.mddocs/

Type Parameters

The golang.org/x/exp/typeparams package provides common utilities for writing tools that interact with generic Go code, as introduced with Go 1.18. It offers proxy APIs for the new type parameter features in the standard library, along with supplemental utilities for working with generic constructs.

Package Information

  • Package Name: golang.org/x/exp/typeparams
  • Package Type: Go
  • Language: Go
  • Installation: go get golang.org/x/exp/typeparams

Core Imports

import (
	"golang.org/x/exp/typeparams"
	"go/ast"
	"go/types"
	"go/token"
)

Basic Usage

package main

import (
	"fmt"
	"golang.org/x/exp/typeparams"
	"go/ast"
	"go/types"
)

func main() {
	// Check if type parameters are enabled
	if typeparams.Enabled() {
		fmt.Println("Type parameters are supported")
	}

	// Create a type parameter with a constraint
	typeName := types.NewTypeName(0, nil, "T", nil)
	constraint := types.Universe.Lookup("any").Type()
	tp := typeparams.NewTypeParam(typeName, constraint)
	fmt.Printf("Type parameter: %v\n", tp)

	// Create a union type
	term1 := typeparams.NewTerm(false, types.Typ[types.Int])
	term2 := typeparams.NewTerm(false, types.Typ[types.String])
	union := typeparams.NewUnion([]*typeparams.Term{term1, term2})
	fmt.Printf("Union: %v\n", union)
}

Overview

This package bridges the gap between code that needs to work with generic Go constructs and varying Go versions. Many APIs are aliases or proxies for Go 1.18+ standard library features, but have stub implementations for older Go versions. Key capabilities include:

  • Extracting type parameters from AST nodes
  • Creating and manipulating generic types
  • Computing normalized structural restrictions on type parameters
  • Working with unions and structural constraints
  • Handling instantiation of generic types
  • Checking type parameter properties and constraints

Capabilities

Type Parameter Detection and Creation

Functions for checking whether a type is a type parameter and creating new type parameters.

func IsTypeParam(t types.Type) bool

Checks if a type is a type parameter.

func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam

Creates a new type parameter with the given name and constraint. This function calls types.NewTypeParam.

func Enabled() bool

Reports whether type parameters are enabled in the current build environment.

Extracting Type Parameters from AST

Functions for extracting type parameter information from AST nodes representing types and functions.

func ForTypeSpec(n *ast.TypeSpec) *ast.FieldList

Extracts type parameters from a type specification. Returns n.TypeParams.

func ForFuncType(n *ast.FuncType) *ast.FieldList

Extracts type parameters from a function type. Returns n.TypeParams.

Named Type Operations

Functions for working with named types and their type arguments.

func ForNamed(named *types.Named) *TypeParamList

Extracts the type parameter list from a named type. Returns the possibly empty type parameter object list from the named type.

func NamedTypeArgs(named *types.Named) *TypeList

Returns the type arguments of a named type. This function calls named.TypeArgs().

func NamedTypeOrigin(named *types.Named) types.Type

Returns the origin type of a named type. This function calls named.Orig().

func SetForNamed(n *types.Named, tparams []*TypeParam)

Sets the type parameters on a named type. Each tparam must be of dynamic type *types.TypeParam.

Signature and Method Operations

Functions for working with function signatures, their type parameters, and generic methods.

func ForSignature(sig *types.Signature) *TypeParamList

Returns the type parameters of a function signature. This function calls sig.TypeParams().

func RecvTypeParams(sig *types.Signature) *TypeParamList

Returns the receiver type parameters of a function signature. This function calls sig.RecvTypeParams().

func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature

Creates a new signature type with the given receiver, receiver type parameters, type parameters, parameters, results, and variadic flag. This function calls types.NewSignatureType.

func OriginMethod(fn *types.Func) *types.Func

Returns the origin method associated with a method function. For methods on non-generic receiver base types, this is just fn. For methods with a generic receiver, this returns the corresponding method in the method set of the origin type. As a special case, if fn is not a method (has no receiver), it returns fn.

Generic Type Instantiation

Functions for instantiating generic types with concrete type arguments.

func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error)

Instantiates a generic type with the provided type arguments. This function calls types.Instantiate.

Type Constraint Analysis

Functions for analyzing and normalizing type constraints and structural restrictions.

func NormalTerms(typ types.Type) ([]*Term, error)

Returns a slice of terms representing the normalized structural type restrictions of a type. For types whose underlying type is not *types.TypeParam, *types.Interface, or *types.Union, this returns a single term. The function normalizes complex constraints by expanding interface embeddings and unions, then computing their intersection.

Returns an error if the type is invalid, exceeds complexity bounds, or has an empty type set. In the latter case, returns ErrEmptyTypeSet.

func GenericAssignableTo(ctxt *Context, V, T types.Type) bool

A generalization of types.AssignableTo that handles uninstantiated generic types. V is considered assignable to T if:

  • V and T are both generic named types, and for every possible instantiation of V[A_1, ..., A_N], the instantiation T[A_1, ..., A_N] is valid and V[A_1, ..., A_N] implements T[A_1, ..., A_N].
  • If T has structural constraints, they must be satisfied by V.

Interface Operations

Functions for checking and modifying interface properties related to generic constraints.

func IsComparable(iface *types.Interface) bool

Checks if an interface is comparable. This function calls iface.IsComparable().

func IsImplicit(iface *types.Interface) bool

Checks if an interface is implicit. This function calls iface.IsImplicit().

func IsMethodSet(iface *types.Interface) bool

Checks if an interface is a method set. This function calls iface.IsMethodSet().

func MarkImplicit(iface *types.Interface)

Marks an interface as implicit. This function calls iface.MarkImplicit().

Instance Tracking

Functions for tracking and retrieving information about type and function instances.

func InitInstances(info *types.Info)

Initializes a types.Info structure to record information about type and function instances.

func GetInstances(info *types.Info) map[*ast.Ident]Instance

Returns the instances map from a types.Info structure. This function calls info.Instances.

AST Index Expression Handling

Functions for packing and unpacking index expressions in AST nodes.

func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) ast.Expr

Creates an AST index expression node. Returns an *ast.IndexExpr or *ast.IndexListExpr depending on the number of indices. Will panic if len(indices) == 0.

func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos)

Extracts data from AST nodes representing index expressions. For an ast.IndexExpr, the resulting indices slice contains exactly one index. For an ast.IndexListExpr (Go 1.18+), it may have a variable number of index expressions. For nodes that don't represent index expressions, the first return value is nil.

Context Creation

Functions for creating type checking contexts.

func NewContext() *Context

Creates a new type checking context. This function calls types.NewContext.

Types

Type Aliases

type Context = types.Context

Context is an alias for types.Context used in type checking operations.

type Instance = types.Instance

Instance is an alias for types.Instance representing a type or function instance.

type IndexListExpr = ast.IndexListExpr

IndexListExpr is an alias for ast.IndexListExpr representing a list of indices in an index expression.

type Term = types.Term

Term is an alias for types.Term representing a term in a union type constraint.

type TypeList = types.TypeList

TypeList is an alias for types.TypeList representing a list of types.

type TypeParam = types.TypeParam

TypeParam is an alias for types.TypeParam representing a type parameter.

type TypeParamList = types.TypeParamList

TypeParamList is an alias for types.TypeParamList representing a list of type parameters.

type Union = types.Union

Union is an alias for types.Union representing a union of type terms.

Term Operations

func NewTerm(tilde bool, typ types.Type) *Term

Creates a new term with an optional tilde. The tilde indicates a type constraint approximation. This function calls types.NewTerm.

Union Operations

func NewUnion(terms []*Term) *Union

Creates a new union type from a slice of terms. This function calls types.NewUnion.

Variables

var ErrEmptyTypeSet = errors.New("empty type set")

ErrEmptyTypeSet is returned if a type set computation results in a type set with no types. This error indicates that the structural constraints defined in a type parameter are contradictory and cannot be satisfied by any type.

Usage Examples

Working with Type Parameters in Generic Code

package main

import (
	"fmt"
	"golang.org/x/exp/typeparams"
	"go/ast"
	"go/parser"
	"go/types"
)

func analyzeGenericFunction(source string) error {
	// Parse the source code
	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset, "example.go", source, 0)
	if err != nil {
		return err
	}

	// For each function declaration
	for _, decl := range file.Decls {
		if fn, ok := decl.(*ast.FuncDecl); ok {
			// Extract type parameters from function
			tparams := typeparams.ForFuncType(fn.Type)
			if tparams != nil && len(tparams.List) > 0 {
				fmt.Printf("Function %s has type parameters\n", fn.Name.Name)
				for _, param := range tparams.List {
					fmt.Printf("  - %s\n", param.Names[0].Name)
				}
			}
		}
	}
	return nil
}

Computing Normalized Type Constraints

package main

import (
	"fmt"
	"golang.org/x/exp/typeparams"
	"go/types"
)

func analyzeConstraint(constraint types.Type) error {
	// Get the normalized terms representing the constraint
	terms, err := typeparams.NormalTerms(constraint)
	if err != nil {
		if err == typeparams.ErrEmptyTypeSet {
			fmt.Println("Constraint has an empty type set")
		}
		return err
	}

	fmt.Printf("Normalized constraint terms:\n")
	for _, term := range terms {
		if term.Tilde() {
			fmt.Printf("  ~%v\n", term.Type())
		} else {
			fmt.Printf("  %v\n", term.Type())
		}
	}
	return nil
}

Creating and Working with Unions

package main

import (
	"fmt"
	"golang.org/x/exp/typeparams"
	"go/types"
)

func createConstraintUnion() *typeparams.Union {
	// Create terms for int and string
	intTerm := typeparams.NewTerm(false, types.Typ[types.Int])
	strTerm := typeparams.NewTerm(false, types.Typ[types.String])

	// Create a union allowing int or string
	union := typeparams.NewUnion([]*typeparams.Term{intTerm, strTerm})
	fmt.Printf("Union created: %v\n", union)
	return union
}

Instantiating Generic Types

package main

import (
	"fmt"
	"golang.org/x/exp/typeparams"
	"go/types"
)

func instantiateGeneric() error {
	// Create a context for type checking
	ctxt := typeparams.NewContext()

	// Assume we have a generic type List[T]
	// and want to instantiate it with int
	typeArgs := []types.Type{types.Typ[types.Int]}

	// Note: In practice, you would extract the generic type from
	// parsed code or type information
	// inst, err := typeparams.Instantiate(ctxt, genericType, typeArgs, true)
	// if err != nil {
	//     return err
	// }
	// fmt.Printf("Instantiated type: %v\n", inst)

	return nil
}

Analyzing Method Receivers

package main

import (
	"fmt"
	"golang.org/x/exp/typeparams"
	"go/types"
)

func analyzeGenericMethod(method *types.Func) {
	sig := method.Type().(*types.Signature)

	// Check if method has receiver type parameters
	recvTypeParams := typeparams.RecvTypeParams(sig)
	fmt.Printf("Method %s receiver type parameters: %d\n",
		method.Name(), recvTypeParams.Len())

	// Check signature type parameters
	typeParams := typeparams.ForSignature(sig)
	fmt.Printf("Method %s type parameters: %d\n",
		method.Name(), typeParams.Len())

	// Get the origin method
	origin := typeparams.OriginMethod(method)
	fmt.Printf("Origin method: %s\n", origin.Name())
}

Working with Named Type Arguments

package main

import (
	"fmt"
	"golang.org/x/exp/typeparams"
	"go/types"
)

func analyzeNamedType(named *types.Named) {
	// Get type parameters
	tparams := typeparams.ForNamed(named)
	fmt.Printf("Named type %s has %d type parameters\n",
		named.String(), tparams.Len())

	// Get type arguments
	targs := typeparams.NamedTypeArgs(named)
	fmt.Printf("Named type %s has %d type arguments\n",
		named.String(), targs.Len())

	// Get origin type
	origin := typeparams.NamedTypeOrigin(named)
	fmt.Printf("Origin type: %v\n", origin)
}

Compatibility

The golang.org/x/exp/typeparams package is designed to work across different Go versions:

  • Go 1.18+: Full support for generics with direct use of standard library APIs
  • Go 1.17 and earlier: Stub implementations that allow code to compile but with limited functionality for generic constructs

This compatibility approach allows developers to write generic-aware tools that can be used across Go versions, with full functionality on Go 1.18+ and graceful degradation on earlier versions.