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