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