Skip to content

Commit 920b9ab

Browse files
committed
cmd/compile/internal/syntax: accept all valid type parameter lists
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 #49482. Change-Id: I269b6291999bf60dc697d33d24a5635f01e065b9 Reviewed-on: https://go-review.googlesource.com/c/go/+/402256 Reviewed-by: Benny Siegert <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 3e00bd0 commit 920b9ab

File tree

5 files changed

+158
-88
lines changed

5 files changed

+158
-88
lines changed

src/cmd/compile/internal/syntax/parser.go

+74-51
Original file line numberDiff line numberDiff line change
@@ -599,10 +599,12 @@ func (p *parser) typeDecl(group *Group) Decl {
599599
// with a "[" as in: P []E. In that case, simply parsing
600600
// an expression would lead to an error: P[] is invalid.
601601
// But since index or slice expressions are never constant
602-
// and thus invalid array length expressions, if we see a
603-
// "[" following a name it must be the start of an array
604-
// or slice constraint. Only if we don't see a "[" do we
605-
// need to parse a full expression.
602+
// and thus invalid array length expressions, if the name
603+
// is followed by "[" it must be the start of an array or
604+
// slice constraint. Only if we don't see a "[" do we
605+
// need to parse a full expression. Notably, name <- x
606+
// is not a concern because name <- x is a statement and
607+
// not an expression.
606608
var x Expr = p.name()
607609
if p.tok != _Lbrack {
608610
// To parse the expression starting with name, expand
@@ -612,53 +614,22 @@ func (p *parser) typeDecl(group *Group) Decl {
612614
x = p.binaryExpr(p.pexpr(x, false), 0)
613615
p.xnest--
614616
}
615-
616-
// analyze the cases
617-
var pname *Name // pname != nil means pname is the type parameter name
618-
var ptype Expr // ptype != nil means ptype is the type parameter type; pname != nil in this case
619-
switch t := x.(type) {
620-
case *Name:
621-
// Unless we see a "]", we are at the start of a type parameter list.
622-
if p.tok != _Rbrack {
623-
// d.Name "[" name ...
624-
pname = t
625-
// no ptype
626-
}
627-
case *Operation:
628-
// If we have an expression of the form name*T, and T is a (possibly
629-
// parenthesized) type literal or the next token is a comma, we are
630-
// at the start of a type parameter list.
631-
if name, _ := t.X.(*Name); name != nil {
632-
if t.Op == Mul && (isTypeLit(t.Y) || p.tok == _Comma) {
633-
// d.Name "[" name "*" t.Y
634-
// d.Name "[" name "*" t.Y ","
635-
t.X, t.Y = t.Y, nil // convert t into unary *t.Y
636-
pname = name
637-
ptype = t
638-
}
639-
}
640-
case *CallExpr:
641-
// If we have an expression of the form name(T), and T is a (possibly
642-
// parenthesized) type literal or the next token is a comma, we are
643-
// at the start of a type parameter list.
644-
if name, _ := t.Fun.(*Name); name != nil {
645-
if len(t.ArgList) == 1 && !t.HasDots && (isTypeLit(t.ArgList[0]) || p.tok == _Comma) {
646-
// d.Name "[" name "(" t.ArgList[0] ")"
647-
// d.Name "[" name "(" t.ArgList[0] ")" ","
648-
pname = name
649-
ptype = t.ArgList[0]
650-
}
651-
}
652-
}
653-
654-
if pname != nil {
617+
// Analyze expression x. If we can split x into a type parameter
618+
// name, possibly followed by a type parameter type, we consider
619+
// this the start of a type parameter list, with some caveats:
620+
// a single name followed by "]" tilts the decision towards an
621+
// array declaration; a type parameter type that could also be
622+
// an ordinary expression but which is followed by a comma tilts
623+
// the decision towards a type parameter list.
624+
if pname, ptype := extractName(x, p.tok == _Comma); pname != nil && (ptype != nil || p.tok != _Rbrack) {
655625
// d.Name "[" pname ...
656626
// d.Name "[" pname ptype ...
657627
// d.Name "[" pname ptype "," ...
658-
d.TParamList = p.paramList(pname, ptype, _Rbrack, true)
628+
d.TParamList = p.paramList(pname, ptype, _Rbrack, true) // ptype may be nil
659629
d.Alias = p.gotAssign()
660630
d.Type = p.typeOrNil()
661631
} else {
632+
// d.Name "[" pname "]" ...
662633
// d.Name "[" x ...
663634
d.Type = p.arrayType(pos, x)
664635
}
@@ -684,17 +655,69 @@ func (p *parser) typeDecl(group *Group) Decl {
684655
return d
685656
}
686657

687-
// isTypeLit reports whether x is a (possibly parenthesized) type literal.
688-
func isTypeLit(x Expr) bool {
658+
// extractName splits the expression x into (name, expr) if syntactically
659+
// x can be written as name expr. The split only happens if expr is a type
660+
// element (per the isTypeElem predicate) or if force is set.
661+
// If x is just a name, the result is (name, nil). If the split succeeds,
662+
// the result is (name, expr). Otherwise the result is (nil, x).
663+
// Examples:
664+
//
665+
// x force name expr
666+
// ------------------------------------
667+
// P*[]int T/F P *[]int
668+
// P*E T P *E
669+
// P*E F nil P*E
670+
// P([]int) T/F P []int
671+
// P(E) T P E
672+
// P(E) F nil P(E)
673+
// P*E|F|~G T/F P *E|F|~G
674+
// P*E|F|G T P *E|F|G
675+
// P*E|F|G F nil P*E|F|G
676+
func extractName(x Expr, force bool) (*Name, Expr) {
677+
switch x := x.(type) {
678+
case *Name:
679+
return x, nil
680+
case *Operation:
681+
if x.Y == nil {
682+
break // unary expr
683+
}
684+
switch x.Op {
685+
case Mul:
686+
if name, _ := x.X.(*Name); name != nil && (isTypeElem(x.Y) || force) {
687+
// x = name *x.Y
688+
op := *x
689+
op.X, op.Y = op.Y, nil // change op into unary *op.Y
690+
return name, &op
691+
}
692+
case Or:
693+
if name, lhs := extractName(x.X, isTypeElem(x.Y) || force); name != nil && lhs != nil { // note: lhs should never be nil
694+
// x = name lhs|x.Y
695+
op := *x
696+
op.X = lhs
697+
return name, &op
698+
}
699+
}
700+
case *CallExpr:
701+
if name, _ := x.Fun.(*Name); name != nil {
702+
if len(x.ArgList) == 1 && !x.HasDots && (isTypeElem(x.ArgList[0]) || force) {
703+
// x = name "(" x.ArgList[0] ")"
704+
return name, x.ArgList[0]
705+
}
706+
}
707+
}
708+
return nil, x
709+
}
710+
711+
// isTypeElem reports whether x is a (possibly parenthesized) type element expression.
712+
// The result is false if x could be a type element OR an ordinary (value) expression.
713+
func isTypeElem(x Expr) bool {
689714
switch x := x.(type) {
690715
case *ArrayType, *StructType, *FuncType, *InterfaceType, *SliceType, *MapType, *ChanType:
691716
return true
692717
case *Operation:
693-
// *T may be a pointer dereferenciation.
694-
// Only consider *T as type literal if T is a type literal.
695-
return x.Op == Mul && x.Y == nil && isTypeLit(x.X)
718+
return isTypeElem(x.X) || (x.Y != nil && isTypeElem(x.Y)) || x.Op == Tilde
696719
case *ParenExpr:
697-
return isTypeLit(x.X)
720+
return isTypeElem(x.X)
698721
}
699722
return false
700723
}

src/cmd/compile/internal/syntax/printer.go

+12-9
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,7 @@ func (p *printer) printRawNode(n Node) {
666666
}
667667
p.print(n.Name)
668668
if n.TParamList != nil {
669-
p.printParameterList(n.TParamList, true)
669+
p.printParameterList(n.TParamList, _Type)
670670
}
671671
p.print(blank)
672672
if n.Alias {
@@ -698,7 +698,7 @@ func (p *printer) printRawNode(n Node) {
698698
}
699699
p.print(n.Name)
700700
if n.TParamList != nil {
701-
p.printParameterList(n.TParamList, true)
701+
p.printParameterList(n.TParamList, _Func)
702702
}
703703
p.printSignature(n.Type)
704704
if n.Body != nil {
@@ -883,20 +883,23 @@ func (p *printer) printDeclList(list []Decl) {
883883
}
884884

885885
func (p *printer) printSignature(sig *FuncType) {
886-
p.printParameterList(sig.ParamList, false)
886+
p.printParameterList(sig.ParamList, 0)
887887
if list := sig.ResultList; list != nil {
888888
p.print(blank)
889889
if len(list) == 1 && list[0].Name == nil {
890890
p.printNode(list[0].Type)
891891
} else {
892-
p.printParameterList(list, false)
892+
p.printParameterList(list, 0)
893893
}
894894
}
895895
}
896896

897-
func (p *printer) printParameterList(list []*Field, types bool) {
897+
// If tok != 0 print a type parameter list: tok == _Type means
898+
// a type parameter list for a type, tok == _Func means a type
899+
// parameter list for a func.
900+
func (p *printer) printParameterList(list []*Field, tok token) {
898901
open, close := _Lparen, _Rparen
899-
if types {
902+
if tok != 0 {
900903
open, close = _Lbrack, _Rbrack
901904
}
902905
p.print(open)
@@ -916,10 +919,10 @@ func (p *printer) printParameterList(list []*Field, types bool) {
916919
}
917920
p.printNode(unparen(f.Type)) // no need for (extra) parentheses around parameter types
918921
}
919-
// A type parameter list [P *T] where T is not a type literal requires a comma as in [P *T,]
922+
// A type parameter list [P *T] where T is not a type element requires a comma as in [P *T,]
920923
// so that it's not parsed as [P*T].
921-
if types && len(list) == 1 {
922-
if t, _ := list[0].Type.(*Operation); t != nil && t.Op == Mul && t.Y == nil && !isTypeLit(t.X) {
924+
if tok == _Type && len(list) == 1 {
925+
if t, _ := list[0].Type.(*Operation); t != nil && !isTypeElem(t) {
923926
p.print(_Comma)
924927
}
925928
}

src/cmd/compile/internal/syntax/printer_test.go

+39-18
Original file line numberDiff line numberDiff line change
@@ -57,48 +57,69 @@ var stringTests = [][2]string{
5757
dup("package p"),
5858
dup("package p; type _ int; type T1 = struct{}; type ( _ *struct{}; T2 = float32 )"),
5959

60-
// generic type declarations
60+
// generic type declarations (given type separated with blank from LHS)
6161
dup("package p; type _[T any] struct{}"),
6262
dup("package p; type _[A, B, C interface{m()}] struct{}"),
6363
dup("package p; type _[T any, A, B, C interface{m()}, X, Y, Z interface{~int}] struct{}"),
6464

65+
dup("package p; type _[P *struct{}] struct{}"),
6566
dup("package p; type _[P *T,] struct{}"),
6667
dup("package p; type _[P *T, _ any] struct{}"),
6768
{"package p; type _[P (*T),] struct{}", "package p; type _[P *T,] struct{}"},
6869
{"package p; type _[P (*T), _ any] struct{}", "package p; type _[P *T, _ any] struct{}"},
6970
{"package p; type _[P (T),] struct{}", "package p; type _[P T] struct{}"},
7071
{"package p; type _[P (T), _ any] struct{}", "package p; type _[P T, _ any] struct{}"},
7172

72-
dup("package p; type _[P *struct{}] struct{}"),
7373
{"package p; type _[P (*struct{})] struct{}", "package p; type _[P *struct{}] struct{}"},
7474
{"package p; type _[P ([]int)] struct{}", "package p; type _[P []int] struct{}"},
75-
76-
dup("package p; type _ [P(T)]struct{}"),
77-
dup("package p; type _ [P((T))]struct{}"),
78-
dup("package p; type _ [P * *T]struct{}"),
79-
dup("package p; type _ [P * T]struct{}"),
80-
dup("package p; type _ [P(*T)]struct{}"),
81-
dup("package p; type _ [P(**T)]struct{}"),
82-
dup("package p; type _ [P * T - T]struct{}"),
83-
84-
// array type declarations
85-
dup("package p; type _ [P * T]struct{}"),
86-
dup("package p; type _ [P * T - T]struct{}"),
75+
{"package p; type _[P ([]int) | int] struct{}", "package p; type _[P []int | int] struct{}"},
76+
77+
// a type literal in an |-expression indicates a type parameter list (blank after type parameter list and type)
78+
dup("package p; type _[P *[]int] struct{}"),
79+
dup("package p; type _[P *T | T, Q T] struct{}"),
80+
dup("package p; type _[P *[]T | T] struct{}"),
81+
dup("package p; type _[P *T | T | T | T | ~T] struct{}"),
82+
dup("package p; type _[P *T | T | T | ~T | T] struct{}"),
83+
dup("package p; type _[P *T | T | struct{} | T] struct{}"),
84+
dup("package p; type _[P <-chan int] struct{}"),
85+
dup("package p; type _[P *T | struct{} | T] struct{}"),
86+
87+
// a trailing comma always indicates a type parameter list (blank after type parameter list and type)
88+
dup("package p; type _[P *T,] struct{}"),
89+
dup("package p; type _[P *T | T,] struct{}"),
90+
dup("package p; type _[P *T | <-T | T,] struct{}"),
91+
92+
// slice/array type declarations (no blank between array length and element type)
93+
dup("package p; type _ []byte"),
94+
dup("package p; type _ [n]byte"),
95+
dup("package p; type _ [P(T)]byte"),
96+
dup("package p; type _ [P((T))]byte"),
97+
dup("package p; type _ [P * *T]byte"),
98+
dup("package p; type _ [P * T]byte"),
99+
dup("package p; type _ [P(*T)]byte"),
100+
dup("package p; type _ [P(**T)]byte"),
101+
dup("package p; type _ [P * T - T]byte"),
102+
dup("package p; type _ [P * T - T]byte"),
103+
dup("package p; type _ [P * T | T]byte"),
104+
dup("package p; type _ [P * T | <-T | T]byte"),
87105

88106
// generic function declarations
89107
dup("package p; func _[T any]()"),
90108
dup("package p; func _[A, B, C interface{m()}]()"),
91109
dup("package p; func _[T any, A, B, C interface{m()}, X, Y, Z interface{~int}]()"),
92110

111+
// generic functions with elided interfaces in type constraints
112+
dup("package p; func _[P *T]() {}"),
113+
dup("package p; func _[P *T | T | T | T | ~T]() {}"),
114+
dup("package p; func _[P *T | T | struct{} | T]() {}"),
115+
dup("package p; func _[P ~int, Q int | string]() {}"),
116+
dup("package p; func _[P struct{f int}, Q *P]() {}"),
117+
93118
// methods with generic receiver types
94119
dup("package p; func (R[T]) _()"),
95120
dup("package p; func (*R[A, B, C]) _()"),
96121
dup("package p; func (_ *R[A, B, C]) _()"),
97122

98-
// type constraint literals with elided interfaces
99-
dup("package p; func _[P ~int, Q int | string]() {}"),
100-
dup("package p; func _[P struct{f int}, Q *P]() {}"),
101-
102123
// channels
103124
dup("package p; type _ chan chan int"),
104125
dup("package p; type _ chan (<-chan int)"),

src/cmd/compile/internal/syntax/testdata/tparams.go

+22
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,25 @@ func f[a, b /* ERROR missing type constraint */ ]()
2222
func f[a t, b t, c /* ERROR missing type constraint */ ]()
2323

2424
func f[a b, /* ERROR expecting ] */ 0] ()
25+
26+
// issue #49482
27+
type (
28+
t[a *[]int] struct{}
29+
t[a *t,] struct{}
30+
t[a *t|[]int] struct{}
31+
t[a *t|t,] struct{}
32+
t[a *t|~t,] struct{}
33+
t[a *struct{}|t] struct{}
34+
t[a *t|struct{}] struct{}
35+
t[a *struct{}|~t] struct{}
36+
)
37+
38+
// issue #51488
39+
type (
40+
t[a *t|t,] struct{}
41+
t[a *t|t, b t] struct{}
42+
t[a *t|t] struct{}
43+
t[a *[]t|t] struct{}
44+
t[a ([]t)] struct{}
45+
t[a ([]t)|t] struct{}
46+
)

src/cmd/compile/internal/types2/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 "not an 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 "not an 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)