github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/types2/subst.go (about) 1 // Copyright 2018 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 type parameter substitution. 6 7 package types2 8 9 import ( 10 "github.com/go-asm/go/cmd/compile/syntax" 11 ) 12 13 type substMap map[*TypeParam]Type 14 15 // makeSubstMap creates a new substitution map mapping tpars[i] to targs[i]. 16 // If targs[i] is nil, tpars[i] is not substituted. 17 func makeSubstMap(tpars []*TypeParam, targs []Type) substMap { 18 assert(len(tpars) == len(targs)) 19 proj := make(substMap, len(tpars)) 20 for i, tpar := range tpars { 21 proj[tpar] = targs[i] 22 } 23 return proj 24 } 25 26 // makeRenameMap is like makeSubstMap, but creates a map used to rename type 27 // parameters in from with the type parameters in to. 28 func makeRenameMap(from, to []*TypeParam) substMap { 29 assert(len(from) == len(to)) 30 proj := make(substMap, len(from)) 31 for i, tpar := range from { 32 proj[tpar] = to[i] 33 } 34 return proj 35 } 36 37 func (m substMap) empty() bool { 38 return len(m) == 0 39 } 40 41 func (m substMap) lookup(tpar *TypeParam) Type { 42 if t := m[tpar]; t != nil { 43 return t 44 } 45 return tpar 46 } 47 48 // subst returns the type typ with its type parameters tpars replaced by the 49 // corresponding type arguments targs, recursively. subst doesn't modify the 50 // incoming type. If a substitution took place, the result type is different 51 // from the incoming type. 52 // 53 // If expanding is non-nil, it is the instance type currently being expanded. 54 // One of expanding or ctxt must be non-nil. 55 func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, expanding *Named, ctxt *Context) Type { 56 assert(expanding != nil || ctxt != nil) 57 58 if smap.empty() { 59 return typ 60 } 61 62 // common cases 63 switch t := typ.(type) { 64 case *Basic: 65 return typ // nothing to do 66 case *TypeParam: 67 return smap.lookup(t) 68 } 69 70 // general case 71 subst := subster{ 72 pos: pos, 73 smap: smap, 74 check: check, 75 expanding: expanding, 76 ctxt: ctxt, 77 } 78 return subst.typ(typ) 79 } 80 81 type subster struct { 82 pos syntax.Pos 83 smap substMap 84 check *Checker // nil if called via Instantiate 85 expanding *Named // if non-nil, the instance that is being expanded 86 ctxt *Context 87 } 88 89 func (subst *subster) typ(typ Type) Type { 90 switch t := typ.(type) { 91 case nil: 92 // Call typOrNil if it's possible that typ is nil. 93 panic("nil typ") 94 95 case *Basic: 96 // nothing to do 97 98 case *Array: 99 elem := subst.typOrNil(t.elem) 100 if elem != t.elem { 101 return &Array{len: t.len, elem: elem} 102 } 103 104 case *Slice: 105 elem := subst.typOrNil(t.elem) 106 if elem != t.elem { 107 return &Slice{elem: elem} 108 } 109 110 case *Struct: 111 if fields, copied := subst.varList(t.fields); copied { 112 s := &Struct{fields: fields, tags: t.tags} 113 s.markComplete() 114 return s 115 } 116 117 case *Pointer: 118 base := subst.typ(t.base) 119 if base != t.base { 120 return &Pointer{base: base} 121 } 122 123 case *Tuple: 124 return subst.tuple(t) 125 126 case *Signature: 127 // Preserve the receiver: it is handled during *Interface and *Named type 128 // substitution. 129 // 130 // Naively doing the substitution here can lead to an infinite recursion in 131 // the case where the receiver is an interface. For example, consider the 132 // following declaration: 133 // 134 // type T[A any] struct { f interface{ m() } } 135 // 136 // In this case, the type of f is an interface that is itself the receiver 137 // type of all of its methods. Because we have no type name to break 138 // cycles, substituting in the recv results in an infinite loop of 139 // recv->interface->recv->interface->... 140 recv := t.recv 141 142 params := subst.tuple(t.params) 143 results := subst.tuple(t.results) 144 if params != t.params || results != t.results { 145 return &Signature{ 146 rparams: t.rparams, 147 // TODO(gri) why can't we nil out tparams here, rather than in instantiate? 148 tparams: t.tparams, 149 // instantiated signatures have a nil scope 150 recv: recv, 151 params: params, 152 results: results, 153 variadic: t.variadic, 154 } 155 } 156 157 case *Union: 158 terms, copied := subst.termlist(t.terms) 159 if copied { 160 // term list substitution may introduce duplicate terms (unlikely but possible). 161 // This is ok; lazy type set computation will determine the actual type set 162 // in normal form. 163 return &Union{terms} 164 } 165 166 case *Interface: 167 methods, mcopied := subst.funcList(t.methods) 168 embeddeds, ecopied := subst.typeList(t.embeddeds) 169 if mcopied || ecopied { 170 iface := subst.check.newInterface() 171 iface.embeddeds = embeddeds 172 iface.embedPos = t.embedPos 173 iface.implicit = t.implicit 174 assert(t.complete) // otherwise we are copying incomplete data 175 iface.complete = t.complete 176 // If we've changed the interface type, we may need to replace its 177 // receiver if the receiver type is the original interface. Receivers of 178 // *Named type are replaced during named type expansion. 179 // 180 // Notably, it's possible to reach here and not create a new *Interface, 181 // even though the receiver type may be parameterized. For example: 182 // 183 // type T[P any] interface{ m() } 184 // 185 // In this case the interface will not be substituted here, because its 186 // method signatures do not depend on the type parameter P, but we still 187 // need to create new interface methods to hold the instantiated 188 // receiver. This is handled by Named.expandUnderlying. 189 iface.methods, _ = replaceRecvType(methods, t, iface) 190 191 // If check != nil, check.newInterface will have saved the interface for later completion. 192 if subst.check == nil { // golang/go#61561: all newly created interfaces must be completed 193 iface.typeSet() 194 } 195 return iface 196 } 197 198 case *Map: 199 key := subst.typ(t.key) 200 elem := subst.typ(t.elem) 201 if key != t.key || elem != t.elem { 202 return &Map{key: key, elem: elem} 203 } 204 205 case *Chan: 206 elem := subst.typ(t.elem) 207 if elem != t.elem { 208 return &Chan{dir: t.dir, elem: elem} 209 } 210 211 case *Named: 212 // dump is for debugging 213 dump := func(string, ...interface{}) {} 214 if subst.check != nil && subst.check.conf.Trace { 215 subst.check.indent++ 216 defer func() { 217 subst.check.indent-- 218 }() 219 dump = func(format string, args ...interface{}) { 220 subst.check.trace(subst.pos, format, args...) 221 } 222 } 223 224 // subst is called during expansion, so in this function we need to be 225 // careful not to call any methods that would cause t to be expanded: doing 226 // so would result in deadlock. 227 // 228 // So we call t.Origin().TypeParams() rather than t.TypeParams(). 229 orig := t.Origin() 230 n := orig.TypeParams().Len() 231 if n == 0 { 232 dump(">>> %s is not parameterized", t) 233 return t // type is not parameterized 234 } 235 236 var newTArgs []Type 237 if t.TypeArgs().Len() != n { 238 return Typ[Invalid] // error reported elsewhere 239 } 240 241 // already instantiated 242 dump(">>> %s already instantiated", t) 243 // For each (existing) type argument targ, determine if it needs 244 // to be substituted; i.e., if it is or contains a type parameter 245 // that has a type argument for it. 246 for i, targ := range t.TypeArgs().list() { 247 dump(">>> %d targ = %s", i, targ) 248 new_targ := subst.typ(targ) 249 if new_targ != targ { 250 dump(">>> substituted %d targ %s => %s", i, targ, new_targ) 251 if newTArgs == nil { 252 newTArgs = make([]Type, n) 253 copy(newTArgs, t.TypeArgs().list()) 254 } 255 newTArgs[i] = new_targ 256 } 257 } 258 259 if newTArgs == nil { 260 dump(">>> nothing to substitute in %s", t) 261 return t // nothing to substitute 262 } 263 264 // Create a new instance and populate the context to avoid endless 265 // recursion. The position used here is irrelevant because validation only 266 // occurs on t (we don't call validType on named), but we use subst.pos to 267 // help with debugging. 268 return subst.check.instance(subst.pos, orig, newTArgs, subst.expanding, subst.ctxt) 269 270 case *TypeParam: 271 return subst.smap.lookup(t) 272 273 default: 274 unreachable() 275 } 276 277 return typ 278 } 279 280 // typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid]. 281 // A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_)) 282 // where an array/slice element is accessed before it is set up. 283 func (subst *subster) typOrNil(typ Type) Type { 284 if typ == nil { 285 return Typ[Invalid] 286 } 287 return subst.typ(typ) 288 } 289 290 func (subst *subster) var_(v *Var) *Var { 291 if v != nil { 292 if typ := subst.typ(v.typ); typ != v.typ { 293 return substVar(v, typ) 294 } 295 } 296 return v 297 } 298 299 func substVar(v *Var, typ Type) *Var { 300 copy := *v 301 copy.typ = typ 302 copy.origin = v.Origin() 303 return © 304 } 305 306 func (subst *subster) tuple(t *Tuple) *Tuple { 307 if t != nil { 308 if vars, copied := subst.varList(t.vars); copied { 309 return &Tuple{vars: vars} 310 } 311 } 312 return t 313 } 314 315 func (subst *subster) varList(in []*Var) (out []*Var, copied bool) { 316 out = in 317 for i, v := range in { 318 if w := subst.var_(v); w != v { 319 if !copied { 320 // first variable that got substituted => allocate new out slice 321 // and copy all variables 322 new := make([]*Var, len(in)) 323 copy(new, out) 324 out = new 325 copied = true 326 } 327 out[i] = w 328 } 329 } 330 return 331 } 332 333 func (subst *subster) func_(f *Func) *Func { 334 if f != nil { 335 if typ := subst.typ(f.typ); typ != f.typ { 336 return substFunc(f, typ) 337 } 338 } 339 return f 340 } 341 342 func substFunc(f *Func, typ Type) *Func { 343 copy := *f 344 copy.typ = typ 345 copy.origin = f.Origin() 346 return © 347 } 348 349 func (subst *subster) funcList(in []*Func) (out []*Func, copied bool) { 350 out = in 351 for i, f := range in { 352 if g := subst.func_(f); g != f { 353 if !copied { 354 // first function that got substituted => allocate new out slice 355 // and copy all functions 356 new := make([]*Func, len(in)) 357 copy(new, out) 358 out = new 359 copied = true 360 } 361 out[i] = g 362 } 363 } 364 return 365 } 366 367 func (subst *subster) typeList(in []Type) (out []Type, copied bool) { 368 out = in 369 for i, t := range in { 370 if u := subst.typ(t); u != t { 371 if !copied { 372 // first function that got substituted => allocate new out slice 373 // and copy all functions 374 new := make([]Type, len(in)) 375 copy(new, out) 376 out = new 377 copied = true 378 } 379 out[i] = u 380 } 381 } 382 return 383 } 384 385 func (subst *subster) termlist(in []*Term) (out []*Term, copied bool) { 386 out = in 387 for i, t := range in { 388 if u := subst.typ(t.typ); u != t.typ { 389 if !copied { 390 // first function that got substituted => allocate new out slice 391 // and copy all functions 392 new := make([]*Term, len(in)) 393 copy(new, out) 394 out = new 395 copied = true 396 } 397 out[i] = NewTerm(t.tilde, u) 398 } 399 } 400 return 401 } 402 403 // replaceRecvType updates any function receivers that have type old to have 404 // type new. It does not modify the input slice; if modifications are required, 405 // the input slice and any affected signatures will be copied before mutating. 406 // 407 // The resulting out slice contains the updated functions, and copied reports 408 // if anything was modified. 409 func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) { 410 out = in 411 for i, method := range in { 412 sig := method.Type().(*Signature) 413 if sig.recv != nil && sig.recv.Type() == old { 414 if !copied { 415 // Allocate a new methods slice before mutating for the first time. 416 // This is defensive, as we may share methods across instantiations of 417 // a given interface type if they do not get substituted. 418 out = make([]*Func, len(in)) 419 copy(out, in) 420 copied = true 421 } 422 newsig := *sig 423 newsig.recv = substVar(sig.recv, new) 424 out[i] = substFunc(method, &newsig) 425 } 426 } 427 return 428 }