typeparams

package
v0.0.0-...-b00ddd9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 29, 2024 License: BSD-3-Clause Imports: 9 Imported by: 0

Documentation

Overview

Package typeparams contains common utilities for writing tools that interact with generic Go code, as introduced with Go 1.18. It supplements the standard library APIs. Notably, the StructuralTerms API computes a minimal representation of the structural restrictions on a type parameter.

An external version of these APIs is available in the golang.org/x/exp/typeparams module.

Index

Constants

This section is empty.

Variables

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

Functions

func CoreType

func CoreType(T types.Type) types.Type

CoreType returns the core type of T or nil if T does not have a core type.

See https://go.dev/ref/spec#Core_types for the definition of a core type.

func Deref

func Deref(t types.Type) types.Type

Deref returns the type of the variable pointed to by t, if t's core type is a pointer; otherwise it returns t.

Do not assume that Deref(T)==T implies T is not a pointer: consider "type T *T", for example.

TODO(adonovan): ideally this would live in typesinternal, but that creates an import cycle. Move there when we melt this package down.

func GenericAssignableTo

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

GenericAssignableTo is a generalization of types.AssignableTo that implements the following rule for uninstantiated generic types:

If V and T are generic named types, then V is considered assignable to T if, 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.

For example, consider the following type declarations:

type Interface[T any] interface {
	Accept(T)
}

type Container[T any] struct {
	Element T
}

func (c Container[T]) Accept(t T) { c.Element = t }

In this case, GenericAssignableTo reports that instantiations of Container are assignable to the corresponding instantiation of Interface.

func InterfaceTermSet

func InterfaceTermSet(iface *types.Interface) ([]*types.Term, error)

InterfaceTermSet computes the normalized terms for a constraint interface, returning an error if the term set cannot be computed or is empty. In the latter case, the error will be ErrEmptyTypeSet.

See the documentation of StructuralTerms for more information on normalization.

func IsTypeParam

func IsTypeParam(t types.Type) bool

IsTypeParam reports whether t is a type parameter (or an alias of one).

func MustDeref

func MustDeref(t types.Type) types.Type

MustDeref returns the type of the variable pointed to by t. It panics if t's core type is not a pointer.

TODO(adonovan): ideally this would live in typesinternal, but that creates an import cycle. Move there when we melt this package down.

func NormalTerms

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

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.

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 parameter P is ~int.

With interface embedding and unions, the specification of structural type restrictions may be arbitrarily complex. For example, consider the following:

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

type B interface{ int|string }

type C interface { ~string|~int }

type T[P interface{ A|B; C }] int

In this example, the structural type restriction of P is ~string|int: A|B expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, which when intersected with C (~string|~int) yields ~string|int.

NormalTerms computes these expansions and reductions, producing a "normalized" form of the embeddings. A structural restriction is normalized if it is a single union containing no interface terms, and is minimal in the sense that removing any term changes the set of types satisfying the constraint. It is left as a proof for the reader that, modulo sorting, there is exactly one such normalized form.

Because the minimal representation always takes this form, NormalTerms returns a slice of tilde terms corresponding to the terms of the union in the normalized structural restriction. An error is returned if the type is invalid, exceeds complexity bounds, or has an empty type set. In the latter case, NormalTerms returns ErrEmptyTypeSet.

NormalTerms makes no guarantees about the order of terms, except that it is deterministic.

func PackIndexExpr

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

PackIndexExpr returns an *ast.IndexExpr or *ast.IndexListExpr, depending on the cardinality of indices. Calling PackIndexExpr with len(indices) == 0 will panic.

func StructuralTerms

func StructuralTerms(tparam *types.TypeParam) ([]*types.Term, error)

StructuralTerms returns a slice of terms representing the normalized structural type restrictions of a type parameter, if any.

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 parameter P is ~int.

With interface embedding and unions, the specification of structural type restrictions may be arbitrarily complex. For example, consider the following:

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

type B interface{ int|string }

type C interface { ~string|~int }

type T[P interface{ A|B; C }] int

In this example, the structural type restriction of P is ~string|int: A|B expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, which when intersected with C (~string|~int) yields ~string|int.

StructuralTerms computes these expansions and reductions, producing a "normalized" form of the embeddings. A structural restriction is normalized if it is a single union containing no interface terms, and is minimal in the sense that removing any term changes the set of types satisfying the constraint. It is left as a proof for the reader that, modulo sorting, there is exactly one such normalized form.

Because the minimal representation always takes this form, StructuralTerms returns a slice of tilde terms corresponding to the terms of the union in the normalized structural restriction. An error is returned if the constraint interface is invalid, exceeds complexity bounds, or has an empty type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet.

StructuralTerms makes no guarantees about the order of terms, except that it is deterministic.

func UnionTermSet

func UnionTermSet(union *types.Union) ([]*types.Term, error)

UnionTermSet computes the normalized terms for a union, returning an error if the term set cannot be computed or is empty. In the latter case, the error will be ErrEmptyTypeSet.

See the documentation of StructuralTerms for more information on normalization.

func UnpackIndexExpr

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

UnpackIndexExpr extracts data from AST nodes that represent index expressions.

For an ast.IndexExpr, the resulting indices slice will contain exactly one index expression. For an ast.IndexListExpr (go1.18+), it may have a variable number of index expressions.

For nodes that don't represent index expressions, the first return value of UnpackIndexExpr will be nil.

Types

type Free

type Free struct {
	// contains filtered or unexported fields
}

Free is a memoization of the set of free type parameters within a type. It makes a sequence of calls to Free.Has for overlapping types more efficient. The zero value is ready for use.

NOTE: Adapted from go/types/infer.go. If it is later exported, factor.

func (*Free) Has

func (w *Free) Has(typ types.Type) (res bool)

Has reports whether the specified type has a free type parameter.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL