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  }