Source file src/cmd/compile/internal/types2/labels.go

     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  
     5  package types2
     6  
     7  import (
     8  	"cmd/compile/internal/syntax"
     9  	. "internal/types/errors"
    10  )
    11  
    12  // labels checks correct label use in body.
    13  func (check *Checker) labels(body *syntax.BlockStmt) {
    14  	// set of all labels in this body
    15  	all := NewScope(nil, body.Pos(), syntax.EndPos(body), "label")
    16  
    17  	fwdJumps := check.blockBranches(all, nil, nil, body.List)
    18  
    19  	// If there are any forward jumps left, no label was found for
    20  	// the corresponding goto statements. Either those labels were
    21  	// never defined, or they are inside blocks and not reachable
    22  	// for the respective gotos.
    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  			alt.(*Label).used = true // avoid another error
    30  			code = JumpIntoBlock
    31  		} else {
    32  			msg = "label %s not declared"
    33  			code = UndeclaredLabel
    34  		}
    35  		check.errorf(jmp.Label, code, msg, name)
    36  	}
    37  
    38  	// spec: "It is illegal to define a label that is never used."
    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  // A block tracks label declarations in a block and its enclosing blocks.
    48  type block struct {
    49  	parent *block                         // enclosing block
    50  	lstmt  *syntax.LabeledStmt            // labeled statement to which this block belongs, or nil
    51  	labels map[string]*syntax.LabeledStmt // allocated lazily
    52  }
    53  
    54  // insert records a new label declaration for the current block.
    55  // The label must not have been declared before in any block.
    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  // gotoTarget returns the labeled statement in the current
    70  // or an enclosing block with the given label name, or nil.
    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  // enclosingTarget returns the innermost enclosing labeled
    81  // statement with the given label name, or nil.
    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  // blockBranches processes a block's statement list and returns the set of outgoing forward jumps.
    92  // all is the scope of all declared labels, parent the set of labels declared in the immediately
    93  // enclosing block, and lstmt is the labeled statement this block is associated with (or nil).
    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  	// All forward jumps jumping over a variable declaration are possibly
   103  	// invalid (they may still jump out of the block and be ok).
   104  	// recordVarDecl records them for the given position.
   105  	recordVarDecl := func(pos syntax.Pos) {
   106  		varDeclPos = pos
   107  		badJumps = append(badJumps[:0], fwdJumps...) // copy fwdJumps to badJumps
   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  			// declare non-blank label
   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  					var err error_
   137  					err.code = DuplicateLabel
   138  					err.soft = true
   139  					err.errorf(lbl.pos, "label %s already declared", name)
   140  					err.recordAltDecl(alt)
   141  					check.report(&err)
   142  					// ok to continue
   143  				} else {
   144  					b.insert(s)
   145  					check.recordDef(s.Label, lbl)
   146  				}
   147  				// resolve matching forward jumps and remove them from fwdJumps
   148  				i := 0
   149  				for _, jmp := range fwdJumps {
   150  					if jmp.Label.Value == name {
   151  						// match
   152  						lbl.used = true
   153  						check.recordUse(jmp.Label, lbl)
   154  						if jumpsOverVarDecl(jmp) {
   155  							check.softErrorf(
   156  								jmp.Label,
   157  								JumpOverDecl,
   158  								"goto %s jumps over variable declaration at line %d",
   159  								name,
   160  								varDeclPos.Line(),
   161  							)
   162  							// ok to continue
   163  						}
   164  					} else {
   165  						// no match - record new forward jump
   166  						fwdJumps[i] = jmp
   167  						i++
   168  					}
   169  				}
   170  				fwdJumps = fwdJumps[:i]
   171  				lstmt = s
   172  			}
   173  			stmtBranches(s.Stmt)
   174  
   175  		case *syntax.BranchStmt:
   176  			if s.Label == nil {
   177  				return // checked in 1st pass (check.stmt)
   178  			}
   179  
   180  			// determine and validate target
   181  			name := s.Label.Value
   182  			switch s.Tok {
   183  			case syntax.Break:
   184  				// spec: "If there is a label, it must be that of an enclosing
   185  				// "for", "switch", or "select" statement, and that is the one
   186  				// whose execution terminates."
   187  				valid := false
   188  				if t := b.enclosingTarget(name); t != nil {
   189  					switch t.Stmt.(type) {
   190  					case *syntax.SwitchStmt, *syntax.SelectStmt, *syntax.ForStmt:
   191  						valid = true
   192  					}
   193  				}
   194  				if !valid {
   195  					check.errorf(s.Label, MisplacedLabel, "invalid break label %s", name)
   196  					return
   197  				}
   198  
   199  			case syntax.Continue:
   200  				// spec: "If there is a label, it must be that of an enclosing
   201  				// "for" statement, and that is the one whose execution advances."
   202  				valid := false
   203  				if t := b.enclosingTarget(name); t != nil {
   204  					switch t.Stmt.(type) {
   205  					case *syntax.ForStmt:
   206  						valid = true
   207  					}
   208  				}
   209  				if !valid {
   210  					check.errorf(s.Label, MisplacedLabel, "invalid continue label %s", name)
   211  					return
   212  				}
   213  
   214  			case syntax.Goto:
   215  				if b.gotoTarget(name) == nil {
   216  					// label may be declared later - add branch to forward jumps
   217  					fwdJumps = append(fwdJumps, s)
   218  					return
   219  				}
   220  
   221  			default:
   222  				check.errorf(s, InvalidSyntaxTree, "branch statement: %s %s", s.Tok, name)
   223  				return
   224  			}
   225  
   226  			// record label use
   227  			obj := all.Lookup(name)
   228  			obj.(*Label).used = true
   229  			check.recordUse(s.Label, obj)
   230  
   231  		case *syntax.AssignStmt:
   232  			if s.Op == syntax.Def {
   233  				recordVarDecl(s.Pos())
   234  			}
   235  
   236  		case *syntax.BlockStmt:
   237  			// Unresolved forward jumps inside the nested block
   238  			// become forward jumps in the current block.
   239  			fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, s.List)...)
   240  
   241  		case *syntax.IfStmt:
   242  			stmtBranches(s.Then)
   243  			if s.Else != nil {
   244  				stmtBranches(s.Else)
   245  			}
   246  
   247  		case *syntax.SwitchStmt:
   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.SelectStmt:
   254  			b := &block{b, lstmt, nil}
   255  			for _, s := range s.Body {
   256  				fwdJumps = append(fwdJumps, check.blockBranches(all, b, nil, s.Body)...)
   257  			}
   258  
   259  		case *syntax.ForStmt:
   260  			stmtBranches(s.Body)
   261  		}
   262  	}
   263  
   264  	for _, s := range list {
   265  		stmtBranches(s)
   266  	}
   267  
   268  	return fwdJumps
   269  }
   270  

View as plain text