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 code = JumpIntoBlock
30 alt.(*Label).used = true
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 err := check.newError(DuplicateLabel)
137 err.soft = true
138 err.addf(lbl.pos, "label %s already declared", name)
139 err.addAltDecl(alt)
140 err.report()
141
142 } else {
143 b.insert(s)
144 check.recordDef(s.Label, lbl)
145 }
146
147 i := 0
148 for _, jmp := range fwdJumps {
149 if jmp.Label.Value == name {
150
151 lbl.used = true
152 check.recordUse(jmp.Label, lbl)
153 if jumpsOverVarDecl(jmp) {
154 check.softErrorf(
155 jmp.Label,
156 JumpOverDecl,
157 "goto %s jumps over variable declaration at line %d",
158 name,
159 varDeclPos.Line(),
160 )
161
162 }
163 } else {
164
165 fwdJumps[i] = jmp
166 i++
167 }
168 }
169 fwdJumps = fwdJumps[:i]
170 lstmt = s
171 }
172 stmtBranches(s.Stmt)
173
174 case *syntax.BranchStmt:
175 if s.Label == nil {
176 return
177 }
178
179
180 name := s.Label.Value
181 switch s.Tok {
182 case syntax.Break:
183
184
185
186 valid := false
187 if t := b.enclosingTarget(name); t != nil {
188 switch t.Stmt.(type) {
189 case *syntax.SwitchStmt, *syntax.SelectStmt, *syntax.ForStmt:
190 valid = true
191 }
192 }
193 if !valid {
194 check.errorf(s.Label, MisplacedLabel, "invalid break label %s", name)
195 return
196 }
197
198 case syntax.Continue:
199
200
201 valid := false
202 if t := b.enclosingTarget(name); t != nil {
203 switch t.Stmt.(type) {
204 case *syntax.ForStmt:
205 valid = true
206 }
207 }
208 if !valid {
209 check.errorf(s.Label, MisplacedLabel, "invalid continue label %s", name)
210 return
211 }
212
213 case syntax.Goto:
214 if b.gotoTarget(name) == nil {
215
216 fwdJumps = append(fwdJumps, s)
217 return
218 }
219
220 default:
221 check.errorf(s, InvalidSyntaxTree, "branch statement: %s %s", s.Tok, name)
222 return
223 }
224
225
226 obj := all.Lookup(name)
227 obj.(*Label).used = true
228 check.recordUse(s.Label, obj)
229
230 case *syntax.AssignStmt:
231 if s.Op == syntax.Def {
232 recordVarDecl(s.Pos())
233 }
234
235 case *syntax.BlockStmt:
236
237
238 fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, s.List)...)
239
240 case *syntax.IfStmt:
241 stmtBranches(s.Then)
242 if s.Else != nil {
243 stmtBranches(s.Else)
244 }
245
246 case *syntax.SwitchStmt:
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.SelectStmt:
253 b := &block{b, lstmt, nil}
254 for _, s := range s.Body {
255 fwdJumps = append(fwdJumps, check.blockBranches(all, b, nil, s.Body)...)
256 }
257
258 case *syntax.ForStmt:
259 stmtBranches(s.Body)
260 }
261 }
262
263 for _, s := range list {
264 stmtBranches(s)
265 }
266
267 return fwdJumps
268 }
269
View as plain text