Commit bd2838be authored by Josh Bleecher Snyder's avatar Josh Bleecher Snyder

cmd/compile: use a map to detect duplicate type switch cases

This is a bit simpler than playing sorting games,
and it is clearer that it generates errors
in the correct (source) order.

It also allows us to simplify sorting.

It also prevents quadratic error messages for
(pathological) inputs with many duplicate type cases.

While we’re here, refactoring deduping into separate functions.

Negligible compilebench impact.

Fixes #15912.

Change-Id: I6cc19edd38875389a70ccbdbdf0d9b7d5ac5946f
Reviewed-on: https://go-review.googlesource.com/26762
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
Reviewed-by: default avatarMatthew Dempsky <mdempsky@google.com>
parent 1a3006b0
...@@ -453,8 +453,8 @@ func genCaseClauses(sw *Node, kind int) caseClauses { ...@@ -453,8 +453,8 @@ func genCaseClauses(sw *Node, kind int) caseClauses {
c.typ = caseKindTypeVar c.typ = caseKindTypeVar
default: default:
c.typ = caseKindTypeConst c.typ = caseKindTypeConst
c.hash = typehash(n.Left.Type)
} }
c.hash = typehash(n.Left.Type)
} else { } else {
// expression switch // expression switch
switch consttype(n.Left) { switch consttype(n.Left) {
...@@ -477,37 +477,53 @@ func genCaseClauses(sw *Node, kind int) caseClauses { ...@@ -477,37 +477,53 @@ func genCaseClauses(sw *Node, kind int) caseClauses {
// sort by value and diagnose duplicate cases // sort by value and diagnose duplicate cases
if kind == switchKindType { if kind == switchKindType {
// type switch checkDupTypeCases(cc.list)
sort.Sort(caseClauseByType(cc.list)) } else {
for i, c1 := range cc.list { checkDupExprCases(cc.list)
for _, c2 := range cc.list[i+1:] {
if c1.hash != c2.hash {
break
} }
if Eqtype(c1.node.Left.Type, c2.node.Left.Type) {
yyerrorl(c2.node.Lineno, "duplicate case %v in type switch\n\tprevious case at %v", c2.node.Left.Type, c1.node.Line()) return cc
}
func checkDupTypeCases(cc []caseClause) {
// 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 Eqtype(n.Left.Type, c.node.Left.Type) {
yyerrorl(c.node.Lineno, "duplicate case %v in type switch\n\tprevious case at %v", c.node.Left.Type, n.Line())
// avoid double-reporting errors
continue Outer
} }
} }
} else { seen[c.hash] = append(seen[c.hash], c.node)
// expression switch
sort.Sort(caseClauseByExpr(cc.list))
for i, c1 := range cc.list {
if i+1 == len(cc.list) {
break
} }
c2 := cc.list[i+1] }
func checkDupExprCases(cc []caseClause) {
sort.Sort(caseClauseByExpr(cc))
for i, c1 := range cc[:len(cc)-1] {
c2 := cc[i+1]
if exprcmp(c1, c2) != 0 { if exprcmp(c1, c2) != 0 {
continue continue
} }
setlineno(c2.node) setlineno(c2.node)
Yyerror("duplicate case %v in switch\n\tprevious case at %v", c1.node.Left, c1.node.Line()) Yyerror("duplicate case %v in switch\n\tprevious case at %v", c1.node.Left, c1.node.Line())
} }
}
// put list back in processing order // put list back in processing order
sort.Sort(caseClauseByOrd(cc.list)) sort.Sort(caseClauseByOrd(cc))
return cc
} }
// walk generates an AST that implements sw, // walk generates an AST that implements sw,
...@@ -801,18 +817,9 @@ func (x caseClauseByType) Len() int { return len(x) } ...@@ -801,18 +817,9 @@ func (x caseClauseByType) Len() int { return len(x) }
func (x caseClauseByType) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x caseClauseByType) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x caseClauseByType) Less(i, j int) bool { func (x caseClauseByType) Less(i, j int) bool {
c1, c2 := x[i], x[j] c1, c2 := x[i], x[j]
switch { // sort by hash code, then ordinal (for the rare case of hash collisions)
// sort non-constants last if c1.hash != c2.hash {
case c1.typ != caseKindTypeConst:
return false
case c2.typ != caseKindTypeConst:
return true
// sort by hash code
case c1.hash != c2.hash:
return c1.hash < c2.hash return c1.hash < c2.hash
} }
// sort by ordinal
return c1.ordinal < c2.ordinal return c1.ordinal < c2.ordinal
} }
...@@ -57,8 +57,8 @@ func f4(e interface{}) { ...@@ -57,8 +57,8 @@ func f4(e interface{}) {
case int: case int:
case int: // ERROR "duplicate case int in type switch" case int: // ERROR "duplicate case int in type switch"
case int64: case int64:
case error: // ERROR "duplicate case error in type switch"
case error: case error:
case error: // ERROR "duplicate case error in type switch"
case fmt.Stringer: case fmt.Stringer:
case fmt.Stringer: // ERROR "duplicate case fmt.Stringer in type switch" case fmt.Stringer: // ERROR "duplicate case fmt.Stringer in type switch"
case struct { case struct {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment