[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")