]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/types2/validtype.go
all: fix typos
[gostls13.git] / src / cmd / compile / internal / types2 / 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 types2
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.