Skip to content

Commit ab0bb52

Browse files
committed
go/parser: accept all valid type parameter lists
This is a port of CL 402256 from the syntax package to go/parser with adjustments because of the different AST structure, and excluding any necessary go/printer changes (separate CL). Type parameter lists starting with the form [name *T|...] or [name (X)|...] may look like an array length expression [x]. Only after parsing the entire initial expression and checking whether the expression contains type elements or is followed by a comma can we make the final decision. This change simplifies the existing parsing strategy: instead of trying to make an upfront decision with limited information (which is insufficient), the parser now parses the start of a type parameter list or array length specification as expression. In a second step, if the expression can be split into a name followed by a type element, or a name followed by an ordinary expression which is succeeded by a comma, we assume a type parameter list (because it can't be an array length). In all other cases we assume an array length specification. Fixes #52559. Change-Id: I11ab6e62b073b78b2331bb6063cf74d2a9eaa236 Reviewed-on: https://go-review.googlesource.com/c/go/+/403937 Run-TryBot: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent fd6ef06 commit ab0bb52

File tree

5 files changed

+144
-85
lines changed

5 files changed

+144
-85
lines changed

src/go/parser/parser.go

+83-71
Original file line numberDiff line numberDiff line change
@@ -785,9 +785,9 @@ func (p *parser) parseParamDecl(name *ast.Ident, typeSetsOK bool) (f field) {
785785
return // don't allow ...type "|" ...
786786

787787
default:
788-
// TODO(rfindley): this looks incorrect in the case of type parameter
789-
// lists.
790-
p.errorExpected(p.pos, ")")
788+
// TODO(rfindley): this is incorrect in the case of type parameter lists
789+
// (should be "']'" in that case)
790+
p.errorExpected(p.pos, "')'")
791791
p.advance(exprEnd)
792792
}
793793

@@ -2592,10 +2592,12 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token
25922592
defer un(trace(p, "TypeSpec"))
25932593
}
25942594

2595-
ident := p.parseIdent()
2596-
spec := &ast.TypeSpec{Doc: doc, Name: ident}
2595+
name := p.parseIdent()
2596+
spec := &ast.TypeSpec{Doc: doc, Name: name}
25972597

