Source file src/go/types/validtype.go
1 // Copyright 2022 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 package types 6 7 // validType verifies that the given type does not "expand" indefinitely 8 // producing a cycle in the type graph. Cycles are detected by marking 9 // defined types. 10 // (Cycles involving alias types, as in "type A = [10]A" are detected 11 // earlier, via the objDecl cycle detection mechanism.) 12 func (check *Checker) validType(typ *Named) { 13 check.validType0(typ, nil, nil) 14 } 15 16 type typeInfo uint 17 18 // validType0 checks if the given type is valid. If typ is a type parameter 19 // its value is looked up in the provided environment. The environment is 20 // nil if typ is not part of (the RHS of) an instantiated type, in that case 21 // any type parameter encountered must be from an enclosing function and can 22 // be ignored. The path is the list of type names that lead to the current typ. 23 func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeInfo { 24 const ( 25 unknown typeInfo = iota 26 marked 27 valid 28 invalid 29 ) 30 31 switch t := typ.(type) { 32 case nil: 33 // We should never see a nil type but be conservative and panic 34 // only in debug mode. 35 if debug { 36 panic("validType0(nil)") 37 } 38 39 case *Array: 40 return check.validType0(t.elem, env, path) 41 42 case *Struct: 43 for _, f := range t.fields { 44 if check.validType0(f.typ, env, path) == invalid { 45 return invalid 46 } 47 } 48 49 case *Union: 50 for _, t := range t.terms { 51 if check.validType0(t.typ, env, path) == invalid { 52 return invalid 53 } 54 } 55 56 case *Interface: 57 for _, etyp := range t.embeddeds { 58 if check.validType0(etyp, env, path) == invalid { 59 return invalid 60 } 61 } 62 63 case *Named: 64 // Don't report a 2nd error if we already know the type is invalid 65 // (e.g., if a cycle was detected earlier, via under). 66 if t.underlying == Typ[Invalid] { 67 check.infoMap[t] = invalid 68 return invalid 69 } 70 71 switch check.infoMap[t] { 72 case unknown: 73 check.infoMap[t] = marked 74 check.infoMap[t] = check.validType0(t.orig.fromRHS, env.push(t), append(path, t.obj)) 75 case marked: 76 // We have seen type t before and thus must have a cycle. 77 check.infoMap[t] = invalid 78 // t cannot be in an imported package otherwise that package 79 // would have reported a type cycle and couldn't have been 80 // imported in the first place. 81 assert(t.obj.pkg == check.pkg) 82 t.underlying = Typ[Invalid] // t is in the current package (no race possibility) 83 // Find the starting point of the cycle and report it. 84 for i, tn := range path { 85 if tn == t.obj { 86 check.cycleError(path[i:]) 87 return invalid 88 } 89 } 90 panic("cycle start not found") 91 } 92 return check.infoMap[t] 93 94 case *TypeParam: 95 // A type parameter stands for the type (argument) it was instantiated with. 96 // Check the corresponding type argument for validity if we have one. 97 if env != nil { 98 if targ := env.tmap[t]; targ != nil { 99 // Type arguments found in targ must be looked 100 // up in the enclosing environment env.link. 101 return check.validType0(targ, env.link, path) 102 } 103 } 104 } 105 106 return valid 107 } 108 109 // A tparamEnv provides the environment for looking up the type arguments 110 // with which type parameters for a given instance were instantiated. 111 // If we don't have an instance, the corresponding tparamEnv is nil. 112 type tparamEnv struct { 113 tmap substMap 114 link *tparamEnv 115 } 116 117 func (env *tparamEnv) push(typ *Named) *tparamEnv { 118 // If typ is not an instantiated type there are no typ-specific 119 // type parameters to look up and we don't need an environment. 120 targs := typ.TypeArgs() 121 if targs == nil { 122 return nil // no instance => nil environment 123 } 124 125 // Populate tmap: remember the type argument for each type parameter. 126 // We cannot use makeSubstMap because the number of type parameters 127 // and arguments may not match due to errors in the source (too many 128 // or too few type arguments). Populate tmap "manually". 129 tparams := typ.TypeParams() 130 n, m := targs.Len(), tparams.Len() 131 if n > m { 132 n = m // too many targs 133 } 134 tmap := make(substMap, n) 135 for i := 0; i < n; i++ { 136 tmap[tparams.At(i)] = targs.At(i) 137 } 138 139 return &tparamEnv{tmap: tmap, link: env} 140 } 141 142 // TODO(gri) Alternative implementation: 143 // We may not need to build a stack of environments to 144 // look up the type arguments for type parameters. The 145 // same information should be available via the path: 146 // We should be able to just walk the path backwards 147 // and find the type arguments in the instance objects. 148