github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/typeparams/common.go (about)

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package typeparams contains common utilities for writing tools that interact
     6  // with generic Go code, as introduced with Go 1.18.
     7  //
     8  // Many of the types and functions in this package are proxies for the new APIs
     9  // introduced in the standard library with Go 1.18. For example, the
    10  // typeparams.Union type is an alias for go/types.Union, and the ForTypeSpec
    11  // function returns the value of the go/ast.TypeSpec.TypeParams field. At Go
    12  // versions older than 1.18 these helpers are implemented as stubs, allowing
    13  // users of this package to write code that handles generic constructs inline,
    14  // even if the Go version being used to compile does not support generics.
    15  //
    16  // Additionally, this package contains common utilities for working with the
    17  // new generic constructs, to supplement the standard library APIs. Notably,
    18  // the StructuralTerms API computes a minimal representation of the structural
    19  // restrictions on a type parameter. In the future, this API may be available
    20  // from go/types.
    21  //
    22  // See the example/README.md for a more detailed guide on how to update tools
    23  // to support generics.
    24  package typeparams
    25  
    26  import (
    27  	"go/ast"
    28  	"go/token"
    29  	"go/types"
    30  )
    31  
    32  // UnpackIndexExpr extracts data from AST nodes that represent index
    33  // expressions.
    34  //
    35  // For an ast.IndexExpr, the resulting indices slice will contain exactly one
    36  // index expression. For an ast.IndexListExpr (go1.18+), it may have a variable
    37  // number of index expressions.
    38  //
    39  // For nodes that don't represent index expressions, the first return value of
    40  // UnpackIndexExpr will be nil.
    41  func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) {
    42  	switch e := n.(type) {
    43  	case *ast.IndexExpr:
    44  		return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack
    45  	case *IndexListExpr:
    46  		return e.X, e.Lbrack, e.Indices, e.Rbrack
    47  	}
    48  	return nil, token.NoPos, nil, token.NoPos
    49  }
    50  
    51  // PackIndexExpr returns an *ast.IndexExpr or *ast.IndexListExpr, depending on
    52  // the cardinality of indices. Calling PackIndexExpr with len(indices) == 0
    53  // will panic.
    54  func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) ast.Expr {
    55  	switch len(indices) {
    56  	case 0:
    57  		panic("empty indices")
    58  	case 1:
    59  		return &ast.IndexExpr{
    60  			X:      x,
    61  			Lbrack: lbrack,
    62  			Index:  indices[0],
    63  			Rbrack: rbrack,
    64  		}
    65  	default:
    66  		return &IndexListExpr{
    67  			X:       x,
    68  			Lbrack:  lbrack,
    69  			Indices: indices,
    70  			Rbrack:  rbrack,
    71  		}
    72  	}
    73  }
    74  
    75  // IsTypeParam reports whether t is a type parameter.
    76  func IsTypeParam(t types.Type) bool {
    77  	_, ok := t.(*TypeParam)
    78  	return ok
    79  }
    80  
    81  // OriginMethod returns the origin method associated with the method fn.
    82  // For methods on a non-generic receiver base type, this is just
    83  // fn. However, for methods with a generic receiver, OriginMethod returns the
    84  // corresponding method in the method set of the origin type.
    85  //
    86  // As a special case, if fn is not a method (has no receiver), OriginMethod
    87  // returns fn.
    88  func OriginMethod(fn *types.Func) *types.Func {
    89  	recv := fn.Type().(*types.Signature).Recv()
    90  	if recv == nil {
    91  
    92  		return fn
    93  	}
    94  	base := recv.Type()
    95  	p, isPtr := base.(*types.Pointer)
    96  	if isPtr {
    97  		base = p.Elem()
    98  	}
    99  	named, isNamed := base.(*types.Named)
   100  	if !isNamed {
   101  		// Receiver is a *types.Interface.
   102  		return fn
   103  	}
   104  	if ForNamed(named).Len() == 0 {
   105  		// Receiver base has no type parameters, so we can avoid the lookup below.
   106  		return fn
   107  	}
   108  	orig := NamedTypeOrigin(named)
   109  	gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name())
   110  	return gfn.(*types.Func)
   111  }
   112  
   113  // GenericAssignableTo is a generalization of types.AssignableTo that
   114  // implements the following rule for uninstantiated generic types:
   115  //
   116  // If V and T are generic named types, then V is considered assignable to T if,
   117  // for every possible instantation of V[A_1, ..., A_N], the instantiation
   118  // T[A_1, ..., A_N] is valid and V[A_1, ..., A_N] implements T[A_1, ..., A_N].
   119  //
   120  // If T has structural constraints, they must be satisfied by V.
   121  //
   122  // For example, consider the following type declarations:
   123  //
   124  //  type Interface[T any] interface {
   125  //  	Accept(T)
   126  //  }
   127  //
   128  //  type Container[T any] struct {
   129  //  	Element T
   130  //  }
   131  //
   132  //  func (c Container[T]) Accept(t T) { c.Element = t }
   133  //
   134  // In this case, GenericAssignableTo reports that instantiations of Container
   135  // are assignable to the corresponding instantiation of Interface.
   136  func GenericAssignableTo(ctxt *Context, V, T types.Type) bool {
   137  	// If V and T are not both named, or do not have matching non-empty type
   138  	// parameter lists, fall back on types.AssignableTo.
   139  
   140  	VN, Vnamed := V.(*types.Named)
   141  	TN, Tnamed := T.(*types.Named)
   142  	if !Vnamed || !Tnamed {
   143  		return types.AssignableTo(V, T)
   144  	}
   145  
   146  	vtparams := ForNamed(VN)
   147  	ttparams := ForNamed(TN)
   148  	if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || NamedTypeArgs(VN).Len() != 0 || NamedTypeArgs(TN).Len() != 0 {
   149  		return types.AssignableTo(V, T)
   150  	}
   151  
   152  	// V and T have the same (non-zero) number of type params. Instantiate both
   153  	// with the type parameters of V. This must always succeed for V, and will
   154  	// succeed for T if and only if the type set of each type parameter of V is a
   155  	// subset of the type set of the corresponding type parameter of T, meaning
   156  	// that every instantiation of V corresponds to a valid instantiation of T.
   157  
   158  	// Minor optimization: ensure we share a context across the two
   159  	// instantiations below.
   160  	if ctxt == nil {
   161  		ctxt = NewContext()
   162  	}
   163  
   164  	var targs []types.Type
   165  	for i := 0; i < vtparams.Len(); i++ {
   166  		targs = append(targs, vtparams.At(i))
   167  	}
   168  
   169  	vinst, err := Instantiate(ctxt, V, targs, true)
   170  	if err != nil {
   171  		panic("type parameters should satisfy their own constraints")
   172  	}
   173  
   174  	tinst, err := Instantiate(ctxt, T, targs, true)
   175  	if err != nil {
   176  		return false
   177  	}
   178  
   179  	return types.AssignableTo(vinst, tinst)
   180  }