// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT. // Source: ../../cmd/compile/internal/types2/instantiate.go // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // This file implements instantiation of generic types // through substitution of type parameters by type arguments. package types import ( "errors" "fmt" "go/token" "internal/buildcfg" . "internal/types/errors" ) // A genericType implements access to its type parameters. type genericType interface { Type TypeParams() *TypeParamList } // Instantiate instantiates the type orig with the given type arguments targs. // orig must be an *Alias, *Named, or *Signature type. If there is no error, // the resulting Type is an instantiated type of the same kind (*Alias, *Named // or *Signature, respectively). // // Methods attached to a *Named type are also instantiated, and associated with // a new *Func that has the same position as the original method, but nil function // scope. // // If ctxt is non-nil, it may be used to de-duplicate the instance against // previous instances with the same identity. As a special case, generic // *Signature origin types are only considered identical if they are pointer // equivalent, so that instantiating distinct (but possibly identical) // signatures will yield different instances. The use of a shared context does // not guarantee that identical instances are deduplicated in all cases. // // If validate is set, Instantiate verifies that the number of type arguments // and parameters match, and that the type arguments satisfy their respective // type constraints. If verification fails, the resulting error may wrap an // *ArgumentError indicating which type argument did not satisfy its type parameter // constraint, and why. // // If validate is not set, Instantiate does not verify the type argument count // or whether the type arguments satisfy their constraints. Instantiate is // guaranteed to not return an error, but may panic. Specifically, for // *Signature types, Instantiate will panic immediately if the type argument // count is incorrect; for *Named types, a panic may occur later inside the // *Named API. func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) { assert(len(targs) > 0) if ctxt == nil { ctxt = NewContext() } orig_ := orig.(genericType) // signature of Instantiate must not change for backward-compatibility if validate { tparams := orig_.TypeParams().list() assert(len(tparams) > 0) if len(targs) != len(tparams) { return nil, fmt.Errorf("got %d type arguments but %s has %d type parameters", len(targs), orig, len(tparams)) } if i, err := (*Checker)(nil).verify(nopos, tparams, targs, ctxt); err != nil { return nil, &ArgumentError{i, err} } } inst := (*Checker)(nil).instance(nopos, orig_, targs, nil, ctxt) return inst, nil } // instance instantiates the given original (generic) function or type with the // provided type arguments and returns the resulting instance. If an identical // instance exists already in the given contexts, it returns that instance, // otherwise it creates a new one. // // If expanding is non-nil, it is the Named instance type currently being // expanded. If ctxt is non-nil, it is the context associated with the current // type-checking pass or call to Instantiate. At least one of expanding or ctxt // must be non-nil. // // For Named types the resulting instance may be unexpanded. func (check *Checker) instance(pos token.Pos, orig genericType, targs []Type, expanding *Named, ctxt *Context) (res Type) { // The order of the contexts below matters: we always prefer instances in the // expanding instance context in order to preserve reference cycles. // // Invariant: if expanding != nil, the returned instance will be the instance // recorded in expanding.inst.ctxt. var ctxts []*Context if expanding != nil { ctxts = append(ctxts, expanding.inst.ctxt) } if ctxt != nil { ctxts = append(ctxts, ctxt) } assert(len(ctxts) > 0) // Compute all hashes; hashes may differ across contexts due to different // unique IDs for Named types within the hasher. hashes := make([]string, len(ctxts)) for i, ctxt := range ctxts { hashes[i] = ctxt.instanceHash(orig, targs) } // Record the result in all contexts. // Prefer to re-use existing types from expanding context, if it exists, to reduce // the memory pinned by the Named type. updateContexts := func(res Type) Type { for i := len(ctxts) - 1; i >= 0; i-- { res = ctxts[i].update(hashes[i], orig, targs, res) } return res } // typ may already have been instantiated with identical type arguments. In // that case, re-use the existing instance. for i, ctxt := range ctxts { if inst := ctxt.lookup(hashes[i], orig, targs); inst != nil { return updateContexts(inst) } } switch orig := orig.(type) { case *Named: res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily case *Alias: if !buildcfg.Experiment.AliasTypeParams { assert(expanding == nil) // Alias instances cannot be reached from Named types } tparams := orig.TypeParams() // TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here) if !check.validateTArgLen(pos, orig.String(), tparams.Len(), len(targs)) { return Typ[Invalid] } if tparams.Len() == 0 { return orig // nothing to do (minor optimization) } return check.newAliasInstance(pos, orig, targs, expanding, ctxt) case *Signature: assert(expanding == nil) // function instances cannot be reached from Named types tparams := orig.TypeParams() // TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here) if !check.validateTArgLen(pos, orig.String(), tparams.Len(), len(targs)) { return Typ[Invalid] } if tparams.Len() == 0 { return orig // nothing to do (minor optimization) } sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), nil, ctxt).(*Signature) // If the signature doesn't use its type parameters, subst // will not make a copy. In that case, make a copy now (so // we can set tparams to nil w/o causing side-effects). if sig == orig { copy := *sig sig = © } // After instantiating a generic signature, it is not generic // anymore; we need to set tparams to nil. sig.tparams = nil res = sig default: // only types and functions can be generic panic(fmt.Sprintf("%v: cannot instantiate %v", pos, orig)) } // Update all contexts; it's possible that we've lost a race. return updateContexts(res) } // validateTArgLen checks that the number of type arguments (got) matches the // number of type parameters (want); if they don't match an error is reported. // If validation fails and check is nil, validateTArgLen panics. func (check *Checker) validateTArgLen(pos token.Pos, name string, want, got int) bool { var qual string switch { case got < want: qual = "not enough" case got > want: qual = "too many" default: return true } msg := check.sprintf("%s type arguments for type %s: have %d, want %d", qual, name, got, want) if check != nil { check.error(atPos(pos), WrongTypeArgCount, msg) return false } panic(fmt.Sprintf("%v: %s", pos, msg)) } func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type, ctxt *Context) (int, error) { smap := makeSubstMap(tparams, targs) for i, tpar := range tparams { // Ensure that we have a (possibly implicit) interface as type bound (go.dev/issue/51048). tpar.iface() // The type parameter bound is parameterized with the same type parameters // as the instantiated type; before we can use it for bounds checking we // need to instantiate it with the type arguments with which we instantiated // the parameterized type. bound := check.subst(pos, tpar.bound, smap, nil, ctxt) var cause string if !check.implements(pos, targs[i], bound, true, &cause) { return i, errors.New(cause) } } return -1, nil } // implements checks if V implements T. The receiver may be nil if implements // is called through an exported API call such as AssignableTo. If constraint // is set, T is a type constraint. // // If the provided cause is non-nil, it may be set to an error string // explaining why V does not implement (or satisfy, for constraints) T. func (check *Checker) implements(pos token.Pos, V, T Type, constraint bool, cause *string) bool { Vu := under(V) Tu := under(T) if !isValid(Vu) || !isValid(Tu) { return true // avoid follow-on errors } if p, _ := Vu.(*Pointer); p != nil && !isValid(under(p.base)) { return true // avoid follow-on errors (see go.dev/issue/49541 for an example) } verb := "implement" if constraint { verb = "satisfy" } Ti, _ := Tu.(*Interface) if Ti == nil { if cause != nil { var detail string if isInterfacePtr(Tu) { detail = check.sprintf("type %s is pointer to interface, not interface", T) } else { detail = check.sprintf("%s is not an interface", T) } *cause = check.sprintf("%s does not %s %s (%s)", V, verb, T, detail) } return false } // Every type satisfies the empty interface. if Ti.Empty() { return true } // T is not the empty interface (i.e., the type set of T is restricted) // An interface V with an empty type set satisfies any interface. // (The empty set is a subset of any set.) Vi, _ := Vu.(*Interface) if Vi != nil && Vi.typeSet().IsEmpty() { return true } // type set of V is not empty // No type with non-empty type set satisfies the empty type set. if Ti.typeSet().IsEmpty() { if cause != nil { *cause = check.sprintf("cannot %s %s (empty type set)", verb, T) } return false } // V must implement T's methods, if any. if m, _ := check.missingMethod(V, T, true, Identical, cause); m != nil /* !Implements(V, T) */ { if cause != nil { *cause = check.sprintf("%s does not %s %s %s", V, verb, T, *cause) } return false } // Only check comparability if we don't have a more specific error. checkComparability := func() bool { if !Ti.IsComparable() { return true } // If T is comparable, V must be comparable. // If V is strictly comparable, we're done. if comparable(V, false /* strict comparability */, nil, nil) { return true } // For constraint satisfaction, use dynamic (spec) comparability // so that ordinary, non-type parameter interfaces implement comparable. if constraint && comparable(V, true /* spec comparability */, nil, nil) { // V is comparable if we are at Go 1.20 or higher. if check == nil || check.allowVersion(atPos(pos), go1_20) { // atPos needed so that go/types generate passes return true } if cause != nil { *cause = check.sprintf("%s to %s comparable requires go1.20 or later", V, verb) } return false } if cause != nil { *cause = check.sprintf("%s does not %s comparable", V, verb) } return false } // V must also be in the set of types of T, if any. // Constraints with empty type sets were already excluded above. if !Ti.typeSet().hasTerms() { return checkComparability() // nothing to do } // If V is itself an interface, each of its possible types must be in the set // of T types (i.e., the V type set must be a subset of the T type set). // Interfaces V with empty type sets were already excluded above. if Vi != nil { if !Vi.typeSet().subsetOf(Ti.typeSet()) { // TODO(gri) report which type is missing if cause != nil { *cause = check.sprintf("%s does not %s %s", V, verb, T) } return false } return checkComparability() } // Otherwise, V's type must be included in the iface type set. var alt Type if Ti.typeSet().is(func(t *term) bool { if !t.includes(V) { // If V ∉ t.typ but V ∈ ~t.typ then remember this type // so we can suggest it as an alternative in the error // message. if alt == nil && !t.tilde && Identical(t.typ, under(t.typ)) { tt := *t tt.tilde = true if tt.includes(V) { alt = t.typ } } return true } return false }) { if cause != nil { var detail string switch { case alt != nil: detail = check.sprintf("possibly missing ~ for %s in %s", alt, T) case mentions(Ti, V): detail = check.sprintf("%s mentions %s, but %s is not in the type set of %s", T, V, V, T) default: detail = check.sprintf("%s missing in %s", V, Ti.typeSet().terms) } *cause = check.sprintf("%s does not %s %s (%s)", V, verb, T, detail) } return false } return checkComparability() } // mentions reports whether type T "mentions" typ in an (embedded) element or term // of T (whether typ is in the type set of T or not). For better error messages. func mentions(T, typ Type) bool { switch T := T.(type) { case *Interface: for _, e := range T.embeddeds { if mentions(e, typ) { return true } } case *Union: for _, t := range T.terms { if mentions(t.typ, typ) { return true } } default: if Identical(T, typ) { return true } } return false }