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