From 65aaa0093cff3ede1f0749875cd3fed8082d55a0 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 25 Oct 2013 14:32:14 -0700 Subject: [PATCH] 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 --- go/types/check.go | 11 +++++++++++ go/types/predicates.go | 4 +++- go/types/testdata/cycles.src | 10 +++++++++- go/types/typexpr.go | 13 ++++++++----- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/go/types/check.go b/go/types/check.go index 48a1ba6b..e471e06f 100644 --- a/go/types/check.go +++ b/go/types/check.go @@ -38,6 +38,7 @@ type checker struct { 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 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 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) { assert(x != nil && typ != 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]) + // perform delayed checks + for _, f := range check.delayed { + f() + } + check.delayed = nil // not needed anymore + // remaining untyped expressions must indeed be untyped if debug { for x, info := range check.untyped { diff --git a/go/types/predicates.go b/go/types/predicates.go index 407135df..accfe1a7 100644 --- a/go/types/predicates.go +++ b/go/types/predicates.go @@ -79,7 +79,9 @@ func isInterface(typ Type) bool { func isComparable(typ Type) bool { switch t := typ.Underlying().(type) { 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: return true case *Struct: diff --git a/go/types/testdata/cycles.src b/go/types/testdata/cycles.src index 10b6f190..29f66046 100644 --- a/go/types/testdata/cycles.src +++ b/go/types/testdata/cycles.src @@ -108,4 +108,12 @@ func _() { // channels c0 chan c0 ) -} \ No newline at end of file +} + +// 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 +} diff --git a/go/types/typexpr.go b/go/types/typexpr.go index 0690dba9..b9d71c42 100644 --- a/go/types/typexpr.go +++ b/go/types/typexpr.go @@ -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 // for operands of the key type; thus the key type must not be a // function, map, or slice." - // TODO(gri) if the key type is not fully defined yet, this test will be incorrect - if !isComparable(typ.key) { - check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key) - // ok to continue - } + // + // Delay this check because it requires fully setup types; + // it is safe to continue in any case (was issue 6667). + check.delay(func() { + if !isComparable(typ.key) { + check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key) + } + }) return typ