From 4843595d7fc093c5dad053e2dfbcedae7ec213f4 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 21 Apr 2020 22:18:25 -0700 Subject: [PATCH] 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 --- src/go/types/api_test.go | 4 ++-- src/go/types/predicates.go | 16 +++++++++++++--- src/go/types/testdata/tmp.go2 | 6 +++--- src/go/types/type.go | 27 ++++++++++++++++++--------- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index eef21db12a..ea6ba3e702 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -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)})`}, diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 52ff494c48..311376df6e 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -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) } diff --git a/src/go/types/testdata/tmp.go2 b/src/go/types/testdata/tmp.go2 index a81582f34a..303b2d3adb 100644 --- a/src/go/types/testdata/tmp.go2 +++ b/src/go/types/testdata/tmp.go2 @@ -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 diff --git a/src/go/types/type.go b/src/go/types/type.go index 70a17bc0e1..638346db2a 100644 --- a/src/go/types/type.go +++ b/src/go/types/type.go @@ -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) }