From 46b40480b61679a6f5349ed3185b1868ea0b8ea8 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 19 Mar 2020 14:00:44 -0700 Subject: [PATCH] go/types: updated documentation Change-Id: Id84fd18dc0e1928c96bb50a1759ced1e56605d51 --- src/go/types/NOTES | 11 +++++++++++ src/go/types/examples/types.go2 | 26 ++++++++++++++++++++++++++ src/go/types/testdata/todos.go2 | 19 ++++++++++++------- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/go/types/NOTES b/src/go/types/NOTES index 08b982b6e4..55b54dea7f 100644 --- a/src/go/types/NOTES +++ b/src/go/types/NOTES @@ -42,6 +42,10 @@ OBSERVATIONS ---------------------------------------------------------------------------------------------------- OPEN QUESTIONS +- Parsing _ = [](a(int)){} requires parentheses around `(a(int))` - should the parser be smarter in + these cases? Another example: []a(b, c){} This cannot be a conversion. Could fix such cases by re- + associating the AST when we see a {. Need to be careful, and need to take into account additional + complexity of spec. - What is the exact nature of a generic type? Does it act as a named type (it can have methods)? - For len/cap(x) where x is of type parameter type and the bound contains arrays only, should the result be a constant? (right now it is not). What are the implications for alternative, non- @@ -119,3 +123,10 @@ DESIGN/IMPLEMENTATION - 2/20/2020: Receiver type parameters must always be provided in the receiver parameter list of a method, even if they are not used by the method. Since the receiver acts like an implicit declaration of those type parameters, they may be blank, as with any other declaration. + +- 3/20/2020: Local type declarations with an underlying type that is type parameter lose the + methods declared with the type parameter bound. But they don't lose the properties of the + underlying type, i.e., the properties of the type parameter bound's type list. + This is something to consider if we were contemplating moving to a methods-only approach + (no type lists), even though local type declarations are exceedingly rare if they exist at + all in the wild. diff --git a/src/go/types/examples/types.go2 b/src/go/types/examples/types.go2 index 453b48cfdb..43c5ee6cb7 100644 --- a/src/go/types/examples/types.go2 +++ b/src/go/types/examples/types.go2 @@ -173,3 +173,29 @@ type _ struct { ( /* ERROR invalid embedded field type */ interface{}) ( /* ERROR invalid embedded field type */ func()) } + +// It's possible to declare local types whose underlying types +// are type parameters. As with ordinary type definitions, the +// types underlying properties are "inherited" but the methods +// are not. +func _(type T interface{ m(); type int })() { + type L T + var x L + + // m is not defined on L (it is not "inherited" from + // its underlying type). + x.m /* ERROR x.m undefined */ () + + // But the properties of T, such that as that it supports + // the operations of the types given by its type bound, + // are also the properties of L. + x++ + _ = x - x + + // On the other hand, if we define a local alias for T, + // that alias stands for T. + type A = T + var y A + y.m() + _ = y < 0 +} diff --git a/src/go/types/testdata/todos.go2 b/src/go/types/testdata/todos.go2 index 7d200c4de5..8739b0997b 100644 --- a/src/go/types/testdata/todos.go2 +++ b/src/go/types/testdata/todos.go2 @@ -20,13 +20,6 @@ func _(type T interface{ type chan int })(ch T) { _ = <- ch /* ERROR cannot receive */ } -// local variable declaration with underlying generic type is not fully supported -func _(type T)() { - type L T // local variable declaration - var x L - _ = x /* ERROR not defined */ + x -} - // pointer indirection of generic types is not yet supported func _(type T interface{ type *int })(p T) { _ = *p /* ERROR cannot indirect */ @@ -46,3 +39,15 @@ func _(type T interface{ type int})(x T) { var _ T = int /* ERROR cannot use */ (42) var _ T = myint /* ERROR cannot use */ (42) } + +// Composite literals that require parentheses around their types. +// Should investigate if it makes sense to be smarter when parsing +// at the cost of more complex rules. +type T1(type P) struct{} +type T2(type P, Q) struct{} + +func _() { + _ = []T1 /* ERROR instantiation */ (int){} // this doesn't work + _ = [](T1(int)){} // this works + _ = [](T2(int, string)){} // T2(int, float) cannot be a conversion - should not need ()'s +}