1// Copyright 2013 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
5package types2
6
7import (
8	"cmd/compile/internal/syntax"
9)
10
11// labels checks correct label use in body.
12func (check *Checker) labels(body *syntax.BlockStmt) {
13	// set of all labels in this body
14	all := NewScope(nil, body.Pos(), syntax.EndPos(body), "label")
15
16	fwdJumps := check.blockBranches(all, nil, nil, body.List)
17
18	// If there are any forward jumps left, no label was found for
19	// the corresponding goto statements. Either those labels were
20	// never defined, or they are inside blocks and not reachable
21	// for the respective gotos.
22	for _, jmp := range fwdJumps {
23		var msg string
24		name := jmp.Label.Value
25		if alt := all.Lookup(name); alt != nil {
26			msg = "goto %s jumps into block"
27			alt.(*Label).used = true // avoid another error
28		} else {
29			msg = "label %s not declared"
30		}
31		check.errorf(jmp.Label, msg, name)
32	}
33
34	// spec: "It is illegal to define a label that is never used."
35	for name, obj := range all.elems {
36		obj = resolve(name, obj)
37		if lbl := obj.(*Label); !lbl.used {
38			check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name)
39		}
40	}
41}
42
43// A block tracks label declarations in a block and its enclosing blocks.
44type block struct {
45	parent *block                         // enclosing block
46	lstmt  *syntax.LabeledStmt            // labeled statement to which this block belongs, or nil
47	labels map[string]*syntax.LabeledStmt // allocated lazily
48}
49
50// insert records a new label declaration for the current block.
51// The label must not have been declared before in any block.
52func (b *block) insert(s *syntax.LabeledStmt) {
53	name := s.Label.Value
54	if debug {
55		assert(b.gotoTarget(name) == nil)
56	}
57	labels := b.labels
58	if labels == nil {
59		labels = make(map[string]*syntax.LabeledStmt)
60		b.labels = labels
61	}
62	labels[name] = s
63}
64
65// gotoTarget returns the labeled statement in the current
66// or an enclosing block with the given label name, or nil.
67func (b *block) gotoTarget(name string) *syntax.LabeledStmt {
68	for s := b; s != nil; s = s.parent {
69		if t := s.labels[name]; t != nil {
70			return t
71		}
72	}
73	return nil
74}
75
76// enclosingTarget returns the innermost enclosing labeled
77// statement with the given label name, or nil.
78func (b *block) enclosingTarget(name string) *syntax.LabeledStmt {
79	for s := b; s != nil; s = s.parent {
80		if t := s.lstmt; t != nil && t.Label.Value == name {
81			return t
82		}
83	}
84	return nil
85}
86
87// blockBranches processes a block's statement list and returns the set of outgoing forward jumps.
88// all is the scope of all declared labels, parent the set of labels declared in the immediately
89// enclosing block, and lstmt is the labeled statement this block is associated with (or nil).
90func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *syntax.LabeledStmt, list []syntax.Stmt) []*syntax.BranchStmt {
91	b := &block{parent, lstmt, nil}
92
93	var (
94		varDeclPos         syntax.Pos
95		fwdJumps, badJumps []*syntax.BranchStmt
96	)
97
98	// All forward jumps jumping over a variable declaration are possibly
99	// invalid (they may still jump out of the block and be ok).
100	// recordVarDecl records them for the given position.
101	recordVarDecl := func(pos syntax.Pos) {
102		varDeclPos = pos
103		badJumps = append(badJumps[:0], fwdJumps...) // copy fwdJumps to badJumps
104	}
105
106	jumpsOverVarDecl := func(jmp *syntax.BranchStmt) bool {
107		if varDeclPos.IsKnown() {
108			for _, bad := range badJumps {
109				if jmp == bad {
110					return true
111				}
112			}
113		}
114		return false
115	}
116
117	var stmtBranches func(syntax.Stmt)
118	stmtBranches = func(s syntax.Stmt) {
119		switch s := s.(type) {
120		case *syntax.DeclStmt:
121			for _, d := range s.DeclList {
122				if d, _ := d.(*syntax.VarDecl); d != nil {
123					recordVarDecl(d.Pos())
124				}
125			}
126
127		case *syntax.LabeledStmt:
128			// declare non-blank label
129			if name := s.Label.Value; name != "_" {
130				lbl := NewLabel(s.Label.Pos(), check.pkg, name)
131				if alt := all.Insert(lbl); alt != nil {
132					var err error_
133					err.soft = true
134					err.errorf(lbl.pos, "label %s already declared", name)
135					err.recordAltDecl(alt)
136					check.report(&err)
137					// ok to continue
138				} else {
139					b.insert(s)
140					check.recordDef(s.Label, lbl)
141				}
142				// resolve matching forward jumps and remove them from fwdJumps
143				i := 0
144				for _, jmp := range fwdJumps {
145					if jmp.Label.Value == name {
146						// match
147						lbl.used = true
148						check.recordUse(jmp.Label, lbl)
149						if jumpsOverVarDecl(jmp) {
150							check.softErrorf(
151								jmp.Label,
152								"goto %s jumps over variable declaration at line %d",
153								name,
154								varDeclPos.Line(),
155							)
156							// ok to continue
157						}
158					} else {
159						// no match - record new forward jump
160						fwdJumps[i] = jmp
161						i++
162					}
163				}
164				fwdJumps = fwdJumps[:i]
165				lstmt = s
166			}
167			stmtBranches(s.Stmt)
168
169		case *syntax.BranchStmt:
170			if s.Label == nil {
171				return // checked in 1st pass (check.stmt)
172			}
173
174			// determine and validate target
175			name := s.Label.Value
176			switch s.Tok {
177			case syntax.Break:
178				// spec: "If there is a label, it must be that of an enclosing
179				// "for", "switch", or "select" statement, and that is the one
180				// whose execution terminates."
181				valid := false
182				if t := b.enclosingTarget(name); t != nil {
183					switch t.Stmt.(type) {
184					case *syntax.SwitchStmt, *syntax.SelectStmt, *syntax.ForStmt:
185						valid = true
186					}
187				}
188				if !valid {
189					check.errorf(s.Label, "invalid break label %s", name)
190					return
191				}
192
193			case syntax.Continue:
194				// spec: "If there is a label, it must be that of an enclosing
195				// "for" statement, and that is the one whose execution advances."
196				valid := false
197				if t := b.enclosingTarget(name); t != nil {
198					switch t.Stmt.(type) {
199					case *syntax.ForStmt:
200						valid = true
201					}
202				}
203				if !valid {
204					check.errorf(s.Label, "invalid continue label %s", name)
205					return
206				}
207
208			case syntax.Goto:
209				if b.gotoTarget(name) == nil {
210					// label may be declared later - add branch to forward jumps
211					fwdJumps = append(fwdJumps, s)
212					return
213				}
214
215			default:
216				check.errorf(s, invalidAST+"branch statement: %s %s", s.Tok, name)
217				return
218			}
219
220			// record label use
221			obj := all.Lookup(name)
222			obj.(*Label).used = true
223			check.recordUse(s.Label, obj)
224
225		case *syntax.AssignStmt:
226			if s.Op == syntax.Def {
227				recordVarDecl(s.Pos())
228			}
229
230		case *syntax.BlockStmt:
231			// Unresolved forward jumps inside the nested block
232			// become forward jumps in the current block.
233			fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, s.List)...)
234
235		case *syntax.IfStmt:
236			stmtBranches(s.Then)
237			if s.Else != nil {
238				stmtBranches(s.Else)
239			}
240
241		case *syntax.SwitchStmt:
242			b := &block{b, lstmt, nil}
243			for _, s := range s.Body {
244				fwdJumps = append(fwdJumps, check.blockBranches(all, b, nil, s.Body)...)
245			}
246
247		case *syntax.SelectStmt:
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.ForStmt:
254			stmtBranches(s.Body)
255		}
256	}
257
258	for _, s := range list {
259		stmtBranches(s)
260	}
261
262	return fwdJumps
263}
264