go.tools/go/types: check map key type when it is fully known
Fixes golang/go#6667. R=adonovan CC=golang-dev https://golang.org/cl/17360043
This commit is contained in:
parent
1e05a58b11
commit
65aaa0093c
|
@ -38,6 +38,7 @@ type checker struct {
|
||||||
conversions map[*ast.CallExpr]bool // set of type-checked conversions (to distinguish from calls)
|
conversions map[*ast.CallExpr]bool // set of type-checked conversions (to distinguish from calls)
|
||||||
untyped map[ast.Expr]exprInfo // map of expressions without final type
|
untyped map[ast.Expr]exprInfo // map of expressions without final type
|
||||||
lhsVarsList [][]*Var // type switch lhs variable sets, for 'declared but not used' errors
|
lhsVarsList [][]*Var // type switch lhs variable sets, for 'declared but not used' errors
|
||||||
|
delayed []func() // delayed checks that require fully setup types
|
||||||
|
|
||||||
firstErr error // first error encountered
|
firstErr error // first error encountered
|
||||||
Info // collected type info
|
Info // collected type info
|
||||||
|
@ -66,6 +67,10 @@ func newChecker(conf *Config, fset *token.FileSet, pkg *Package) *checker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (check *checker) delay(f func()) {
|
||||||
|
check.delayed = append(check.delayed, f)
|
||||||
|
}
|
||||||
|
|
||||||
func (check *checker) recordTypeAndValue(x ast.Expr, typ Type, val exact.Value) {
|
func (check *checker) recordTypeAndValue(x ast.Expr, typ Type, val exact.Value) {
|
||||||
assert(x != nil && typ != nil)
|
assert(x != nil && typ != nil)
|
||||||
if m := check.Types; m != nil {
|
if m := check.Types; m != nil {
|
||||||
|
@ -196,6 +201,12 @@ func (conf *Config) check(pkgPath string, fset *token.FileSet, files []*ast.File
|
||||||
|
|
||||||
check.resolveFiles(files[:i])
|
check.resolveFiles(files[:i])
|
||||||
|
|
||||||
|
// perform delayed checks
|
||||||
|
for _, f := range check.delayed {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
check.delayed = nil // not needed anymore
|
||||||
|
|
||||||
// remaining untyped expressions must indeed be untyped
|
// remaining untyped expressions must indeed be untyped
|
||||||
if debug {
|
if debug {
|
||||||
for x, info := range check.untyped {
|
for x, info := range check.untyped {
|
||||||
|
|
|
@ -79,7 +79,9 @@ func isInterface(typ Type) bool {
|
||||||
func isComparable(typ Type) bool {
|
func isComparable(typ Type) bool {
|
||||||
switch t := typ.Underlying().(type) {
|
switch t := typ.Underlying().(type) {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
return t.kind != Invalid && t.kind != UntypedNil
|
// assume invalid types to be comparable
|
||||||
|
// to avoid follow-up errors
|
||||||
|
return t.kind != UntypedNil
|
||||||
case *Pointer, *Interface, *Chan:
|
case *Pointer, *Interface, *Chan:
|
||||||
return true
|
return true
|
||||||
case *Struct:
|
case *Struct:
|
||||||
|
|
|
@ -109,3 +109,11 @@ func _() {
|
||||||
c0 chan c0
|
c0 chan c0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test cases for issue 6667
|
||||||
|
|
||||||
|
type A [10]map[A /* ERROR invalid map key */ ]bool
|
||||||
|
|
||||||
|
type S struct {
|
||||||
|
m map[S /* ERROR invalid map key */ ]bool
|
||||||
|
}
|
||||||
|
|
|
@ -314,11 +314,14 @@ func (check *checker) typInternal(e ast.Expr, def *Named, cycleOk bool) Type {
|
||||||
// spec: "The comparison operators == and != must be fully defined
|
// spec: "The comparison operators == and != must be fully defined
|
||||||
// for operands of the key type; thus the key type must not be a
|
// for operands of the key type; thus the key type must not be a
|
||||||
// function, map, or slice."
|
// function, map, or slice."
|
||||||
// TODO(gri) if the key type is not fully defined yet, this test will be incorrect
|
//
|
||||||
if !isComparable(typ.key) {
|
// Delay this check because it requires fully setup types;
|
||||||
check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key)
|
// it is safe to continue in any case (was issue 6667).
|
||||||
// ok to continue
|
check.delay(func() {
|
||||||
}
|
if !isComparable(typ.key) {
|
||||||
|
check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return typ
|
return typ
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue