if closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing {
p.print(token.COMMA)
p.linebreak(closing, 0, ignore, true)
- } else if mode == typeTParam && fields.NumFields() == 1 {
- // Otherwise, if we are in a type parameter list that could be confused
- // with the constant array length expression [P*C], print a comma so that
- // parsing is unambiguous.
- //
- // Note that while ParenExprs can also be ambiguous (issue #49482), the
- // printed type is never parenthesized (stripParensAlways is used above).
- if t, _ := fields.List[0].Type.(*ast.StarExpr); t != nil && !isTypeLit(t.X) {
- p.print(token.COMMA)
- }
+ } else if mode == typeTParam && fields.NumFields() == 1 && combinesWithName(fields.List[0].Type) {
+ // A type parameter list [P T] where the name P and the type expression T syntactically
+ // combine to another valid (value) expression requires a trailing comma, as in [P *T,]
+ // (or an enclosing interface as in [P interface(*T)]), so that the type parameter list
+ // is not parsed as an array length [P*T].
+ p.print(token.COMMA)
}
// unindent if we indented
p.print(fields.Closing, closeTok)
}
-// isTypeLit reports whether x is a (possibly parenthesized) type literal.
-func isTypeLit(x ast.Expr) bool {
+// combinesWithName reports whether a name followed by the expression x
+// syntactically combines to another valid (value) expression. For instance
+// using *T for x, "name *T" syntactically appears as the expression x*T.
+// On the other hand, using P|Q or *P|~Q for x, "name P|Q" or name *P|~Q"
+// cannot be combined into a valid (value) expression.
+func combinesWithName(x ast.Expr) bool {
+ switch x := x.(type) {
+ case *ast.StarExpr:
+ // name *x.X combines to name*x.X if x.X is not a type element
+ return !isTypeElem(x.X)
+ case *ast.BinaryExpr:
+ return combinesWithName(x.X) && !isTypeElem(x.Y)
+ case *ast.ParenExpr:
+ // name(x) combines but we are making sure at
+ // the call site that x is never parenthesized.
+ panic("unexpected parenthesized expression")
+ }
+ return false
+}
+
+// isTypeElem reports whether x is a (possibly parenthesized) type element expression.
+// The result is false if x could be a type element OR an ordinary (value) expression.
+func isTypeElem(x ast.Expr) bool {
switch x := x.(type) {
case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
return true
- case *ast.StarExpr:
- // *T may be a pointer dereferenciation.
- // Only consider *T as type literal if T is a type literal.
- return isTypeLit(x.X)
+ case *ast.UnaryExpr:
+ return x.Op == token.TILDE
+ case *ast.BinaryExpr:
+ return isTypeElem(x.X) || isTypeElem(x.Y)
case *ast.ParenExpr:
- return isTypeLit(x.X)
+ return isTypeElem(x.X)
}
return false
}
// various potentially ambiguous type parameter lists (issue #49482)
type _[P *T,] struct{}
+type _[P T | T] struct{}
+type _[P T | T | T | T] struct{}
type _[P *T, _ any] struct{}
type _[P *T,] struct{}
type _[P *T, _ any] struct{}
type _ [P(*struct{})]struct{}
type _[P []int] struct{}
-// array type declarations
-type _ [P(T)]struct{}
-type _ [P((T))]struct{}
-type _ [P * *T]struct{}
-type _ [P * T]struct{}
-type _ [P(*T)]struct{}
-type _ [P(**T)]struct{}
-type _ [P * T]struct{}
-type _ [P*T - T]struct{}
-
-type _[
- P *T,
-] struct{}
+// a type literal in an |-expression indicates a type parameter list (blank after type parameter list and type)
+type _[P *[]int] struct{}
+type _[P *T | T, Q T] struct{}
+type _[P *[]T | T] struct{}
+type _[P *T | T | T | T | ~T] struct{}
+type _[P *T | T | T | ~T | T] struct{}
+type _[P *T | T | struct{} | T] struct{}
+type _[P <-chan int] struct{}
+type _[P *T | struct{} | T] struct{}
+
+// a trailing comma always indicates a (possibly invalid) type parameter list (blank after type parameter list and type)
+type _[P *T,] struct{}
+type _[P *T | T,] struct{}
+type _[P *T | <-T | T,] struct{}
+
+// slice/array type declarations (no blank between array length and element type)
+type _ []byte
+type _ [n]byte
+type _ [P(T)]byte
+type _ [P((T))]byte
+type _ [P * *T]byte
+type _ [P * T]byte
+type _ [P(*T)]byte
+type _ [P(**T)]byte
+type _ [P*T - T]byte
+type _ [P*T - T]byte
+type _ [P*T | T]byte
+type _ [P*T | <-T | T]byte
// equivalent test cases for potentially ambiguous type parameter lists, except
// for function declarations there is no ambiguity (issue #51548)
// various potentially ambiguous type parameter lists (issue #49482)
type _[P *T,] struct{}
+type _[P T | T] struct{}
+type _[P T | T | T | T] struct{}
type _[P *T, _ any] struct{}
type _[P (*T),] struct{}
type _[P (*T), _ any] struct{}
type _[P (*struct{})] struct{}
type _[P ([]int)] struct{}
-// array type declarations
-type _ [P(T)]struct{}
-type _ [P((T))]struct{}
-type _ [P * *T]struct{}
-type _ [P * T]struct{}
-type _ [P(*T)]struct{}
-type _ [P(**T)]struct{}
-type _ [P * T]struct{}
-type _ [P * T - T]struct{}
-
-type _[
- P *T,
-] struct{}
+// a type literal in an |-expression indicates a type parameter list (blank after type parameter list and type)
+type _[P *[]int] struct{}
+type _[P *T | T, Q T] struct{}
+type _[P *[]T | T] struct{}
+type _[P *T | T | T | T | ~T] struct{}
+type _[P *T | T | T | ~T | T] struct{}
+type _[P *T | T | struct{} | T] struct{}
+type _[P <-chan int] struct{}
+type _[P *T | struct{} | T] struct{}
+
+// a trailing comma always indicates a (possibly invalid) type parameter list (blank after type parameter list and type)
+type _[P *T,] struct{}
+type _[P *T | T,] struct{}
+type _[P *T | <-T | T,] struct{}
+
+// slice/array type declarations (no blank between array length and element type)
+type _ []byte
+type _ [n]byte
+type _ [P(T)]byte
+type _ [P((T))]byte
+type _ [P * *T]byte
+type _ [P * T]byte
+type _ [P(*T)]byte
+type _ [P(**T)]byte
+type _ [P * T - T]byte
+type _ [P * T - T]byte
+type _ [P * T | T]byte
+type _ [P * T | <-T | T]byte
// equivalent test cases for potentially ambiguous type parameter lists, except
// for function declarations there is no ambiguity (issue #51548)