go/types: implement specific converter methods for *instance types

This is fixing a known issue (see below) and many unknown issues:
An *instance type may legally represent any kind of type after
instantiation.

Given the declaration:

	type T(type P) P

T(int) is now a valid constant type, and T([]int) is a valid
slice type.

Also, fixed the implementation of isTyped/isUntyped which caused
types to expand prematurely.

Change-Id: Ic71c7252e3b066b0f97c2f1892cf20b2c4c2ef98
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go2-dev/+/723717
Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer 2020-04-21 22:18:25 -07:00
parent 37801e2c49
commit 4843595d7f
4 changed files with 36 additions and 17 deletions

View File

@ -444,7 +444,7 @@ func TestDefsInfo(t *testing.T) {
// generic types must be sanitized
// (need to use sufficiently nested types to provoke unexpanded types)
// TODO(gri) add analogous test for constants, once T(int) is accepted as constant type
{`package g0; type t(type P) P; const x = (t(int))(42)`, `x`, `const g0.x g0.t(int)`},
{`package g1; type t(type P) P; var x = (t(int))(42)`, `x`, `var g1.x g1.t(int)`},
{`package g2; type t(type P) P; type x struct{ f t(int) }`, `x`, `type g2.x struct{f g2.t(int)}`},
{`package g3; type t(type P) P; func f(x struct{ f t(string) }); var g = f`, `g`, `var g3.g func(x struct{f g3.t(string)})`},
@ -489,7 +489,7 @@ func TestUsesInfo(t *testing.T) {
// generic types must be sanitized
// (need to use sufficiently nested types to provoke unexpanded types)
// TODO(gri) add analogous test for constants, once T(int) is accepted as constant type
{`package g0; func _() { _ = x }; type t(type P) P; const x = (t(int))(42)`, `x`, `const g0.x g0.t(int)`},
{`package g1; func _() { _ = x }; type t(type P) P; var x = (t(int))(42)`, `x`, `var g1.x g1.t(int)`},
{`package g2; func _() { type _ x }; type t(type P) P; type x struct{ f t(int) }`, `x`, `type g2.x struct{f g2.t(int)}`},
{`package g3; func _() { _ = f }; type t(type P) P; func f(x struct{ f t(string) })`, `f`, `func g3.f(x struct{f g3.t(string)})`},

View File

@ -11,6 +11,8 @@ import (
"sort"
)
// isNamed reports whether typ has a name.
// isNamed may be called with types that are not fully set up.
func isNamed(typ Type) bool {
switch typ.(type) {
case *Basic, *Named, *instance:
@ -44,14 +46,22 @@ func isComplex(typ Type) bool { return is(typ, IsComplex) }
func isNumeric(typ Type) bool { return is(typ, IsNumeric) }
func isString(typ Type) bool { return is(typ, IsString) }
// isTyped reports whether typ is typed; i.e., not an untyped
// constant or boolean. isTyped may be called with types that
// are not fully set up.
func isTyped(typ Type) bool {
t := typ.Basic()
// isTyped is called with types that are not fully
// set up. Must not call Basic()!
// A *Named or *instance type is always typed, so
// we only need to check if we have a true *Basic
// type.
t, _ := typ.(*Basic)
return t == nil || t.info&IsUntyped == 0
}
// isUntyped(typ) is the same as !isTyped(typ).
func isUntyped(typ Type) bool {
t := typ.Basic()
return t != nil && t.info&IsUntyped != 0
return !isTyped(typ)
}
func isOrdered(typ Type) bool { return is(typ, IsOrdered) }

View File

@ -1,5 +1,5 @@
package p
type E(type P) struct {
(E /* ERROR illegal cycle */ (P))
}
type T(type P) P
const _ T(int) = 0

View File

@ -19,12 +19,16 @@ type Type interface {
// Under returns the true expanded underlying type.
// If it doesn't exist, the result is Typ[Invalid].
// Under must only be called when a type is known
// to be fully set up.
Under() Type
// String returns a string representation of a type.
String() string
// Converters
// A converter must only be called when a type is
// known to be fully set up.
Basic() *Basic
Array() *Array
Slice() *Slice
@ -698,7 +702,19 @@ type instance struct {
aType
}
func (t *instance) Named() *Named { return t.expand().Named() }
// Converter methods
func (t *instance) Basic() *Basic { return t.Under().Basic() }
func (t *instance) Array() *Array { return t.Under().Array() }
func (t *instance) Slice() *Slice { return t.Under().Slice() }
func (t *instance) Struct() *Struct { return t.Under().Struct() }
func (t *instance) Pointer() *Pointer { return t.Under().Pointer() }
func (t *instance) Tuple() *Tuple { return t.Under().Tuple() }
func (t *instance) Signature() *Signature { return t.Under().Signature() }
func (t *instance) Interface() *Interface { return t.Under().Interface() }
func (t *instance) Map() *Map { return t.Under().Map() }
func (t *instance) Chan() *Chan { return t.Under().Chan() }
func (t *instance) Named() *Named { return t.expand().Named() }
func (t *instance) TypeParam() *TypeParam { return t.Under().TypeParam() }
// expand returns the instantiated (= expanded) type of t.
// The result is either an instantiated *Named type, or
@ -775,15 +791,8 @@ func (t *Map) Under() Type { return t }
func (t *Chan) Under() Type { return t }
// see decl.go for implementation of Named.Under
func (t *TypeParam) Under() Type { return t }
func (t *instance) Under() Type {
typ := t.expand()
if n, _ := typ.(*Named); n != nil {
return n.Under()
}
return typ
}
func (t *instance) Under() Type { return t.expand().Under() }
func (t *Basic) String() string { return TypeString(t, nil) }
func (t *Array) String() string { return TypeString(t, nil) }