From 1cd505c353e4656ba28fd8de8708e8c8e6c21cbf Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 28 Jun 2021 19:41:29 -0700 Subject: [PATCH] [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 Reviewed-by: Robert Findley --- .../types2/testdata/fixedbugs/issue46090.go2 | 9 +++ src/cmd/compile/internal/types2/typexpr.go | 2 +- src/cmd/compile/internal/types2/universe.go | 61 +++++++------------ 3 files changed, 32 insertions(+), 40 deletions(-) create mode 100644 src/cmd/compile/internal/types2/testdata/fixedbugs/issue46090.go2 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 00000000000..81b31974c8d --- /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 fe676be2efd..5626fed7568 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -25,7 +25,7 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo // 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 2bcc49778ef..c9b53bac921 100644 --- a/src/cmd/compile/internal/types2/universe.go +++ b/src/cmd/compile/internal/types2/universe.go @@ -20,11 +20,12 @@ var Universe *Scope 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 @@ func defPredeclaredTypes() { 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 @@ func DefPredeclaredTestFuncs() { 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 @@ func init() { 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")