]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/types2/validtype.go
all: fix TODO comment hanging indents
[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                 // Note: ensure that t.orig is fully resolved by calling Underlying().
67                 if t.Underlying() == Typ[Invalid] {
68                         check.infoMap[t] = invalid
69                         return invalid
70                 }
71
72                 switch check.infoMap[t] {
73                 case unknown:
74                         check.infoMap[t] = marked
75                         check.infoMap[t] = check.validType0(t.orig.fromRHS, env.push(t), append(path, t.obj))
76                 case marked:
77                         // We have seen type t before and thus must have a cycle.
78                         check.infoMap[t] = invalid
79                         // t cannot be in an imported package otherwise that package
80                         // would have reported a type cycle and couldn't have been
81                         // imported in the first place.
82                         assert(t.obj.pkg == check.pkg)
83                         t.underlying = Typ[Invalid] // t is in the current package (no race possibility)
84                         // Find the starting point of the cycle and report it.
85                         for i, tn := range path {
86                                 if tn == t.obj {
87                                         check.cycleError(path[i:])
88                                         return invalid
89                                 }
90                         }
91                         panic("cycle start not found")
92                 }
93                 return check.infoMap[t]
94
95         case *TypeParam:
96                 // A type parameter stands for the type (argument) it was instantiated with.
97                 // Check the corresponding type argument for validity if we have one.
98                 if env != nil {
99                         if targ := env.tmap[t]; targ != nil {
100                                 // Type arguments found in targ must be looked
101                                 // up in the enclosing environment env.link.
102                                 return check.validType0(targ, env.link, path)
103                         }
104                 }
105         }
106
107         return valid
108 }
109
110 // A tparamEnv provides the environment for looking up the type arguments
111 // with which type parameters for a given instance were instantiated.
112 // If we don't have an instance, the corresponding tparamEnv is nil.
113 type tparamEnv struct {
114         tmap substMap
115         link *tparamEnv
116 }
117
118 func (env *tparamEnv) push(typ *Named) *tparamEnv {
119         // If typ is not an instantiated type there are no typ-specific
120         // type parameters to look up and we don't need an environment.
121         targs := typ.TypeArgs()
122         if targs == nil {
123                 return nil // no instance => nil environment
124         }
125
126         // Populate tmap: remember the type argument for each type parameter.
127         // We cannot use makeSubstMap because the number of type parameters
128         // and arguments may not match due to errors in the source (too many
129         // or too few type arguments). Populate tmap "manually".
130         tparams := typ.TypeParams()
131         n, m := targs.Len(), tparams.Len()
132         if n > m {
133                 n = m // too many targs
134         }
135         tmap := make(substMap, n)
136         for i := 0; i < n; i++ {
137                 tmap[tparams.At(i)] = targs.At(i)
138         }
139
140         return &tparamEnv{tmap: tmap, link: env}
141 }
142
143 // TODO(gri) Alternative implementation:
144 // We may not need to build a stack of environments to
145 // look up the type arguments for type parameters. The
146 // same information should be available via the path:
147 // We should be able to just walk the path backwards
148 // and find the type arguments in the instance objects.