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