github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/cmd/compile/internal/gc/checkcfg.go (about) 1 // Copyright 2017 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 5 package gc 6 7 import ( 8 "cmd/compile/internal/types" 9 "cmd/internal/src" 10 ) 11 12 // checkcontrolflow checks fn's control flow structures for correctness. 13 // It catches: 14 // * misplaced breaks and continues 15 // * bad labeled break and continues 16 // * invalid, unused, duplicate, and missing labels 17 // * gotos jumping over declarations and into blocks 18 func checkcontrolflow(fn *Node) { 19 c := controlflow{ 20 labels: make(map[string]*cfLabel), 21 labeledNodes: make(map[*Node]*cfLabel), 22 } 23 c.pushPos(fn.Pos) 24 c.stmtList(fn.Nbody) 25 26 // Check that we used all labels. 27 for name, lab := range c.labels { 28 if !lab.used() && !lab.reported && !lab.defNode.Used() { 29 yyerrorl(lab.defNode.Pos, "label %v defined and not used", name) 30 lab.reported = true 31 } 32 if lab.used() && !lab.defined() && !lab.reported { 33 yyerrorl(lab.useNode.Pos, "label %v not defined", name) 34 lab.reported = true 35 } 36 } 37 38 // Check any forward gotos. Non-forward gotos have already been checked. 39 for _, n := range c.fwdGotos { 40 lab := c.labels[n.Left.Sym.Name] 41 // If the label is undefined, we have already have printed an error. 42 if lab.defined() { 43 c.checkgoto(n, lab.defNode) 44 } 45 } 46 } 47 48 type controlflow struct { 49 // Labels and labeled control flow nodes (OFOR, OFORUNTIL, OSWITCH, OSELECT) in f. 50 labels map[string]*cfLabel 51 labeledNodes map[*Node]*cfLabel 52 53 // Gotos that jump forward; required for deferred checkgoto calls. 54 fwdGotos []*Node 55 56 // Breaks are allowed in loops, switches, and selects. 57 allowBreak bool 58 // Continues are allowed only in loops. 59 allowContinue bool 60 61 // Position stack. The current position is top of stack. 62 pos []src.XPos 63 } 64 65 // cfLabel is a label tracked by a controlflow. 66 type cfLabel struct { 67 ctlNode *Node // associated labeled control flow node 68 defNode *Node // label definition Node (OLABEL) 69 // Label use Node (OGOTO, OBREAK, OCONTINUE). 70 // There might be multiple uses, but we only need to track one. 71 useNode *Node 72 reported bool // reported indicates whether an error has already been reported for this label 73 } 74 75 // defined reports whether the label has a definition (OLABEL node). 76 func (l *cfLabel) defined() bool { return l.defNode != nil } 77 78 // used reports whether the label has a use (OGOTO, OBREAK, or OCONTINUE node). 79 func (l *cfLabel) used() bool { return l.useNode != nil } 80 81 // label returns the label associated with sym, creating it if necessary. 82 func (c *controlflow) label(sym *types.Sym) *cfLabel { 83 lab := c.labels[sym.Name] 84 if lab == nil { 85 lab = new(cfLabel) 86 c.labels[sym.Name] = lab 87 } 88 return lab 89 } 90 91 // stmtList checks l. 92 func (c *controlflow) stmtList(l Nodes) { 93 for _, n := range l.Slice() { 94 c.stmt(n) 95 } 96 } 97 98 // stmt checks n. 99 func (c *controlflow) stmt(n *Node) { 100 c.pushPos(n.Pos) 101 defer c.popPos() 102 c.stmtList(n.Ninit) 103 104 checkedNbody := false 105 106 switch n.Op { 107 case OLABEL: 108 sym := n.Left.Sym 109 lab := c.label(sym) 110 // Associate label with its control flow node, if any 111 if ctl := n.labeledControl(); ctl != nil { 112 c.labeledNodes[ctl] = lab 113 } 114 115 if !lab.defined() { 116 lab.defNode = n 117 } else { 118 c.err("label %v already defined at %v", sym, linestr(lab.defNode.Pos)) 119 lab.reported = true 120 } 121 122 case OGOTO: 123 lab := c.label(n.Left.Sym) 124 if !lab.used() { 125 lab.useNode = n 126 } 127 if lab.defined() { 128 c.checkgoto(n, lab.defNode) 129 } else { 130 c.fwdGotos = append(c.fwdGotos, n) 131 } 132 133 case OCONTINUE, OBREAK: 134 if n.Left == nil { 135 // plain break/continue 136 if n.Op == OCONTINUE && !c.allowContinue { 137 c.err("%v is not in a loop", n.Op) 138 } else if !c.allowBreak { 139 c.err("%v is not in a loop, switch, or select", n.Op) 140 } 141 break 142 } 143 144 // labeled break/continue; look up the target 145 sym := n.Left.Sym 146 lab := c.label(sym) 147 if !lab.used() { 148 lab.useNode = n.Left 149 } 150 if !lab.defined() { 151 c.err("%v label not defined: %v", n.Op, sym) 152 lab.reported = true 153 break 154 } 155 ctl := lab.ctlNode 156 if n.Op == OCONTINUE && ctl != nil && (ctl.Op == OSWITCH || ctl.Op == OSELECT) { 157 // Cannot continue in a switch or select. 158 ctl = nil 159 } 160 if ctl == nil { 161 // Valid label but not usable with a break/continue here, e.g.: 162 // for { 163 // continue abc 164 // } 165 // abc: 166 // for {} 167 c.err("invalid %v label %v", n.Op, sym) 168 lab.reported = true 169 } 170 171 case OFOR, OFORUNTIL, OSWITCH, OSELECT: 172 // set up for continue/break in body 173 allowBreak := c.allowBreak 174 allowContinue := c.allowContinue 175 c.allowBreak = true 176 switch n.Op { 177 case OFOR, OFORUNTIL: 178 c.allowContinue = true 179 } 180 lab := c.labeledNodes[n] 181 if lab != nil { 182 // labeled for loop 183 lab.ctlNode = n 184 } 185 186 // check body 187 c.stmtList(n.Nbody) 188 checkedNbody = true 189 190 // tear down continue/break 191 c.allowBreak = allowBreak 192 c.allowContinue = allowContinue 193 if lab != nil { 194 lab.ctlNode = nil 195 } 196 } 197 198 if !checkedNbody { 199 c.stmtList(n.Nbody) 200 } 201 c.stmtList(n.List) 202 c.stmtList(n.Rlist) 203 } 204 205 // pushPos pushes a position onto the position stack. 206 func (c *controlflow) pushPos(pos src.XPos) { 207 if !pos.IsKnown() { 208 pos = c.peekPos() 209 if Debug['K'] != 0 { 210 Warn("controlflow: unknown position") 211 } 212 } 213 c.pos = append(c.pos, pos) 214 } 215 216 // popLine pops the top of the position stack. 217 func (c *controlflow) popPos() { c.pos = c.pos[:len(c.pos)-1] } 218 219 // peekPos peeks at the top of the position stack. 220 func (c *controlflow) peekPos() src.XPos { return c.pos[len(c.pos)-1] } 221 222 // err reports a control flow error at the current position. 223 func (c *controlflow) err(msg string, args ...interface{}) { 224 yyerrorl(c.peekPos(), msg, args...) 225 } 226 227 // checkgoto checks that a goto from from to to does not 228 // jump into a block or jump over variable declarations. 229 func (c *controlflow) checkgoto(from *Node, to *Node) { 230 if from.Op != OGOTO || to.Op != OLABEL { 231 Fatalf("bad from/to in checkgoto: %v -> %v", from, to) 232 } 233 234 // from and to's Sym fields record dclstack's value at their 235 // position, which implicitly encodes their block nesting 236 // level and variable declaration position within that block. 237 // 238 // For valid gotos, to.Sym will be a tail of from.Sym. 239 // Otherwise, any link in to.Sym not also in from.Sym 240 // indicates a block/declaration being jumped into/over. 241 // 242 // TODO(mdempsky): We should only complain about jumping over 243 // variable declarations, but currently we reject type and 244 // constant declarations too (#8042). 245 246 if from.Sym == to.Sym { 247 return 248 } 249 250 nf := dcldepth(from.Sym) 251 nt := dcldepth(to.Sym) 252 253 // Unwind from.Sym so it's no longer than to.Sym. It's okay to 254 // jump out of blocks or backwards past variable declarations. 255 fs := from.Sym 256 for ; nf > nt; nf-- { 257 fs = fs.Link 258 } 259 260 if fs == to.Sym { 261 return 262 } 263 264 // Decide what to complain about. Unwind to.Sym until where it 265 // forked from from.Sym, and keep track of the innermost block 266 // and declaration we jumped into/over. 267 var block *types.Sym 268 var dcl *types.Sym 269 270 // If to.Sym is longer, unwind until it's the same length. 271 ts := to.Sym 272 for ; nt > nf; nt-- { 273 if ts.Pkg == nil { 274 block = ts 275 } else { 276 dcl = ts 277 } 278 ts = ts.Link 279 } 280 281 // Same length; unwind until we find their common ancestor. 282 for ts != fs { 283 if ts.Pkg == nil { 284 block = ts 285 } else { 286 dcl = ts 287 } 288 ts = ts.Link 289 fs = fs.Link 290 } 291 292 // Prefer to complain about 'into block' over declarations. 293 pos := from.Left.Pos 294 if block != nil { 295 yyerrorl(pos, "goto %v jumps into block starting at %v", from.Left.Sym, linestr(block.Lastlineno)) 296 } else { 297 yyerrorl(pos, "goto %v jumps over declaration of %v at %v", from.Left.Sym, dcl, linestr(dcl.Lastlineno)) 298 } 299 } 300 301 // dcldepth returns the declaration depth for a dclstack Sym; that is, 302 // the sum of the block nesting level and the number of declarations 303 // in scope. 304 func dcldepth(s *types.Sym) int { 305 n := 0 306 for ; s != nil; s = s.Link { 307 n++ 308 } 309 return n 310 }