[dev.typeparams] cmd/compile/internal/types2: "comparable" must not be visible before Go 1.18
While at it, clean up the setup of comparable in universe.go.
Fixes #46090
Change-Id: I9655b3e137a03763d677d9a2a730c5570ccff6dc
Reviewed-on: https://go-review.googlesource.com/c/go/+/331517
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46090.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46090.go2
new file mode 100644
index 0000000..81b3197
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46090.go2
@@ -0,0 +1,9 @@
+// Copyright 2020 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.
+
+// The predeclared type comparable is not visible before Go 1.18.
+
+package go1_17
+
+type _ comparable // ERROR undeclared
diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go
index fe676be..5626fed 100644
--- a/src/cmd/compile/internal/types2/typexpr.go
+++ b/src/cmd/compile/internal/types2/typexpr.go
@@ -25,7 +25,7 @@
// Note that we cannot use check.lookup here because the returned scope
// may be different from obj.Parent(). See also Scope.LookupParent doc.
scope, obj := check.scope.LookupParent(e.Value, check.pos)
- if obj == nil {
+ if obj == nil || obj == universeComparable && !check.allowVersion(check.pkg, 1, 18) {
if e.Value == "_" {
check.error(e, "cannot use _ as value or type")
} else {
diff --git a/src/cmd/compile/internal/types2/universe.go b/src/cmd/compile/internal/types2/universe.go
index 2bcc497..c9b53ba 100644
--- a/src/cmd/compile/internal/types2/universe.go
+++ b/src/cmd/compile/internal/types2/universe.go
@@ -20,11 +20,12 @@
var Unsafe *Package
var (
- universeIota *Const
- universeByte *Basic // uint8 alias, but has name "byte"
- universeRune *Basic // int32 alias, but has name "rune"
- universeAny *Interface
- universeError *Named
+ universeIota *Const
+ universeByte *Basic // uint8 alias, but has name "byte"
+ universeRune *Basic // int32 alias, but has name "rune"
+ universeAny *Interface
+ universeError *Named
+ universeComparable Object
)
// Typ contains the predeclared *Basic types indexed by their
@@ -77,21 +78,30 @@
def(NewTypeName(nopos, nil, t.name, t))
}
- // any
- // (Predeclared and entered into universe scope so we do all the
- // usual checks; but removed again from scope later since it's
- // only visible as constraint in a type parameter list.)
+ // type any = interface{}
+ // Entered into universe scope so we do all the usual checks;
+ // but removed again from scope later since it's only visible
+ // as constraint in a type parameter list.
def(NewTypeName(nopos, nil, "any", &emptyInterface))
- // Error has a nil package in its qualified name since it is in no package
+ // type error interface{ Error() string }
{
res := NewVar(nopos, nil, "", Typ[String])
- sig := &Signature{results: NewTuple(res)}
+ sig := NewSignature(nil, nil, NewTuple(res), false)
err := NewFunc(nopos, nil, "Error", sig)
typ := &Named{underlying: NewInterfaceType([]*Func{err}, nil)}
sig.recv = NewVar(nopos, nil, "", typ)
def(NewTypeName(nopos, nil, "error", typ))
}
+
+ // type comparable interface{ ==() }
+ {
+ sig := NewSignature(nil, nil, nil, false)
+ eql := NewFunc(nopos, nil, "==", sig)
+ typ := &Named{underlying: NewInterfaceType([]*Func{eql}, nil)}
+ sig.recv = NewVar(nopos, nil, "", typ)
+ def(NewTypeName(nopos, nil, "comparable", typ))
+ }
}
var predeclaredConsts = [...]struct {
@@ -200,33 +210,6 @@
def(newBuiltin(_Trace))
}
-func defPredeclaredComparable() {
- // The "comparable" interface can be imagined as defined like
- //
- // type comparable interface {
- // == () untyped bool
- // != () untyped bool
- // }
- //
- // == and != cannot be user-declared but we can declare
- // a magic method == and check for its presence when needed.
-
- // Define interface { == () }. We don't care about the signature
- // for == so leave it empty except for the receiver, which is
- // set up later to match the usual interface method assumptions.
- sig := new(Signature)
- eql := NewFunc(nopos, nil, "==", sig)
- iface := NewInterfaceType([]*Func{eql}, nil)
-
- // set up the defined type for the interface
- obj := NewTypeName(nopos, nil, "comparable", nil)
- named := NewNamed(obj, iface, nil)
- obj.color_ = black
- sig.recv = NewVar(nopos, nil, "", named) // complete == signature
-
- def(obj)
-}
-
func init() {
Universe = NewScope(nil, nopos, nopos, "universe")
Unsafe = NewPackage("unsafe", "unsafe")
@@ -236,13 +219,13 @@
defPredeclaredConsts()
defPredeclaredNil()
defPredeclaredFuncs()
- defPredeclaredComparable()
universeIota = Universe.Lookup("iota").(*Const)
universeByte = Universe.Lookup("byte").(*TypeName).typ.(*Basic)
universeRune = Universe.Lookup("rune").(*TypeName).typ.(*Basic)
universeAny = Universe.Lookup("any").(*TypeName).typ.(*Interface)
universeError = Universe.Lookup("error").(*TypeName).typ.(*Named)
+ universeComparable = Universe.Lookup("comparable")
// "any" is only visible as constraint in a type parameter list
delete(Universe.elems, "any")