1
2
3
4
5 package types2
6
7 import (
8 "cmd/compile/internal/syntax"
9 . "internal/types/errors"
10 )
11
12
13 func (check *Checker) labels(body *syntax.BlockStmt) {
14
15 all := NewScope(nil, body.Pos(), syntax.EndPos(body), "label")
16
17 fwdJumps := check.blockBranches(all, nil, nil, body.List)
18
19
20
21
22
23 for _, jmp := range fwdJumps {
24 var msg string
25 var code Code
26 name := jmp.Label.Value
27 if alt := all.Lookup(name); alt != nil {
28 msg = "goto %s jumps into block"
29 alt.(*Label).used = true
30 code = JumpIntoBlock
31
32 } else {
33 msg = "label %s not declared"
34 code = UndeclaredLabel
35 name = quote(name)
36 }
37 check.errorf(jmp.Label, code, msg, name)
38 }
39
40
41 for name, obj := range all.elems {
42 obj = resolve(name, obj)
43 if lbl := obj.(*Label); !lbl.used {
44 check.softErrorf(lbl.pos, UnusedLabel, "label %s declared and not used", quote(lbl.name))
45 }
46 }
47 }
48
49
50 type block struct {
51 parent *block
52 lstmt *syntax.LabeledStmt
53 labels map[string]*syntax.LabeledStmt
54 }
55
56
57
58 func (b *block) insert(s *syntax.LabeledStmt) {
59 name := s.Label.Value
60 if debug {
61 assert(b.gotoTarget(name) == nil)
62 }
63 labels := b.labels
64 if labels == nil {
65 labels = make(map[string]*syntax.LabeledStmt)
66 b.labels = labels
67 }
68 labels[name] = s
69 }
70
71
72
73 func (b *block) gotoTarget(name string) *syntax.LabeledStmt {
74 for s := b; s != nil; s = s.parent {
75 if t := s.labels[name]; t != nil {
76 return t
77 }
78 }
79 return nil
80 }
81
82
83
84 func (b *block) enclosingTarget(name string) *syntax.LabeledStmt {
85 for s := b; s != nil; s = s.parent {
86 if t := s.lstmt; t != nil && t.Label.Value == name {
87 return t
88 }
89 }
90 return nil
91 }
92
93
94
95
96 func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *syntax.LabeledStmt, list []syntax.Stmt) []*syntax.BranchStmt {
97 b := &block{parent, lstmt, nil}
98
99 var (
100 varDeclPos syntax.Pos
101 fwdJumps, badJumps []*syntax.BranchStmt
102 )
103
104
105
106
107 recordVarDecl := func(pos syntax.Pos) {
108 varDeclPos = pos
109 badJumps = append(badJumps[:0], fwdJumps...)
110 }
111
112 jumpsOverVarDecl := func(jmp *syntax.BranchStmt) bool {
113 if varDeclPos.IsKnown() {
114 for _, bad := range badJumps {
115 if jmp == bad {
116 return true
117 }
118 }
119 }
120 return false
121 }
122
123 var stmtBranches func(syntax.Stmt)
124 stmtBranches = func(s syntax.Stmt) {
125 switch s := s.(type) {
126 case *syntax.DeclStmt:
127 for _, d := range s.DeclList {
128 if d, _ := d.(*syntax.VarDecl); d != nil {
129 recordVarDecl(d.Pos())
130 }
131 }
132
133 case *syntax.LabeledStmt:
134
135 if name := s.Label.Value; name != "_" {
136 lbl := NewLabel(s.Label.Pos(), check.pkg, name)
137 if alt := all.Insert(lbl); alt != nil {
138 err := check.newError(DuplicateLabel)
139 err.soft = true
140 err.addf(lbl.pos, "label %s already declared", quote(name))
141 err.addAltDecl(alt)
142 err.report()
143
144 } else {
145 b.insert(s)
146 check.recordDef(s.Label, lbl)
147 }
148
149 i := 0
150 for _, jmp := range fwdJumps {
151 if jmp.Label.Value == name {
152
153 lbl.used = true
154 check.recordUse(jmp.Label, lbl)
155 if jumpsOverVarDecl(jmp) {
156 check.softErrorf(
157 jmp.Label,
158 JumpOverDecl,
159 "goto %s jumps over variable declaration at line %d",
160 name,
161 varDeclPos.Line(),
162 )
163
164 }
165 } else {
166
167 fwdJumps[i] = jmp
168 i++
169 }
170 }
171 fwdJumps = fwdJumps[:i]
172 lstmt = s
173 }
174 stmtBranches(s.Stmt)
175
176 case *syntax.BranchStmt:
177 if s.Label == nil {
178 return
179 }
180
181
182 name := s.Label.Value
183 switch s.Tok {
184 case syntax.Break:
185
186
187
188 valid := false
189 if t := b.enclosingTarget(name); t != nil {
190 switch t.Stmt.(type) {
191 case *syntax.SwitchStmt, *syntax.SelectStmt, *syntax.ForStmt:
192 valid = true
193 }
194 }
195 if !valid {
196 check.errorf(s.Label, MisplacedLabel, "invalid break label %s", quote(name))
197 return
198 }
199
200 case syntax.Continue:
201
202
203 valid := false
204 if t := b.enclosingTarget(name); t != nil {
205 switch t.Stmt.(type) {
206 case *syntax.ForStmt:
207 valid = true
208 }
209 }
210 if !valid {
211 check.errorf(s.Label, MisplacedLabel, "invalid continue label %s", quote(name))
212 return
213 }
214
215 case syntax.Goto:
216 if b.gotoTarget(name) == nil {
217
218 fwdJumps = append(fwdJumps, s)
219 return
220 }
221
222 default:
223 check.errorf(s, InvalidSyntaxTree, "branch statement: %s %s", s.Tok, name)
224 return
225 }
226
227
228 obj := all.Lookup(name)
229 obj.(*Label).used = true
230 check.recordUse(s.Label, obj)
231
232 case *syntax.AssignStmt:
233 if s.Op == syntax.Def {
234 recordVarDecl(s.Pos())
235 }
236
237 case *syntax.BlockStmt:
238
239
240 fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, s.List)...)
241
242 case *syntax.IfStmt:
243 stmtBranches(s.Then)
244 if s.Else != nil {
245 stmtBranches(s.Else)
246 }
247
248 case *syntax.SwitchStmt:
249 b := &block{b, lstmt, nil}
250 for _, s := range s.Body {
251 fwdJumps = append(fwdJumps, check.blockBranches(all, b, nil, s.Body)...)
252 }
253
254 case *syntax.SelectStmt:
255 b := &block{b, lstmt, nil}
256 for _, s := range s.Body {
257 fwdJumps = append(fwdJumps, check.blockBranches(all, b, nil, s.Body)...)
258 }
259
260 case *syntax.ForStmt:
261 stmtBranches(s.Body)
262 }
263 }
264
265 for _, s := range list {
266 stmtBranches(s)
267 }
268
269 return fwdJumps
270 }
271
View as plain text