diff options
author | Robert Griesemer <gri@golang.org> | 2020-10-19 15:28:22 -0700 |
---|---|---|
committer | Robert Griesemer <gri@golang.org> | 2020-10-21 00:51:12 +0000 |
commit | ca36ba83ab86b9eb1ddc076f0ebfda648ce31d6b (patch) | |
tree | c061b8a1ffa5064e361e9ba58ea6693fab6ab0e0 /src/cmd/compile/internal/types2/return.go | |
parent | 6ff16fe3ee46f8e35c18226d04bd38a396eb4175 (diff) | |
download | go-ca36ba83ab86b9eb1ddc076f0ebfda648ce31d6b.tar.gz go-ca36ba83ab86b9eb1ddc076f0ebfda648ce31d6b.zip |
[dev.typeparams] cmd/compile/internal/importer, types2: initial check-in of types2 and importer
This is a copy of the importer and types2 (unreviewed) prototype version
excluding the testdata directory containing tests (see below). Each file
is marked with the comment
// UNREVIEWED
on the first line. The plan is to check in this code wholesale (it runs and
passes all tests) and then review the code file-by-file via subsequent CLs
and remove the "// UNREVIEWED" comments as we review the files.
Since most tests are unchanged from the original go/types, the next CL will
commit those tests as they don't need to be reviewed again. (Eventually we
may want to factor them out and share them from a single place, e.g. the
test directory.)
The existing file fmtmap_test.go was updated.
Change-Id: I9bd0ad1a7e7188b501423483a44d18e623c0fe71
Reviewed-on: https://go-review.googlesource.com/c/go/+/263624
Trust: Robert Griesemer <gri@golang.org>
Trust: Keith Randall <khr@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Diffstat (limited to 'src/cmd/compile/internal/types2/return.go')
-rw-r--r-- | src/cmd/compile/internal/types2/return.go | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/types2/return.go b/src/cmd/compile/internal/types2/return.go new file mode 100644 index 0000000000..88234b1723 --- /dev/null +++ b/src/cmd/compile/internal/types2/return.go @@ -0,0 +1,180 @@ +// UNREVIEWED +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements isTerminating. + +package types2 + +import ( + "cmd/compile/internal/syntax" +) + +// isTerminating reports if s is a terminating statement. +// If s is labeled, label is the label name; otherwise s +// is "". +func (check *Checker) isTerminating(s syntax.Stmt, label string) bool { + switch s := s.(type) { + default: + unreachable() + + case *syntax.DeclStmt, *syntax.EmptyStmt, *syntax.SendStmt, + *syntax.AssignStmt, *syntax.CallStmt: + // no chance + + case *syntax.LabeledStmt: + return check.isTerminating(s.Stmt, s.Label.Value) + + case *syntax.ExprStmt: + // calling the predeclared (possibly parenthesized) panic() function is terminating + if call, ok := unparen(s.X).(*syntax.CallExpr); ok && check.isPanic[call] { + return true + } + + case *syntax.ReturnStmt: + return true + + case *syntax.BranchStmt: + if s.Tok == syntax.Goto || s.Tok == syntax.Fallthrough { + return true + } + + case *syntax.BlockStmt: + return check.isTerminatingList(s.List, "") + + case *syntax.IfStmt: + if s.Else != nil && + check.isTerminating(s.Then, "") && + check.isTerminating(s.Else, "") { + return true + } + + case *syntax.SwitchStmt: + return check.isTerminatingSwitch(s.Body, label) + + case *syntax.SelectStmt: + for _, cc := range s.Body { + if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) { + return false + } + + } + return true + + case *syntax.ForStmt: + if s.Cond == nil && !hasBreak(s.Body, label, true) { + return true + } + } + + return false +} + +func (check *Checker) isTerminatingList(list []syntax.Stmt, label string) bool { + // trailing empty statements are permitted - skip them + for i := len(list) - 1; i >= 0; i-- { + if _, ok := list[i].(*syntax.EmptyStmt); !ok { + return check.isTerminating(list[i], label) + } + } + return false // all statements are empty +} + +func (check *Checker) isTerminatingSwitch(body []*syntax.CaseClause, label string) bool { + hasDefault := false + for _, cc := range body { + if cc.Cases == nil { + hasDefault = true + } + if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) { + return false + } + } + return hasDefault +} + +// TODO(gri) For nested breakable statements, the current implementation of hasBreak +// will traverse the same subtree repeatedly, once for each label. Replace +// with a single-pass label/break matching phase. + +// hasBreak reports if s is or contains a break statement +// referring to the label-ed statement or implicit-ly the +// closest outer breakable statement. +func hasBreak(s syntax.Stmt, label string, implicit bool) bool { + switch s := s.(type) { + default: + unreachable() + + case *syntax.DeclStmt, *syntax.EmptyStmt, *syntax.ExprStmt, + *syntax.SendStmt, *syntax.AssignStmt, *syntax.CallStmt, + *syntax.ReturnStmt: + // no chance + + case *syntax.LabeledStmt: + return hasBreak(s.Stmt, label, implicit) + + case *syntax.BranchStmt: + if s.Tok == syntax.Break { + if s.Label == nil { + return implicit + } + if s.Label.Value == label { + return true + } + } + + case *syntax.BlockStmt: + return hasBreakList(s.List, label, implicit) + + case *syntax.IfStmt: + if hasBreak(s.Then, label, implicit) || + s.Else != nil && hasBreak(s.Else, label, implicit) { + return true + } + + case *syntax.SwitchStmt: + if label != "" && hasBreakCaseList(s.Body, label, false) { + return true + } + + case *syntax.SelectStmt: + if label != "" && hasBreakCommList(s.Body, label, false) { + return true + } + + case *syntax.ForStmt: + if label != "" && hasBreak(s.Body, label, false) { + return true + } + } + + return false +} + +func hasBreakList(list []syntax.Stmt, label string, implicit bool) bool { + for _, s := range list { + if hasBreak(s, label, implicit) { + return true + } + } + return false +} + +func hasBreakCaseList(list []*syntax.CaseClause, label string, implicit bool) bool { + for _, s := range list { + if hasBreakList(s.Body, label, implicit) { + return true + } + } + return false +} + +func hasBreakCommList(list []*syntax.CommClause, label string, implicit bool) bool { + for _, s := range list { + if hasBreakList(s.Body, label, implicit) { + return true + } + } + return false +} |