// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT. // Source: ../../cmd/compile/internal/types2/subst.go // Copyright 2018 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 type parameter substitution. package types import ( "go/token" ) type substMap map[*TypeParam]Type // makeSubstMap creates a new substitution map mapping tpars[i] to targs[i]. // If targs[i] is nil, tpars[i] is not substituted. func makeSubstMap(tpars []*TypeParam, targs []Type) substMap { assert(len(tpars) == len(targs)) proj := make(substMap, len(tpars)) for i, tpar := range tpars { proj[tpar] = targs[i] } return proj } // makeRenameMap is like makeSubstMap, but creates a map used to rename type // parameters in from with the type parameters in to. func makeRenameMap(from, to []*TypeParam) substMap { assert(len(from) == len(to)) proj := make(substMap, len(from)) for i, tpar := range from { proj[tpar] = to[i] } return proj } func (m substMap) empty() bool { return len(m) == 0 } func (m substMap) lookup(tpar *TypeParam) Type { if t := m[tpar]; t != nil { return t } return tpar } // subst returns the type typ with its type parameters tpars replaced by the // corresponding type arguments targs, recursively. subst doesn't modify the // incoming type. If a substitution took place, the result type is different // from the incoming type. // // If expanding is non-nil, it is the instance type currently being expanded. // One of expanding or ctxt must be non-nil. func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, expanding *Named, ctxt *Context) Type { assert(expanding != nil || ctxt != nil) if smap.empty() { return typ } // common cases switch t := typ.(type) { case *Basic: return typ // nothing to do case *TypeParam: return smap.lookup(t) } // general case subst := subster{ pos: pos, smap: smap, check: check, expanding: expanding, ctxt: ctxt, } return subst.typ(typ) } type subster struct { pos token.Pos smap substMap check *Checker // nil if called via Instantiate expanding *Named // if non-nil, the instance that is being expanded ctxt *Context } func (subst *subster) typ(typ Type) Type { switch t := typ.(type) { case nil: // Call typOrNil if it's possible that typ is nil. panic("nil typ") case *Basic: // nothing to do case *Alias: // This code follows the code for *Named types closely. // TODO(gri) try to factor better orig := t.Origin() n := orig.TypeParams().Len() if n == 0 { return t // type is not parameterized } // TODO(gri) do we need this for Alias types? if t.TypeArgs().Len() != n { return Typ[Invalid] // error reported elsewhere } // already instantiated // For each (existing) type argument determine if it needs // to be substituted; i.e., if it is or contains a type parameter // that has a type argument for it. if targs := substList(t.TypeArgs().list(), subst.typ); targs != nil { return subst.check.newAliasInstance(subst.pos, t.orig, targs, subst.expanding, subst.ctxt) } case *Array: elem := subst.typOrNil(t.elem) if elem != t.elem { return &Array{len: t.len, elem: elem} } case *Slice: elem := subst.typOrNil(t.elem) if elem != t.elem { return &Slice{elem: elem} } case *Struct: if fields := substList(t.fields, subst.var_); fields != nil { s := &Struct{fields: fields, tags: t.tags} s.markComplete() return s } case *Pointer: base := subst.typ(t.base) if base != t.base { return &Pointer{base: base} } case *Tuple: return subst.tuple(t) case *Signature: // Preserve the receiver: it is handled during *Interface and *Named type // substitution. // // Naively doing the substitution here can lead to an infinite recursion in // the case where the receiver is an interface. For example, consider the // following declaration: // // type T[A any] struct { f interface{ m() } } // // In this case, the type of f is an interface that is itself the receiver // type of all of its methods. Because we have no type name to break // cycles, substituting in the recv results in an infinite loop of // recv->interface->recv->interface->... recv := t.recv params := subst.tuple(t.params) results := subst.tuple(t.results) if params != t.params || results != t.results { return &Signature{ rparams: t.rparams, // TODO(gri) why can't we nil out tparams here, rather than in instantiate? tparams: t.tparams, // instantiated signatures have a nil scope recv: recv, params: params, results: results, variadic: t.variadic, } } case *Union: if terms := substList(t.terms, subst.term); terms != nil { // term list substitution may introduce duplicate terms (unlikely but possible). // This is ok; lazy type set computation will determine the actual type set // in normal form. return &Union{terms} } case *Interface: methods := substList(t.methods, subst.func_) embeddeds := substList(t.embeddeds, subst.typ) if methods != nil || embeddeds != nil { if methods == nil { methods = t.methods } if embeddeds == nil { embeddeds = t.embeddeds } iface := subst.check.newInterface() iface.embeddeds = embeddeds iface.embedPos = t.embedPos iface.implicit = t.implicit assert(t.complete) // otherwise we are copying incomplete data iface.complete = t.complete // If we've changed the interface type, we may need to replace its // receiver if the receiver type is the original interface. Receivers of // *Named type are replaced during named type expansion. // // Notably, it's possible to reach here and not create a new *Interface, // even though the receiver type may be parameterized. For example: // // type T[P any] interface{ m() } // // In this case the interface will not be substituted here, because its // method signatures do not depend on the type parameter P, but we still // need to create new interface methods to hold the instantiated // receiver. This is handled by Named.expandUnderlying. iface.methods, _ = replaceRecvType(methods, t, iface) // If check != nil, check.newInterface will have saved the interface for later completion. if subst.check == nil { // golang/go#61561: all newly created interfaces must be completed iface.typeSet() } return iface } case *Map: key := subst.typ(t.key) elem := subst.typ(t.elem) if key != t.key || elem != t.elem { return &Map{key: key, elem: elem} } case *Chan: elem := subst.typ(t.elem) if elem != t.elem { return &Chan{dir: t.dir, elem: elem} } case *Named: // subst is called during expansion, so in this function we need to be // careful not to call any methods that would cause t to be expanded: doing // so would result in deadlock. // // So we call t.Origin().TypeParams() rather than t.TypeParams(). orig := t.Origin() n := orig.TypeParams().Len() if n == 0 { return t // type is not parameterized } if t.TypeArgs().Len() != n { return Typ[Invalid] // error reported elsewhere } // already instantiated // For each (existing) type argument determine if it needs // to be substituted; i.e., if it is or contains a type parameter // that has a type argument for it. if targs := substList(t.TypeArgs().list(), subst.typ); targs != nil { // Create a new instance and populate the context to avoid endless // recursion. The position used here is irrelevant because validation only // occurs on t (we don't call validType on named), but we use subst.pos to // help with debugging. return subst.check.instance(subst.pos, orig, targs, subst.expanding, subst.ctxt) } case *TypeParam: return subst.smap.lookup(t) default: panic("unreachable") } return typ } // typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid]. // A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_)) // where an array/slice element is accessed before it is set up. func (subst *subster) typOrNil(typ Type) Type { if typ == nil { return Typ[Invalid] } return subst.typ(typ) } func (subst *subster) var_(v *Var) *Var { if v != nil { if typ := subst.typ(v.typ); typ != v.typ { return cloneVar(v, typ) } } return v } func cloneVar(v *Var, typ Type) *Var { copy := *v copy.typ = typ copy.origin = v.Origin() return © } func (subst *subster) tuple(t *Tuple) *Tuple { if t != nil { if vars := substList(t.vars, subst.var_); vars != nil { return &Tuple{vars: vars} } } return t } // substList applies subst to each element of the incoming slice. // If at least one element changes, the result is a new slice with // all the (possibly updated) elements of the incoming slice; // otherwise the result it nil. The incoming slice is unchanged. func substList[T comparable](in []T, subst func(T) T) (out []T) { for i, t := range in { if u := subst(t); u != t { if out == nil { // lazily allocate a new slice on first substitution out = make([]T, len(in)) copy(out, in) } out[i] = u } } return } func (subst *subster) func_(f *Func) *Func { if f != nil { if typ := subst.typ(f.typ); typ != f.typ { return cloneFunc(f, typ) } } return f } func cloneFunc(f *Func, typ Type) *Func { copy := *f copy.typ = typ copy.origin = f.Origin() return © } func (subst *subster) term(t *Term) *Term { if typ := subst.typ(t.typ); typ != t.typ { return NewTerm(t.tilde, typ) } return t } // replaceRecvType updates any function receivers that have type old to have // type new. It does not modify the input slice; if modifications are required, // the input slice and any affected signatures will be copied before mutating. // // The resulting out slice contains the updated functions, and copied reports // if anything was modified. func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) { out = in for i, method := range in { sig := method.Signature() if sig.recv != nil && sig.recv.Type() == old { if !copied { // Allocate a new methods slice before mutating for the first time. // This is defensive, as we may share methods across instantiations of // a given interface type if they do not get substituted. out = make([]*Func, len(in)) copy(out, in) copied = true } newsig := *sig newsig.recv = cloneVar(sig.recv, new) out[i] = cloneFunc(method, &newsig) } } return }