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  )
    10  
    11  // labels checks correct label use in body.
    12  func (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.
    44  type 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.
    52  func (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.
    67  func (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.
    78  func (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).
    90  func (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  

View as plain text