From 726ffce659a173951186097b26489570cff24fd3 Mon Sep 17 00:00:00 2001 From: Rob Findley Date: Fri, 16 Jul 2021 09:44:08 -0400 Subject: [PATCH] [dev.typeparams] go/types: "comparable" must not be visible before Go 1.18 This is a straightforward port of CL 331517 to go/types. Change-Id: Id00761fd5dffb4d09e19f086d18ddc20f11528d0 Reviewed-on: https://go-review.googlesource.com/c/go/+/335032 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- .../types/testdata/fixedbugs/issue46090.go2 | 9 +++ src/go/types/typexpr.go | 2 +- src/go/types/universe.go | 64 +++++++------------ 3 files changed, 32 insertions(+), 43 deletions(-) create mode 100644 src/go/types/testdata/fixedbugs/issue46090.go2 diff --git a/src/go/types/testdata/fixedbugs/issue46090.go2 b/src/go/types/testdata/fixedbugs/issue46090.go2 new file mode 100644 index 0000000000..81b31974c8 --- /dev/null +++ b/src/go/types/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/go/types/typexpr.go b/src/go/types/typexpr.go index 070b0ade3e..64a1b37cef 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -27,7 +27,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool) // 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.Name, check.pos) - if obj == nil { + if obj == nil || obj == universeComparable && !check.allowVersion(check.pkg, 1, 18) { if e.Name == "_" { check.errorf(e, _InvalidBlank, "cannot use _ as value or type") } else { diff --git a/src/go/types/universe.go b/src/go/types/universe.go index 7ce401827e..540b0ac118 100644 --- a/src/go/types/universe.go +++ b/src/go/types/universe.go @@ -8,7 +8,6 @@ package types import ( "go/constant" - "go/internal/typeparams" "go/token" "strings" ) @@ -22,11 +21,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 @@ -79,21 +79,30 @@ func defPredeclaredTypes() { def(NewTypeName(token.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(token.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(token.NoPos, nil, "", Typ[String]) - sig := &Signature{results: NewTuple(res)} + sig := NewSignature(nil, nil, NewTuple(res), false) err := NewFunc(token.NoPos, nil, "Error", sig) typ := &Named{underlying: NewInterfaceType([]*Func{err}, nil)} sig.recv = NewVar(token.NoPos, nil, "", typ) def(NewTypeName(token.NoPos, nil, "error", typ)) } + + // type comparable interface{ ==() } + { + sig := NewSignature(nil, nil, nil, false) + eql := NewFunc(token.NoPos, nil, "==", sig) + typ := &Named{underlying: NewInterfaceType([]*Func{eql}, nil)} + sig.recv = NewVar(token.NoPos, nil, "", typ) + def(NewTypeName(token.NoPos, nil, "comparable", typ)) + } } var predeclaredConsts = [...]struct { @@ -202,33 +211,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(token.NoPos, nil, "==", sig) - iface := NewInterfaceType([]*Func{eql}, nil) - - // set up the defined type for the interface - obj := NewTypeName(token.NoPos, nil, "comparable", nil) - named := NewNamed(obj, iface, nil) - obj.color_ = black - sig.recv = NewVar(token.NoPos, nil, "", named) // complete == signature - - def(obj) -} - func init() { Universe = NewScope(nil, token.NoPos, token.NoPos, "universe") Unsafe = NewPackage("unsafe", "unsafe") @@ -238,15 +220,13 @@ func init() { defPredeclaredConsts() defPredeclaredNil() defPredeclaredFuncs() - if typeparams.Enabled { - 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")