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