aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/types2/return.go
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2020-10-19 15:28:22 -0700
committerRobert Griesemer <gri@golang.org>2020-10-21 00:51:12 +0000
commitca36ba83ab86b9eb1ddc076f0ebfda648ce31d6b (patch)
treec061b8a1ffa5064e361e9ba58ea6693fab6ab0e0 /src/cmd/compile/internal/types2/return.go
parent6ff16fe3ee46f8e35c18226d04bd38a396eb4175 (diff)
downloadgo-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.go180
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
+}