25982598
if p.tok == token.LBRACK && p.allowGenerics() {
2599+
// spec.Name "[" ...
2600+
// array/slice type or type parameter list
25992601
lbrack := p.pos
26002602
p.next()
26012603
if p.tok == token.IDENT {
@@ -2608,14 +2610,12 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token
26082610
// with a "[" as in: P []E. In that case, simply parsing
26092611
// an expression would lead to an error: P[] is invalid.
26102612
// But since index or slice expressions are never constant
2611-
// and thus invalid array length expressions, if we see a
2612-
// "[" following a name it must be the start of an array
2613-
// or slice constraint. Only if we don't see a "[" do we
2614-
// need to parse a full expression.
2615-
2616-
// Index or slice expressions are never constant and thus invalid
2617-
// array length expressions. Thus, if we see a "[" following name
2618-
// we can safely assume that "[" name starts a type parameter list.
2613+
// and thus invalid array length expressions, if the name
2614+
// is followed by "[" it must be the start of an array or
2615+
// slice constraint. Only if we don't see a "[" do we
2616+
// need to parse a full expression. Notably, name <- x
2617+
// is not a concern because name <- x is a statement and
2618+
// not an expression.
26192619
var x ast.Expr = p.parseIdent()
26202620
if p.tok != token.LBRACK {
26212621
// To parse the expression starting with name, expand
@@ -2626,58 +2626,21 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token
26262626
x = p.parseBinaryExpr(lhs, token.LowestPrec+1, false)
26272627
p.exprLev--
26282628
}
2629-
2630-
// analyze the cases
2631-
var pname *ast.Ident // pname != nil means pname is the type parameter name
2632-
var ptype ast.Expr // ptype != nil means ptype is the type parameter type; pname != nil in this case
2633-
2634-
switch t := x.(type) {
2635-
case *ast.Ident:
2636-
// Unless we see a "]", we are at the start of a type parameter list.
2637-
if p.tok != token.RBRACK {
2638-
// d.Name "[" name ...
2639-
pname = t
2640-
// no ptype
2641-
}
2642-
case *ast.BinaryExpr:
2643-
// If we have an expression of the form name*T, and T is a (possibly
2644-
// parenthesized) type literal or the next token is a comma, we are
2645-
// at the start of a type parameter list.
2646-
if name, _ := t.X.(*ast.Ident); name != nil {
2647-
if t.Op == token.MUL && (isTypeLit(t.Y) || p.tok == token.COMMA) {
2648-
// d.Name "[" name "*" t.Y
2649-
// d.Name "[" name "*" t.Y ","
2650-
// convert t into unary *t.Y
2651-
pname = name
2652-
ptype = &ast.StarExpr{Star: t.OpPos, X: t.Y}
2653-
}
2654-
}
2655-
if pname == nil {
2656-
// A normal binary expression. Since we passed check=false, we must
2657-
// now check its operands.
2658-
p.checkBinaryExpr(t)
2659-
}
2660-
case *ast.CallExpr:
2661-
// If we have an expression of the form name(T), and T is a (possibly
2662-
// parenthesized) type literal or the next token is a comma, we are
2663-
// at the start of a type parameter list.
2664-
if name, _ := t.Fun.(*ast.Ident); name != nil {
2665-
if len(t.Args) == 1 && !t.Ellipsis.IsValid() && (isTypeLit(t.Args[0]) || p.tok == token.COMMA) {
2666-
// d.Name "[" name "(" t.ArgList[0] ")"
2667-
// d.Name "[" name "(" t.ArgList[0] ")" ","
2668-
pname = name
2669-
ptype = t.Args[0]
2670-
}
2671-
}
2672-
}
2673-
2674-
if pname != nil {
2675-
// d.Name "[" pname ...
2676-
// d.Name "[" pname ptype ...
2677-
// d.Name "[" pname ptype "," ...
2678-
p.parseGenericType(spec, lbrack, pname, ptype)
2629+
// Analyze expression x. If we can split x into a type parameter
2630+
// name, possibly followed by a type parameter type, we consider
2631+
// this the start of a type parameter list, with some caveats:
2632+
// a single name followed by "]" tilts the decision towards an
2633+
// array declaration; a type parameter type that could also be
2634+
// an ordinary expression but which is followed by a comma tilts
2635+
// the decision towards a type parameter list.
2636+
if pname, ptype := extractName(x, p.tok == token.COMMA); pname != nil && (ptype != nil || p.tok != token.RBRACK) {
2637+
// spec.Name "[" pname ...
2638+
// spec.Name "[" pname ptype ...
2639+
// spec.Name "[" pname ptype "," ...
2640+
p.parseGenericType(spec, lbrack, pname, ptype) // ptype may be nil
26792641
} else {
2680-
// d.Name "[" x ...
2642+
// spec.Name "[" pname "]" ...
2643+
// spec.Name "[" x ...
26812644
spec.Type = p.parseArrayType(lbrack, x)
26822645
}
26832646
} else {
@@ -2700,17 +2663,66 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token
27002663
return spec
27012664
}
27022665

2703-
// isTypeLit reports whether x is a (possibly parenthesized) type literal.
2704-
func isTypeLit(x ast.Expr) bool {
2666+
// extractName splits the expression x into (name, expr) if syntactically
2667+
// x can be written as name expr. The split only happens if expr is a type
2668+
// element (per the isTypeElem predicate) or if force is set.
2669+
// If x is just a name, the result is (name, nil). If the split succeeds,
2670+
// the result is (name, expr). Otherwise the result is (nil, x).
2671+
// Examples:
2672+
//
2673+
// x force name expr
2674+
// ------------------------------------
2675+
// P*[]int T/F P *[]int
2676+
// P*E T P *E
2677+
// P*E F nil P*E
2678+
// P([]int) T/F P []int
2679+
// P(E) T P E
2680+
// P(E) F nil P(E)
2681+
// P*E|F|~G T/F P *E|F|~G
2682+
// P*E|F|G T P *E|F|G
2683+
// P*E|F|G F nil P*E|F|G
2684+
func extractName(x ast.Expr, force bool) (*ast.Ident, ast.Expr) {
2685+
switch x := x.(type) {
2686+
case *ast.Ident:
2687+
return x, nil
2688+
case *ast.BinaryExpr:
2689+
switch x.Op {
2690+
case token.MUL:
2691+
if name, _ := x.X.(*ast.Ident); name != nil && (force || isTypeElem(x.Y)) {
2692+
// x = name *x.Y
2693+
return name, &ast.StarExpr{Star: x.OpPos, X: x.Y}
2694+
}
2695+
case token.OR:
2696+
if name, lhs := extractName(x.X, force || isTypeElem(x.Y)); name != nil && lhs != nil {
2697+
// x = name lhs|x.Y
2698+
op := *x
2699+
op.X = lhs
2700+
return name, &op
2701+
}
2702+
}
2703+
case *ast.CallExpr:
2704+
if name, _ := x.Fun.(*ast.Ident); name != nil {
2705+
if len(x.Args) == 1 && x.Ellipsis == token.NoPos && (force || isTypeElem(x.Args[0])) {
2706+
// x = name "(" x.ArgList[0] ")"
2707+
return name, x.Args[0]
2708+
}
2709+
}
2710+
}
2711+
return nil, x
2712+
}
2713+
2714+
// isTypeElem reports whether x is a (possibly parenthesized) type element expression.
2715+
// The result is false if x could be a type element OR an ordinary (value) expression.
2716+
func isTypeElem(x ast.Expr) bool {
27052717
switch x := x.(type) {
27062718
case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
27072719
return true
2708-
case *ast.StarExpr:
2709-
// *T may be a pointer dereferenciation.
2710-
// Only consider *T as type literal if T is a type literal.
2711-
return isTypeLit(x.X)
2720+
case *ast.BinaryExpr:
2721+
return isTypeElem(x.X) || isTypeElem(x.Y)
2722+
case *ast.UnaryExpr:
2723+
return x.Op == token.TILDE
27122724
case *ast.ParenExpr:
2713-
return isTypeLit(x.X)
2725+
return isTypeElem(x.X)
27142726
}
27152727
return false
27162728
}

src/go/parser/testdata/issue49482.go2

+2-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ type (
2929
_ [P*T-T, /* ERROR "unexpected comma" */ ]struct{}
3030
_ [10, /* ERROR "unexpected comma" */ ]struct{}
3131

32-
// These should be parsed as generic type declarations.
33-
_[P *struct /* ERROR "expected expression" */ {}|int] struct{}
34-
_[P *struct /* ERROR "expected expression" */ {}|int|string] struct{}
32+
_[P *struct{}|int] struct{}
33+
_[P *struct{}|int|string] struct{}
3534
)

src/go/parser/testdata/tparams.go2

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2020 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
type _[a /* ERROR "all type parameters must be named" */, b] struct{}
8+
type _[a t, b t, c /* ERROR "all type parameters must be named" */ ] struct{}
9+
type _ struct {
10+
t [n]byte
11+
t[a]
12+
t[a, b]
13+
}
14+
type _ interface {
15+
t[a]
16+
m[ /* ERROR "method must have no type parameters" */ _ _, /* ERROR mixed */ _]()
17+
t[a, b]
18+
}
19+
20+
func _[] /* ERROR "empty type parameter list" */ ()
21+
func _[a /* ERROR "all type parameters must be named" */, b ]()
22+
func _[a t, b t, c /* ERROR "all type parameters must be named" */ ]()
23+
24+
// TODO(rfindley) incorrect error message (see existing TODO in parser)
25+
func f[a b, 0 /* ERROR "expected '\)', found 0" */ ] ()
26+
27+
// issue #49482
28+
type (
29+
_[a *[]int] struct{}
30+
_[a *t,] struct{}
31+
_[a *t|[]int] struct{}
32+
_[a *t|t,] struct{}
33+
_[a *t|~t,] struct{}
34+
_[a *struct{}|t] struct{}
35+
_[a *t|struct{}] struct{}
36+
_[a *struct{}|~t] struct{}
37+
)
38+
39+
// issue #51488
40+
type (
41+
_[a *t|t,] struct{}
42+
_[a *t|t, b t] struct{}
43+
_[a *t|t] struct{}
44+
_[a *[]t|t] struct{}
45+
_[a ([]t)] struct{}
46+
_[a ([]t)|t] struct{}
47+
)

src/go/printer/testdata/generics.golden

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ type _[P T] struct{}
4848
type _[P T, _ any] struct{}
4949

5050
type _[P *struct{}] struct{}
51-
type _[P *struct{}] struct{}
51+
type _ [P(*struct{})]struct{}
5252
type _[P []int] struct{}
5353

5454
// array type declarations

src/go/types/testdata/fixedbugs/issue49482.go

+11-10
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// This file is tested when running "go test -run Manual"
6-
// without source arguments. Use for one-off debugging.
7-
85
package p
96

107
// The following is OK, per the special handling for type literals discussed in issue #49482.
@@ -14,12 +11,16 @@ type _[P (*int),] int
1411

1512
const P = 2 // declare P to avoid noisy 'undeclared name' errors below.
1613

17-
// The following parse as invalid array types.
18-
type _[P *int /* ERROR "int \(type\) is not an expression" */ ] int
19-
type _[P /* ERROR non-function P */ (*int)] int
14+
// The following parse as invalid array types due to parsing ambiguitiues.
15+
type _ [P *int /* ERROR "int \(type\) is not an expression" */ ]int
16+
type _ [P /* ERROR non-function P */ (*int)]int
2017

21-
// The following should be parsed as a generic type, but is instead parsed as an array type.
22-
type _[P *struct /* ERROR "expected expression" */ {}| int /* ERROR "not an expression" */ ] struct{}
18+
// Adding a trailing comma or an enclosing interface resolves the ambiguity.
19+
type _[P *int,] int
20+
type _[P (*int),] int
21+
type _[P interface{*int}] int
22+
type _[P interface{(*int)}] int
2323

24-
// The following fails to parse, due to the '~'
25-
type _[P *struct /* ERROR "expected expression" */ {}|~int /* ERROR "not an expression" */ ] struct{}
24+
// The following parse correctly as valid generic types.
25+
type _[P *struct{} | int] struct{}
26+
type _[P *struct{} | ~int] struct{}

0 commit comments

Comments
 (0)