cmd/compile: avoid spurious errors for invalid map key types

Instead of trying to validate map key types eagerly in some
cases, delay their validation to the end of type-checking,
when we all type information is present.

Passes go build -toolexec 'toolstash -cmp' -a std .

Fixes #21273.
Fixes #21657.

Change-Id: I532369dc91c6adca1502d6aa456bb06b57e6c7ff
Reviewed-on: https://go-review.googlesource.com/75310
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
griesemer 2017-11-01 17:38:07 -07:00 committed by Robert Griesemer
parent aec345d638
commit 25159d3af9
4 changed files with 41 additions and 37 deletions

View file

@ -291,6 +291,7 @@ func dowidth(t *types.Type) {
case TFORW: // should have been filled in
if !t.Broke() {
t.SetBroke(true)
yyerror("invalid recursive type %v", t)
}
w = 1 // anything will do

View file

@ -513,6 +513,8 @@ func Main(archInit func(*Arch)) {
fcount++
}
}
// With all types ckecked, it's now safe to verify map keys.
checkMapKeys()
timings.AddEvent(fcount, "funcs")
// Phase 4: Decide how to capture closed variables.

View file

@ -7,7 +7,6 @@ package gc
import (
"cmd/compile/internal/types"
"cmd/internal/objabi"
"cmd/internal/src"
"fmt"
"math"
"strings"
@ -420,18 +419,7 @@ func typecheck1(n *Node, top int) *Node {
}
n.Op = OTYPE
n.Type = types.NewMap(l.Type, r.Type)
// map key validation
alg, bad := algtype1(l.Type)
if alg == ANOEQ {
if bad.Etype == TFORW {
// queue check for map until all the types are done settling.
mapqueue = append(mapqueue, mapqueueval{l, n.Pos})
} else if bad.Etype != TANY {
// no need to queue, key is already bad
yyerror("invalid map key type %v", l.Type)
}
}
mapqueue = append(mapqueue, n) // check map keys when all types are settled
n.Left = nil
n.Right = nil
@ -3496,16 +3484,18 @@ func stringtoarraylit(n *Node) *Node {
return nn
}
var ntypecheckdeftype int
var mapqueue []*Node
type mapqueueval struct {
n *Node
lno src.XPos
func checkMapKeys() {
for _, n := range mapqueue {
k := n.Type.MapType().Key
if !k.Broke() && !IsComparable(k) {
yyerrorl(n.Pos, "invalid map key type %v", k)
}
}
mapqueue = nil
}
// tracks the line numbers at which forward types are first used as map keys
var mapqueue []mapqueueval
func copytype(n *Node, t *types.Type) {
if t.Etype == TFORW {
// This type isn't computed yet; when it is, update n.
@ -3565,7 +3555,6 @@ func copytype(n *Node, t *types.Type) {
}
func typecheckdeftype(n *Node) {
ntypecheckdeftype++
lno := lineno
setlineno(n)
n.Type.Sym = n.Sym
@ -3584,22 +3573,6 @@ func typecheckdeftype(n *Node) {
}
lineno = lno
// if there are no type definitions going on, it's safe to
// try to validate the map key types for the interfaces
// we just read.
if ntypecheckdeftype == 1 {
for _, e := range mapqueue {
lineno = e.lno
if !IsComparable(e.n.Type) {
yyerror("invalid map key type %v", e.n.Type)
}
}
mapqueue = nil
lineno = lno
}
ntypecheckdeftype--
}
func typecheckdef(n *Node) {

View file

@ -0,0 +1,28 @@
// errorcheck
// Copyright 2017 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.
package main
type T0 T0 // ERROR "invalid recursive type"
type _ map[T0]int
type T1 struct{ T1 } // ERROR "invalid recursive type"
type _ map[T1]int
func f() {
type T2 T2 // ERROR "invalid recursive type"
type _ map[T2]int
}
func g() {
type T3 struct{ T3 } // ERROR "invalid recursive type"
type _ map[T3]int
}
func h() {
type T4 struct{ m map[T4]int } // ERROR "invalid map key"
type _ map[T4]int // ERROR "invalid map key"
}