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

View as plain text