cmd/compile: rework checkdupfields

Use a map to detect duplicate symbols. Allows eliminating an otherwise
unneeded field from Sym and gets rid of a global variable.

Change-Id: Ic004bca7e9130a1261a1cddbc17244529a2a1df4
Reviewed-on: https://go-review.googlesource.com/20552
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
index 8c9906c..e1209ff 100644
--- a/src/cmd/compile/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -795,19 +795,23 @@
 	return f
 }
 
-var uniqgen uint32
-
-func checkdupfields(t *Type, what string) {
+// checkdupfields emits errors for duplicately named fields or methods in
+// a list of struct or interface types.
+func checkdupfields(what string, ts ...*Type) {
 	lno := lineno
 
-	for ; t != nil; t = t.Down {
-		if t.Sym != nil && t.Nname != nil && !isblank(t.Nname) {
-			if t.Sym.Uniqgen == uniqgen {
-				lineno = t.Nname.Lineno
-				Yyerror("duplicate %s %s", what, t.Sym.Name)
-			} else {
-				t.Sym.Uniqgen = uniqgen
+	seen := make(map[*Sym]bool)
+	for _, t := range ts {
+		for f, it := IterFields(t); f != nil; f = it.Next() {
+			if f.Sym == nil || f.Nname == nil || isblank(f.Nname) {
+				continue
 			}
+			if seen[f.Sym] {
+				lineno = f.Nname.Lineno
+				Yyerror("duplicate %s %s", what, f.Sym.Name)
+				continue
+			}
+			seen[f.Sym] = true
 		}
 	}
 
@@ -839,8 +843,7 @@
 		}
 	}
 
-	uniqgen++
-	checkdupfields(t.Type, "field")
+	checkdupfields("field", t)
 
 	if !t.Broke {
 		checkwidth(t)
@@ -980,8 +983,7 @@
 		}
 	}
 
-	uniqgen++
-	checkdupfields(t.Type, "method")
+	checkdupfields("method", t)
 	t = sortinter(t)
 	checkwidth(t)
 
@@ -1156,10 +1158,7 @@
 	*t.ResultsP() = tofunargs(out)
 	*t.ParamsP() = tofunargs(in)
 
-	uniqgen++
-	checkdupfields(t.Recvs().Type, "argument")
-	checkdupfields(t.Results().Type, "argument")
-	checkdupfields(t.Params().Type, "argument")
+	checkdupfields("argument", t.Recvs(), t.Results(), t.Params())
 
 	if t.Recvs().Broke || t.Results().Broke || t.Params().Broke {
 		t.Broke = true
diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
index 29b4b54..3d221e6 100644
--- a/src/cmd/compile/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -68,7 +68,6 @@
 
 type Sym struct {
 	Flags     SymFlags
-	Uniqgen   uint32
 	Link      *Sym
 	Importdef *Pkg   // where imported definition was found
 	Linkname  string // link name
diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/gc/sizeof_test.go
index 199b5ce..489dfaa 100644
--- a/src/cmd/compile/internal/gc/sizeof_test.go
+++ b/src/cmd/compile/internal/gc/sizeof_test.go
@@ -26,7 +26,7 @@
 		{Func{}, 104, 184},
 		{Name{}, 52, 80},
 		{Node{}, 92, 144},
-		{Sym{}, 64, 112},
+		{Sym{}, 60, 112},
 		{Type{}, 144, 240},
 	}