diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index c94f80a149..5a8d9875a6 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -1129,18 +1129,25 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field { doc := p.leadComment var idents []*ast.Ident var typ ast.Expr - x := p.parseTypeName(nil) - if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN { - // method - idents = []*ast.Ident{ident} - scope := ast.NewScope(nil) // method scope - tparams, params := p.parseParameters(scope, methodTypeParamsOk|variadicOk, "method") - results := p.parseResult(scope, true) - typ = &ast.FuncType{Func: token.NoPos, TParams: tparams, Params: params, Results: results} - } else { - // embedded interface - typ = x - p.resolve(typ) + switch p.tok { + case token.IDENT: + x := p.parseTypeName(nil) + if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN { + // method + idents = []*ast.Ident{ident} + scope := ast.NewScope(nil) // method scope + tparams, params := p.parseParameters(scope, methodTypeParamsOk|variadicOk, "method") + results := p.parseResult(scope, true) + typ = &ast.FuncType{Func: token.NoPos, TParams: tparams, Params: params, Results: results} + } else { + // embedded interface + typ = x + p.resolve(typ) + } + case token.LPAREN: + // embedded, possibly parameterized interface + // (using the enclosing parentheses to distinguish it from a method declaration) + typ = p.parseType(true) } p.expectSemi() // call before accessing p.linecomment @@ -1163,7 +1170,7 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType { L: for { switch p.tok { - case token.IDENT: + case token.IDENT, token.LPAREN: mlist = append(mlist, p.parseMethodSpec(scope)) case token.TYPE: p.next() diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go index ade290d90c..2fcba3d429 100644 --- a/src/go/parser/short_test.go +++ b/src/go/parser/short_test.go @@ -98,6 +98,11 @@ var valids = []string{ // interfaces with (contract) type lists `package p; type _ interface{type int}`, `package p; type _ interface{type int, float32; type bool; m(); type string;}`, + + // interfaces with parenthesized embedded and possibly parameterized interfaces + `package p; type I1 interface{}; type I2 interface{ (I1) }`, + `package p; type I1(type T) interface{}; type I2 interface{ (I1(int)) }`, + `package p; type I1(type T) interface{}; type I2(type T) interface{ (I1(T)) }`, } func TestValid(t *testing.T) { diff --git a/src/go/types/NOTES b/src/go/types/NOTES index d912bc838d..102a920f94 100644 --- a/src/go/types/NOTES +++ b/src/go/types/NOTES @@ -1,7 +1,9 @@ This file works as a sort of notebook/implementation log. It replaces my notebook based approach so we have a better track record. I only switched to this file recently, hence it is incomplete. +---------------------------------------------------------------------------------------------------- TODO + - type assertions on/against parameterized types - no need for error messages where a _ type parameter cannot be inferred (_ cannot be used) - use Underlying() to return a type parameter's bound? investigate! @@ -11,16 +13,20 @@ TODO - improve error messages! - use []*TypeParam for tparams in subst? (unclear) +---------------------------------------------------------------------------------------------------- OPEN ISSUES + - instantiating a parameterized function type w/o value or result parameters may have unexpected side-effects (we don't make a copy of the signature in some cases) - investigate - using a contract and enumerating type arguments currently leads to an error (e.g. func f(type T C(T)) (x T) ... ) - leaving away unused receiver type parameters leads to an error; e.g.: "type S(type T) struct{}; func (S) _()" -- allow parenthesized embedded interfaces so we can embed parameterized interfaces and they won't be confused - for methods - confirm that it's ok to use inference in missingMethod to compare parameterized methods +- now that we allow parenthesized embedded interfaces, should we allow parenthesized embedded fields? + (probably yes, for symmetry and consistency). +---------------------------------------------------------------------------------------------------- DESIGN/IMPLEMENTATION + - 11/19/2019: For type parameters with interface bounds to work, the scope of all type parameters in a type parameter list starts at the "type" keyword. This makes all type parameters visible for all type parameter bounds (interfaces that may be parameterized with the type parameters). @@ -67,6 +73,12 @@ DESIGN/IMPLEMENTATION name cannot be a type parameter name declared later (or by the receiver type specification). This seems reasonable and should help avoid confusion which is possible otherwise. -- 1/6/2020: Embedding a parameterized interface in an interface is not possible because it looks - like a method declaration. We probably need to find a away around this; maybe parenthesize the - embedded interface (which is currently not permitted, syntactically). +- 1/7/2020: We distinguish embedded instantiated (parameterized) interfaces from methods by enclosing + the embedded interfaces in parentheses (the design draft recommends this change). Since this opens + the possibility for any parenthesized type (that is an interface), we can also allow (parenthesized) + interface literals as it is simpler to permit those than forbid them. + +- 1/7/2020: The current implementation permits empty type parameter lists as in: "func f(type)(x int)" + but we cannot call such a function as "f()(1)"; the empty type argument list causes problems. + We should either disallow empty type parameter lists or document this well. + diff --git a/src/go/types/examples/types.go2 b/src/go/types/examples/types.go2 index db70cfe0a1..5c50b5c771 100644 --- a/src/go/types/examples/types.go2 +++ b/src/go/types/examples/types.go2 @@ -18,7 +18,6 @@ var _ List(byte) = []byte{} // A generic binary tree might be declared as follows. type Tree(type E) struct { - // TODO(gri) should be able to use Tree w/o repeating type args left, right *Tree(E) payload E } @@ -107,3 +106,43 @@ var _ T // ERROR cannot use generic type T // In type context, parameterized (generic) types cannot use instantiation syntax. // See also NOTES entry from 12/4/2019. var _ (T /* ERROR cannot use generic type T */ )( /* ERROR expected ';' */ int) + +// All types may be parameterized, including interfaces. +type I1(type T) interface{ + m1(T) +} + +// To differentiate an instantiated interface such as I1(int) from a method +// declaration when embedding it, we must use parentheses around it: +type I2 interface { + I1(int) // method! + (I1(string)) // embedded I1 +} + +func _() { + var x I2 + x.I1(0) + x.m1("foo") +} + +// Generally, we now allow any embedded interfaces to be parenthesized. +// Because that removes the restriction that embedded interfaces must +// be named, we can even "inline" interface literals (though that's of +// little practial use - it's simply more complicated to disallow them). +// Here are some more examples: +type I0 interface { + m0() +} + +type I3 interface { + I0 + (I1(bool)) + ((interface{ m(string) })) +} + +func _() { + var x I3 + x.m0() + x.m1(true) + x.m("foo") +} diff --git a/src/go/types/testdata/tmp.go2 b/src/go/types/testdata/tmp.go2 index 2d18ac3d8d..3dc51460b5 100644 --- a/src/go/types/testdata/tmp.go2 +++ b/src/go/types/testdata/tmp.go2 @@ -14,8 +14,12 @@ func (S) m(type B)(B) var _ I = S{} +type E(type T) interface { + m(type B)(B) +} + type II interface{ - m(type C)(C) + (E(int)) mm() int }