aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/syntax/branches.go
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2017-04-18 20:44:54 -0700
committerRobert Griesemer <gri@golang.org>2017-04-21 22:30:55 +0000
commita50962131acb7def8241cd5e78b99746c6e52771 (patch)
tree0f4e9cb584519ed4c2a0526613b0201051e5187c /src/cmd/compile/internal/syntax/branches.go
parent7b0b52ef2b2b86eef3b73859ceaa986c8c0e47f7 (diff)
downloadgo-a50962131acb7def8241cd5e78b99746c6e52771.tar.gz
go-a50962131acb7def8241cd5e78b99746c6e52771.zip
cmd/compile/internal/syntax: compute BranchStmt.Target statements
- Add new BranchStmt.Target field: It's the destination for break, continue, or goto statements. - When parsing with CheckBranches enabled, set the BranchStmt.Target field. We get the information practically for free from the branch checker, so keep it for further use. - Fix a couple of comments. - This could use a test, but the new Target field is currently not used, and writing a test is tedious w/o a general tree visitor. Do it later. For now, visually verified output from syntax dump. Change-Id: Id691d89efab514ad885e19ac9759506106579520 Reviewed-on: https://go-review.googlesource.com/40988 Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Diffstat (limited to 'src/cmd/compile/internal/syntax/branches.go')
-rw-r--r--src/cmd/compile/internal/syntax/branches.go70
1 files changed, 40 insertions, 30 deletions
diff --git a/src/cmd/compile/internal/syntax/branches.go b/src/cmd/compile/internal/syntax/branches.go
index 9d21f93d5c..5fecdd6551 100644
--- a/src/cmd/compile/internal/syntax/branches.go
+++ b/src/cmd/compile/internal/syntax/branches.go
@@ -9,7 +9,7 @@ import (
"fmt"
)
-// TODO(gri) do this while parsing instead of in a separate pass?
+// TODO(gri) consider making this part of the parser code
// checkBranches checks correct use of labels and branch
// statements (break, continue, goto) in a function body.
@@ -25,7 +25,7 @@ func checkBranches(body *BlockStmt, errh ErrorHandler) {
// scope of all labels in this body
ls := &labelScope{errh: errh}
- fwdGotos := ls.blockBranches(nil, 0, nil, body.Pos(), body.List)
+ fwdGotos := ls.blockBranches(nil, targets{}, nil, body.Pos(), body.List)
// If there are any forward gotos left, no matching label was
// found for them. Either those labels were never defined, or
@@ -91,12 +91,12 @@ func (ls *labelScope) declare(b *block, s *LabeledStmt) *label {
// gotoTarget returns the labeled statement matching the given name and
// declared in block b or any of its enclosing blocks. The result is nil
// if the label is not defined, or doesn't match a valid labeled statement.
-func (ls *labelScope) gotoTarget(b *block, name string) *label {
+func (ls *labelScope) gotoTarget(b *block, name string) *LabeledStmt {
if l := ls.labels[name]; l != nil {
l.used = true // even if it's not a valid target
for ; b != nil; b = b.parent {
if l.parent == b {
- return l
+ return l.lstmt
}
}
}
@@ -121,17 +121,18 @@ func (ls *labelScope) enclosingTarget(b *block, name string) *LabeledStmt {
return nil
}
-// context flags
-const (
- breakOk = 1 << iota
- continueOk
-)
+// targets describes the target statements within which break
+// or continue statements are valid.
+type targets struct {
+ breaks Stmt // *ForStmt, *SwitchStmt, *SelectStmt, or nil
+ continues *ForStmt // or nil
+}
// blockBranches processes a block's body starting at start and returns the
// list of unresolved (forward) gotos. parent is the immediately enclosing
-// block (or nil), context provides information about the enclosing statements,
+// block (or nil), ctxt provides information about the enclosing statements,
// and lstmt is the labeled statement asociated with this block, or nil.
-func (ls *labelScope) blockBranches(parent *block, context uint, lstmt *LabeledStmt, start src.Pos, body []Stmt) []*BranchStmt {
+func (ls *labelScope) blockBranches(parent *block, ctxt targets, lstmt *LabeledStmt, start src.Pos, body []Stmt) []*BranchStmt {
b := &block{parent: parent, start: start, lstmt: lstmt}
var varPos src.Pos
@@ -159,8 +160,10 @@ func (ls *labelScope) blockBranches(parent *block, context uint, lstmt *LabeledS
return false
}
- innerBlock := func(flags uint, start src.Pos, body []Stmt) {
- fwdGotos = append(fwdGotos, ls.blockBranches(b, context|flags, lstmt, start, body)...)
+ innerBlock := func(ctxt targets, start src.Pos, body []Stmt) {
+ // Unresolved forward gotos from the inner block
+ // become forward gotos for the current block.
+ fwdGotos = append(fwdGotos, ls.blockBranches(b, ctxt, lstmt, start, body)...)
}
for _, stmt := range body {
@@ -183,6 +186,7 @@ func (ls *labelScope) blockBranches(parent *block, context uint, lstmt *LabeledS
i := 0
for _, fwd := range fwdGotos {
if fwd.Label.Value == name {
+ fwd.Target = s
l.used = true
if jumpsOverVarDecl(fwd) {
ls.err(
@@ -209,11 +213,15 @@ func (ls *labelScope) blockBranches(parent *block, context uint, lstmt *LabeledS
if s.Label == nil {
switch s.Tok {
case _Break:
- if context&breakOk == 0 {
+ if t := ctxt.breaks; t != nil {
+ s.Target = t
+ } else {
ls.err(s.Pos(), "break is not in a loop, switch, or select")
}
case _Continue:
- if context&continueOk == 0 {
+ if t := ctxt.continues; t != nil {
+ s.Target = t
+ } else {
ls.err(s.Pos(), "continue is not in a loop")
}
case _Fallthrough:
@@ -234,12 +242,10 @@ func (ls *labelScope) blockBranches(parent *block, context uint, lstmt *LabeledS
// "for", "switch", or "select" statement, and that is the one
// whose execution terminates."
if t := ls.enclosingTarget(b, name); t != nil {
- valid := false
- switch t.Stmt.(type) {
+ switch t := t.Stmt.(type) {
case *SwitchStmt, *SelectStmt, *ForStmt:
- valid = true
- }
- if !valid {
+ s.Target = t
+ default:
ls.err(s.Label.Pos(), "invalid break label %s", name)
}
} else {
@@ -250,7 +256,9 @@ func (ls *labelScope) blockBranches(parent *block, context uint, lstmt *LabeledS
// spec: "If there is a label, it must be that of an enclosing
// "for" statement, and that is the one whose execution advances."
if t := ls.enclosingTarget(b, name); t != nil {
- if _, ok := t.Stmt.(*ForStmt); !ok {
+ if t, ok := t.Stmt.(*ForStmt); ok {
+ s.Target = t
+ } else {
ls.err(s.Label.Pos(), "invalid continue label %s", name)
}
} else {
@@ -258,7 +266,9 @@ func (ls *labelScope) blockBranches(parent *block, context uint, lstmt *LabeledS
}
case _Goto:
- if ls.gotoTarget(b, name) == nil {
+ if t := ls.gotoTarget(b, name); t != nil {
+ s.Target = t
+ } else {
// label may be declared later - add goto to forward gotos
fwdGotos = append(fwdGotos, s)
}
@@ -275,27 +285,27 @@ func (ls *labelScope) blockBranches(parent *block, context uint, lstmt *LabeledS
}
case *BlockStmt:
- // Unresolved forward gotos from the nested block
- // become forward gotos for the current block.
- innerBlock(0, s.Pos(), s.List)
+ innerBlock(ctxt, s.Pos(), s.List)
case *IfStmt:
- innerBlock(0, s.Then.Pos(), s.Then.List)
+ innerBlock(ctxt, s.Then.Pos(), s.Then.List)
if s.Else != nil {
- innerBlock(0, s.Else.Pos(), []Stmt{s.Else})
+ innerBlock(ctxt, s.Else.Pos(), []Stmt{s.Else})
}
case *ForStmt:
- innerBlock(breakOk|continueOk, s.Body.Pos(), s.Body.List)
+ innerBlock(targets{s, s}, s.Body.Pos(), s.Body.List)
case *SwitchStmt:
+ inner := targets{s, ctxt.continues}
for _, cc := range s.Body {
- innerBlock(breakOk, cc.Pos(), cc.Body)
+ innerBlock(inner, cc.Pos(), cc.Body)
}
case *SelectStmt:
+ inner := targets{s, ctxt.continues}
for _, cc := range s.Body {
- innerBlock(breakOk, cc.Pos(), cc.Body)
+ innerBlock(inner, cc.Pos(), cc.Body)
}
}
}