Source file src/cmd/compile/internal/types2/instantiate.go

     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  // This file implements instantiation of generic types
     6  // through substitution of type parameters by type arguments.
     7  
     8  package types2
     9  
    10  import (
    11  	"cmd/compile/internal/syntax"
    12  	"errors"
    13  	"fmt"
    14  	. "internal/types/errors"
    15  )
    16  
    17  // A genericType implements access to its type parameters.
    18  type genericType interface {
    19  	Type
    20  	TypeParams() *TypeParamList
    21  }
    22  
    23  // Instantiate instantiates the type orig with the given type arguments targs.
    24  // orig must be a generic *Alias, *Named, or *Signature type. If there is no error,
    25  // the resulting Type is an instantiated type of the same kind (*Alias, *Named
    26  // or *Signature, respectively).
    27  //
    28  // Methods attached to a *Named type are also instantiated, and associated with
    29  // a new *Func that has the same position as the original method, but nil function
    30  // scope.
    31  //
    32  // If ctxt is non-nil, it may be used to de-duplicate the instance against
    33  // previous instances with the same identity. As a special case, generic
    34  // *Signature origin types are only considered identical if they are pointer
    35  // equivalent, so that instantiating distinct (but possibly identical)
    36  // signatures will yield different instances. The use of a shared context does
    37  // not guarantee that identical instances are deduplicated in all cases.
    38  //
    39  // If validate is set, Instantiate verifies that the type orig is in fact generic,
    40  // that the number of type arguments and parameters match, and that the type arguments
    41  // satisfy their respective type constraints.
    42  // If verification fails, the resulting error may wrap an *ArgumentError indicating
    43  // which type argument did not satisfy its type parameter constraint, and why.
    44  //
    45  // If validate is not set, Instantiate does not check if orig is generic, verify the
    46  // type argument count, or check whether the type arguments satisfy their constraints.
    47  // Instantiate is guaranteed to not return an error, but may panic. Specifically,
    48  // for *Signature types, Instantiate will panic immediately if the type argument
    49  // count is incorrect; for *Named types, a panic may occur later inside the
    50  // *Named API.
    51  func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) {
    52  	if ctxt == nil {
    53  		ctxt = NewContext()
    54  	}
    55  	orig_, ok := orig.(genericType) // signature of Instantiate must not change for backward-compatibility
    56  	if !ok {
    57  		panic(sprintf(nil, false, "cannot instantiate non-generic %s: expected *Named, *Alias, or *Signature", orig))
    58  	}
    59  	if len(targs) == 0 {
    60  		panic(sprintf(nil, false, "cannot instantiate %s: empty type argument list", orig))
    61  	}
    62  
    63  	if validate {
    64  		tparams := orig_.TypeParams().list()
    65  		if len(tparams) == 0 {
    66  			return nil, fmt.Errorf("cannot instantiate non-generic %s: has no type parameters", orig)
    67  		}
    68  		if len(targs) != len(tparams) {
    69  			return nil, fmt.Errorf("cannot instantiate %s: got %d type arguments but have %d type parameters", orig, len(targs), len(tparams))
    70  		}
    71  		if i, err := (*Checker)(nil).verify(nopos, tparams, targs, ctxt); err != nil {
    72  			return nil, &ArgumentError{i, err}
    73  		}
    74  	}
    75  
    76  	inst := (*Checker)(nil).instance(nopos, orig_, targs, nil, ctxt)
    77  	return inst, nil
    78  }
    79  
    80  // instance instantiates the given original (generic) function or type with the
    81  // provided type arguments and returns the resulting instance. If an identical
    82  // instance exists already in the given contexts, it returns that instance,
    83  // otherwise it creates a new one. If there is an error (such as wrong number
    84  // of type arguments), the result is Typ[Invalid].
    85  //
    86  // If expanding is non-nil, it is the Named instance type currently being
    87  // expanded. If ctxt is non-nil, it is the context associated with the current
    88  // type-checking pass or call to Instantiate. At least one of expanding or ctxt
    89  // must be non-nil.
    90  //
    91  // For Named types the resulting instance may be unexpanded.
    92  //
    93  // check may be nil (when not type-checking syntax); pos is used only if check is non-nil.
    94  func (check *Checker) instance(pos syntax.Pos, orig genericType, targs []Type, expanding *Named, ctxt *Context) (res Type) {
    95  	// The order of the contexts below matters: we always prefer instances in the
    96  	// expanding instance context in order to preserve reference cycles.
    97  	//
    98  	// Invariant: if expanding != nil, the returned instance will be the instance
    99  	// recorded in expanding.inst.ctxt.
   100  	var ctxts []*Context
   101  	if expanding != nil {
   102  		ctxts = append(ctxts, expanding.inst.ctxt)
   103  	}
   104  	if ctxt != nil {
   105  		ctxts = append(ctxts, ctxt)
   106  	}
   107  	assert(len(ctxts) > 0)
   108  
   109  	// Compute all hashes; hashes may differ across contexts due to different
   110  	// unique IDs for Named types within the hasher.
   111  	hashes := make([]string, len(ctxts))
   112  	for i, ctxt := range ctxts {
   113  		hashes[i] = ctxt.instanceHash(orig, targs)
   114  	}
   115  
   116  	// Record the result in all contexts.
   117  	// Prefer to re-use existing types from expanding context, if it exists, to reduce
   118  	// the memory pinned by the Named type.
   119  	updateContexts := func(res Type) Type {
   120  		for i := len(ctxts) - 1; i >= 0; i-- {
   121  			res = ctxts[i].update(hashes[i], orig, targs, res)
   122  		}
   123  		return res
   124  	}
   125  
   126  	// typ may already have been instantiated with identical type arguments. In
   127  	// that case, re-use the existing instance.
   128  	for i, ctxt := range ctxts {
   129  		if inst := ctxt.lookup(hashes[i], orig, targs); inst != nil {
   130  			return updateContexts(inst)
   131  		}
   132  	}
   133  
   134  	switch orig := orig.(type) {
   135  	case *Named:
   136  		res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily
   137  
   138  	case *Alias:
   139  		// verify type parameter count (see go.dev/issue/71198 for a test case)
   140  		tparams := orig.TypeParams()
   141  		if !check.validateTArgLen(pos, orig.obj.Name(), tparams.Len(), len(targs)) {
   142  			// TODO(gri) Consider returning a valid alias instance with invalid
   143  			//           underlying (aliased) type to match behavior of *Named
   144  			//           types. Then this function will never return an invalid
   145  			//           result.
   146  			return Typ[Invalid]
   147  		}
   148  		if tparams.Len() == 0 {
   149  			return orig // nothing to do (minor optimization)
   150  		}
   151  
   152  		res = check.newAliasInstance(pos, orig, targs, expanding, ctxt)
   153  
   154  	case *Signature:
   155  		assert(expanding == nil) // function instances cannot be reached from Named types
   156  		// Note that orig may be a generic method on a generic type. In that case, orig
   157  		// is an instantiated type. It will not have receiver type parameters, but will
   158  		// still have ordinary type parameters.
   159  		assert(orig.RecvTypeParams() == nil)
   160  		assert(orig.TypeParams() != nil)
   161  
   162  		tparams := orig.TypeParams()
   163  		// TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here)
   164  		if !check.validateTArgLen(pos, orig.String(), tparams.Len(), len(targs)) {
   165  			return Typ[Invalid]
   166  		}
   167  		if tparams.Len() == 0 {
   168  			return orig // nothing to do (minor optimization)
   169  		}
   170  		sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), nil, ctxt).(*Signature)
   171  		// If the signature doesn't use its type parameters, subst
   172  		// will not make a copy. In that case, make a copy now (so
   173  		// we can set tparams to nil w/o causing side-effects).
   174  		if sig == orig {
   175  			copy := *sig
   176  			sig = &copy
   177  		}
   178  		// After instantiating a generic signature, it is not generic
   179  		// anymore; we need to set tparams to nil.
   180  		sig.tparams = nil
   181  		res = sig
   182  
   183  	default:
   184  		// only types and functions can be generic
   185  		panic(fmt.Sprintf("%v: cannot instantiate %v", pos, orig))
   186  	}
   187  
   188  	// Update all contexts; it's possible that we've lost a race.
   189  	return updateContexts(res)
   190  }
   191  
   192  // validateTArgLen checks that the number of type arguments (got) matches the
   193  // number of type parameters (want); if they don't match an error is reported.
   194  // If validation fails and check is nil, validateTArgLen panics.
   195  func (check *Checker) validateTArgLen(pos syntax.Pos, name string, want, got int) bool {
   196  	var qual string
   197  	switch {
   198  	case got < want:
   199  		qual = "not enough"
   200  	case got > want:
   201  		qual = "too many"
   202  	default:
   203  		return true
   204  	}
   205  
   206  	msg := check.sprintf("%s type arguments for type %s: have %d, want %d", qual, name, got, want)
   207  	if check != nil {
   208  		check.error(atPos(pos), WrongTypeArgCount, msg)
   209  		return false
   210  	}
   211  
   212  	panic(fmt.Sprintf("%v: %s", pos, msg))
   213  }
   214  
   215  // check may be nil; pos is used only if check is non-nil.
   216  func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type, ctxt *Context) (int, error) {
   217  	smap := makeSubstMap(tparams, targs)
   218  	for i, tpar := range tparams {
   219  		// Ensure that we have a (possibly implicit) interface as type bound (go.dev/issue/51048).
   220  		tpar.iface()
   221  		// The type parameter bound is parameterized with the same type parameters
   222  		// as the instantiated type; before we can use it for bounds checking we
   223  		// need to instantiate it with the type arguments with which we instantiated
   224  		// the parameterized type.
   225  		bound := check.subst(pos, tpar.bound, smap, nil, ctxt)
   226  		var cause string
   227  		if !check.implements(targs[i], bound, true, &cause) {
   228  			return i, errors.New(cause)
   229  		}
   230  	}
   231  	return -1, nil
   232  }
   233  
   234  // implements checks if V implements T. The receiver may be nil if implements
   235  // is called through an exported API call such as AssignableTo. If constraint
   236  // is set, T is a type constraint.
   237  //
   238  // If the provided cause is non-nil, it may be set to an error string
   239  // explaining why V does not implement (or satisfy, for constraints) T.
   240  func (check *Checker) implements(V, T Type, constraint bool, cause *string) bool {
   241  	Vu := V.Underlying()
   242  	Tu := T.Underlying()
   243  	if !isValid(Vu) || !isValid(Tu) {
   244  		return true // avoid follow-on errors
   245  	}
   246  	if p, _ := Vu.(*Pointer); p != nil && !isValid(p.base.Underlying()) {
   247  		return true // avoid follow-on errors (see go.dev/issue/49541 for an example)
   248  	}
   249  
   250  	verb := "implement"
   251  	if constraint {
   252  		verb = "satisfy"
   253  	}
   254  
   255  	Ti, _ := Tu.(*Interface)
   256  	if Ti == nil {
   257  		if cause != nil {
   258  			var detail string
   259  			if isInterfacePtr(Tu) {
   260  				detail = check.interfacePtrError(T)
   261  			} else {
   262  				detail = check.sprintf("%s is not an interface", T)
   263  			}
   264  			*cause = check.sprintf("%s does not %s %s (%s)", V, verb, T, detail)
   265  		}
   266  		return false
   267  	}
   268  
   269  	// Every type satisfies the empty interface.
   270  	if Ti.Empty() {
   271  		return true
   272  	}
   273  	// T is not the empty interface (i.e., the type set of T is restricted)
   274  
   275  	// An interface V with an empty type set satisfies any interface.
   276  	// (The empty set is a subset of any set.)
   277  	Vi, _ := Vu.(*Interface)
   278  	if Vi != nil && Vi.typeSet().IsEmpty() {
   279  		return true
   280  	}
   281  	// type set of V is not empty
   282  
   283  	// No type with non-empty type set satisfies the empty type set.
   284  	if Ti.typeSet().IsEmpty() {
   285  		if cause != nil {
   286  			*cause = check.sprintf("cannot %s %s (empty type set)", verb, T)
   287  		}
   288  		return false
   289  	}
   290  
   291  	// V must implement T's methods, if any.
   292  	if !check.hasAllMethods(V, T, true, Identical, cause) /* !Implements(V, T) */ {
   293  		if cause != nil {
   294  			*cause = check.sprintf("%s does not %s %s %s", V, verb, T, *cause)
   295  		}
   296  		return false
   297  	}
   298  
   299  	// Only check comparability if we don't have a more specific error.
   300  	checkComparability := func() bool {
   301  		if !Ti.IsComparable() {
   302  			return true
   303  		}
   304  		// If T is comparable, V must be comparable.
   305  		// If V is strictly comparable, we're done.
   306  		if comparableType(V, false /* strict comparability */, nil) == nil {
   307  			return true
   308  		}
   309  		// For constraint satisfaction, use dynamic (spec) comparability
   310  		// so that ordinary, non-type parameter interfaces implement comparable.
   311  		if constraint && comparableType(V, true /* spec comparability */, nil) == nil {
   312  			// V is comparable if we are at Go 1.20 or higher.
   313  			if check == nil || check.allowVersion(go1_20) {
   314  				return true
   315  			}
   316  			if cause != nil {
   317  				*cause = check.sprintf("%s to %s comparable requires go1.20 or later", V, verb)
   318  			}
   319  			return false
   320  		}
   321  		if cause != nil {
   322  			*cause = check.sprintf("%s does not %s comparable", V, verb)
   323  		}
   324  		return false
   325  	}
   326  
   327  	// V must also be in the set of types of T, if any.
   328  	// Constraints with empty type sets were already excluded above.
   329  	if !Ti.typeSet().hasTerms() {
   330  		return checkComparability() // nothing to do
   331  	}
   332  
   333  	// If V is itself an interface, each of its possible types must be in the set
   334  	// of T types (i.e., the V type set must be a subset of the T type set).
   335  	// Interfaces V with empty type sets were already excluded above.
   336  	if Vi != nil {
   337  		if !Vi.typeSet().subsetOf(Ti.typeSet()) {
   338  			// TODO(gri) report which type is missing
   339  			if cause != nil {
   340  				*cause = check.sprintf("%s does not %s %s", V, verb, T)
   341  			}
   342  			return false
   343  		}
   344  		return checkComparability()
   345  	}
   346  
   347  	// Otherwise, V's type must be included in the iface type set.
   348  	var alt Type
   349  	if Ti.typeSet().is(func(t *term) bool {
   350  		if !t.includes(V) {
   351  			// If V ∉ t.typ but V ∈ ~t.typ then remember this type
   352  			// so we can suggest it as an alternative in the error
   353  			// message.
   354  			if alt == nil && !t.tilde && Identical(t.typ, t.typ.Underlying()) {
   355  				tt := *t
   356  				tt.tilde = true
   357  				if tt.includes(V) {
   358  					alt = t.typ
   359  				}
   360  			}
   361  			return true
   362  		}
   363  		return false
   364  	}) {
   365  		if cause != nil {
   366  			var detail string
   367  			switch {
   368  			case alt != nil:
   369  				detail = check.sprintf("possibly missing ~ for %s in %s", alt, T)
   370  			case mentions(Ti, V):
   371  				detail = check.sprintf("%s mentions %s, but %s is not in the type set of %s", T, V, V, T)
   372  			default:
   373  				detail = check.sprintf("%s missing in %s", V, Ti.typeSet().terms)
   374  			}
   375  			*cause = check.sprintf("%s does not %s %s (%s)", V, verb, T, detail)
   376  		}
   377  		return false
   378  	}
   379  
   380  	return checkComparability()
   381  }
   382  
   383  // mentions reports whether type T "mentions" typ in an (embedded) element or term
   384  // of T (whether typ is in the type set of T or not). For better error messages.
   385  func mentions(T, typ Type) bool {
   386  	switch T := T.(type) {
   387  	case *Interface:
   388  		for _, e := range T.embeddeds {
   389  			if mentions(e, typ) {
   390  				return true
   391  			}
   392  		}
   393  	case *Union:
   394  		for _, t := range T.terms {
   395  			if mentions(t.typ, typ) {
   396  				return true
   397  			}
   398  		}
   399  	default:
   400  		if Identical(T, typ) {
   401  			return true
   402  		}
   403  	}
   404  	return false
   405  }
   406  

View as plain text