]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile: move duplicate type-case checking into typecheck
authorMatthew Dempsky <mdempsky@google.com>
Wed, 11 Sep 2019 03:06:51 +0000 (20:06 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Wed, 11 Sep 2019 23:33:11 +0000 (23:33 +0000)
Part of the general trend of moving yyerror calls out of walk and into
typecheck.

Notably, this requires splitting test/typeswitch2.go into two files,
because now some of the errors are reported during typecheck and
others are still reported during walk; and if there were any errors
during typecheck, then cmd/compile exits without invoking walk.

Passes toolstash-check.

Change-Id: I05ee0c00b99af659ee1eef098d342d0d736cf31e
Reviewed-on: https://go-review.googlesource.com/c/go/+/194659
Reviewed-by: Robert Griesemer <gri@golang.org>
src/cmd/compile/internal/gc/swt.go
test/typeswitch2.go
test/typeswitch2b.go [new file with mode: 0644]

index 40c0ea19628e35445263add498211d69f353618d..5089efe08bf4f8407f2305bb5651b8659ae8abdc 100644 (file)
@@ -6,6 +6,7 @@ package gc
 
 import (
        "cmd/compile/internal/types"
+       "cmd/internal/src"
        "sort"
 )
 
@@ -80,6 +81,7 @@ func typecheckTypeSwitch(n *Node) {
        }
 
        var defCase, nilCase *Node
+       var ts typeSet
        for _, ncase := range n.List.Slice() {
                ls := ncase.List.Slice()
                if len(ls) == 0 { // default:
@@ -120,6 +122,10 @@ func typecheckTypeSwitch(n *Node) {
                                                " (missing %v method)", n.Left.Right, n1.Type, missing.Sym)
                                }
                        }
+
+                       if n1.Op == OTYPE {
+                               ts.add(ncase.Pos, n1.Type)
+                       }
                }
 
                if ncase.Rlist.Len() != 0 {
@@ -151,6 +157,34 @@ func typecheckTypeSwitch(n *Node) {
        }
 }
 
+type typeSet struct {
+       m map[string][]typeSetEntry
+}
+
+type typeSetEntry struct {
+       pos src.XPos
+       typ *types.Type
+}
+
+func (s *typeSet) add(pos src.XPos, typ *types.Type) {
+       if s.m == nil {
+               s.m = make(map[string][]typeSetEntry)
+       }
+
+       // LongString does not uniquely identify types, so we need to
+       // disambiguate collisions with types.Identical.
+       // TODO(mdempsky): Add a method that *is* unique.
+       ls := typ.LongString()
+       prevs := s.m[ls]
+       for _, prev := range prevs {
+               if types.Identical(typ, prev.typ) {
+                       yyerrorl(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, linestr(prev.pos))
+                       return
+               }
+       }
+       s.m[ls] = append(prevs, typeSetEntry{pos, typ})
+}
+
 func typecheckExprSwitch(n *Node) {
        t := types.Types[TBOOL]
        if n.Left != nil {
@@ -599,41 +633,9 @@ func (s *typeSwitch) genCaseClauses(clauses []*Node) caseClauses {
                cc.defjmp = nod(OBREAK, nil, nil)
        }
 
-       // diagnose duplicate cases
-       s.checkDupCases(cc.list)
        return cc
 }
 
-func (s *typeSwitch) checkDupCases(cc []caseClause) {
-       if len(cc) < 2 {
-               return
-       }
-       // We store seen types in a map keyed by type hash.
-       // It is possible, but very unlikely, for multiple distinct types to have the same hash.
-       seen := make(map[uint32][]*Node)
-       // To avoid many small allocations of length 1 slices,
-       // also set up a single large slice to slice into.
-       nn := make([]*Node, 0, len(cc))
-Outer:
-       for _, c := range cc {
-               prev, ok := seen[c.hash]
-               if !ok {
-                       // First entry for this hash.
-                       nn = append(nn, c.node)
-                       seen[c.hash] = nn[len(nn)-1 : len(nn) : len(nn)]
-                       continue
-               }
-               for _, n := range prev {
-                       if types.Identical(n.Left.Type, c.node.Left.Type) {
-                               yyerrorl(c.node.Pos, "duplicate case %v in type switch\n\tprevious case at %v", c.node.Left.Type, n.Line())
-                               // avoid double-reporting errors
-                               continue Outer
-                       }
-               }
-               seen[c.hash] = append(seen[c.hash], c.node)
-       }
-}
-
 // walk generates an AST that implements sw,
 // where sw is a type switch.
 // The AST is generally of the form of a linear
index 5958b7db8ebd73d5e7b4f747236e6f3fd9a056c0..62c96c8330f6010333dce002269511ba3ee49fff 100644 (file)
@@ -35,13 +35,3 @@ func whatis(x interface{}) string {
        }
        return ""
 }
-
-func notused(x interface{}) {
-       // The first t is in a different scope than the 2nd t; it cannot
-       // be accessed (=> declared and not used error); but it is legal
-       // to declare it.
-       switch t := 0; t := x.(type) { // ERROR "declared and not used"
-       case int:
-               _ = t // this is using the t of "t := x.(type)"
-       }
-}
diff --git a/test/typeswitch2b.go b/test/typeswitch2b.go
new file mode 100644 (file)
index 0000000..135ae86
--- /dev/null
@@ -0,0 +1,20 @@
+// errorcheck
+
+// Copyright 2019 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.
+
+// Verify that various erroneous type switches are caught by the compiler.
+// Does not compile.
+
+package main
+
+func notused(x interface{}) {
+       // The first t is in a different scope than the 2nd t; it cannot
+       // be accessed (=> declared and not used error); but it is legal
+       // to declare it.
+       switch t := 0; t := x.(type) { // ERROR "declared and not used"
+       case int:
+               _ = t // this is using the t of "t := x.(type)"
+       }
+}