github.com/bir3/gocompiler@v0.3.205/src/go/types/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 types 8 9 import ( 10 "github.com/bir3/gocompiler/src/go/token" 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 is pure in the sense 50 // that it doesn't modify the incoming type. If a substitution took place, the 51 // result type is different 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 token.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 token.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(rFindley) 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.implicit = t.implicit 173 iface.complete = t.complete 174 // If we've changed the interface type, we may need to replace its 175 // receiver if the receiver type is the original interface. Receivers of 176 // *Named type are replaced during named type expansion. 177 // 178 // Notably, it's possible to reach here and not create a new *Interface, 179 // even though the receiver type may be parameterized. For example: 180 // 181 // type T[P any] interface{ m() } 182 // 183 // In this case the interface will not be substituted here, because its 184 // method signatures do not depend on the type parameter P, but we still 185 // need to create new interface methods to hold the instantiated 186 // receiver. This is handled by Named.expandUnderlying. 187 iface.methods, _ = replaceRecvType(methods, t, iface) 188 return iface 189 } 190 191 case *Map: 192 key := subst.typ(t.key) 193 elem := subst.typ(t.elem) 194 if key != t.key || elem != t.elem { 195 return &Map{key: key, elem: elem} 196 } 197 198 case *Chan: 199 elem := subst.typ(t.elem) 200 if elem != t.elem { 201 return &Chan{dir: t.dir, elem: elem} 202 } 203 204 case *Named: 205 // dump is for debugging 206 dump := func(string, ...any) {} 207 if subst.check != nil && trace { 208 subst.check.indent++ 209 defer func() { 210 subst.check.indent-- 211 }() 212 dump = func(format string, args ...any) { 213 subst.check.trace(subst.pos, format, args...) 214 } 215 } 216 217 // subst is called during expansion, so in this function we need to be 218 // careful not to call any methods that would cause t to be expanded: doing 219 // so would result in deadlock. 220 // 221 // So we call t.Origin().TypeParams() rather than t.TypeParams(). 222 orig := t.Origin() 223 n := orig.TypeParams().Len() 224 if n == 0 { 225 dump(">>> %s is not parameterized", t) 226 return t // type is not parameterized 227 } 228 229 var newTArgs []Type 230 if t.TypeArgs().Len() != n { 231 return Typ[Invalid] // error reported elsewhere 232 } 233 234 // already instantiated 235 dump(">>> %s already instantiated", t) 236 // For each (existing) type argument targ, determine if it needs 237 // to be substituted; i.e., if it is or contains a type parameter 238 // that has a type argument for it. 239 for i, targ := range t.TypeArgs().list() { 240 dump(">>> %d targ = %s", i, targ) 241 new_targ := subst.typ(targ) 242 if new_targ != targ { 243 dump(">>> substituted %d targ %s => %s", i, targ, new_targ) 244 if newTArgs == nil { 245 newTArgs = make([]Type, n) 246 copy(newTArgs, t.TypeArgs().list()) 247 } 248 newTArgs[i] = new_targ 249 } 250 } 251 252 if newTArgs == nil { 253 dump(">>> nothing to substitute in %s", t) 254 return t // nothing to substitute 255 } 256 257 // Create a new instance and populate the context to avoid endless 258 // recursion. The position used here is irrelevant because validation only 259 // occurs on t (we don't call validType on named), but we use subst.pos to 260 // help with debugging. 261 return subst.check.instance(subst.pos, orig, newTArgs, subst.expanding, subst.ctxt) 262 263 case *TypeParam: 264 return subst.smap.lookup(t) 265 266 default: 267 unreachable() 268 } 269 270 return typ 271 } 272 273 // typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid]. 274 // A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_)) 275 // where an array/slice element is accessed before it is set up. 276 func (subst *subster) typOrNil(typ Type) Type { 277 if typ == nil { 278 return Typ[Invalid] 279 } 280 return subst.typ(typ) 281 } 282 283 func (subst *subster) var_(v *Var) *Var { 284 if v != nil { 285 if typ := subst.typ(v.typ); typ != v.typ { 286 return substVar(v, typ) 287 } 288 } 289 return v 290 } 291 292 func substVar(v *Var, typ Type) *Var { 293 copy := *v 294 copy.typ = typ 295 copy.origin = v.Origin() 296 return © 297 } 298 299 func (subst *subster) tuple(t *Tuple) *Tuple { 300 if t != nil { 301 if vars, copied := subst.varList(t.vars); copied { 302 return &Tuple{vars: vars} 303 } 304 } 305 return t 306 } 307 308 func (subst *subster) varList(in []*Var) (out []*Var, copied bool) { 309 out = in 310 for i, v := range in { 311 if w := subst.var_(v); w != v { 312 if !copied { 313 // first variable that got substituted => allocate new out slice 314 // and copy all variables 315 new := make([]*Var, len(in)) 316 copy(new, out) 317 out = new 318 copied = true 319 } 320 out[i] = w 321 } 322 } 323 return 324 } 325 326 func (subst *subster) func_(f *Func) *Func { 327 if f != nil { 328 if typ := subst.typ(f.typ); typ != f.typ { 329 return substFunc(f, typ) 330 } 331 } 332 return f 333 } 334 335 func substFunc(f *Func, typ Type) *Func { 336 copy := *f 337 copy.typ = typ 338 copy.origin = f.Origin() 339 return © 340 } 341 342 func (subst *subster) funcList(in []*Func) (out []*Func, copied bool) { 343 out = in 344 for i, f := range in { 345 if g := subst.func_(f); g != f { 346 if !copied { 347 // first function that got substituted => allocate new out slice 348 // and copy all functions 349 new := make([]*Func, len(in)) 350 copy(new, out) 351 out = new 352 copied = true 353 } 354 out[i] = g 355 } 356 } 357 return 358 } 359 360 func (subst *subster) typeList(in []Type) (out []Type, copied bool) { 361 out = in 362 for i, t := range in { 363 if u := subst.typ(t); u != t { 364 if !copied { 365 // first function that got substituted => allocate new out slice 366 // and copy all functions 367 new := make([]Type, len(in)) 368 copy(new, out) 369 out = new 370 copied = true 371 } 372 out[i] = u 373 } 374 } 375 return 376 } 377 378 func (subst *subster) termlist(in []*Term) (out []*Term, copied bool) { 379 out = in 380 for i, t := range in { 381 if u := subst.typ(t.typ); u != t.typ { 382 if !copied { 383 // first function that got substituted => allocate new out slice 384 // and copy all functions 385 new := make([]*Term, len(in)) 386 copy(new, out) 387 out = new 388 copied = true 389 } 390 out[i] = NewTerm(t.tilde, u) 391 } 392 } 393 return 394 } 395 396 // replaceRecvType updates any function receivers that have type old to have 397 // type new. It does not modify the input slice; if modifications are required, 398 // the input slice and any affected signatures will be copied before mutating. 399 // 400 // The resulting out slice contains the updated functions, and copied reports 401 // if anything was modified. 402 func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) { 403 out = in 404 for i, method := range in { 405 sig := method.Type().(*Signature) 406 if sig.recv != nil && sig.recv.Type() == old { 407 if !copied { 408 // Allocate a new methods slice before mutating for the first time. 409 // This is defensive, as we may share methods across instantiations of 410 // a given interface type if they do not get substituted. 411 out = make([]*Func, len(in)) 412 copy(out, in) 413 copied = true 414 } 415 newsig := *sig 416 newsig.recv = substVar(sig.recv, new) 417 out[i] = substFunc(method, &newsig) 418 } 419 } 420 return 421 }