]> Cypherpunks repositories - gostls13.git/commitdiff
go/printer: fix printing for possibly ambiguous type constraints
authorRobert Griesemer <gri@golang.org>
Thu, 5 May 2022 01:08:36 +0000 (18:08 -0700)
committerRobert Griesemer <gri@golang.org>
Fri, 6 May 2022 16:18:34 +0000 (16:18 +0000)
This is a port of the printer changes from CLs 402256 and 404397
in the syntax package to go/printer, with adjustments for the
different AST structure and test framework.

For #52559.

Change-Id: Ib7165979a4bd9df91f7f0f1c23b756a41ca31eb3
Reviewed-on: https://go-review.googlesource.com/c/go/+/404194
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
src/go/printer/nodes.go
src/go/printer/testdata/generics.golden
src/go/printer/testdata/generics.input

index 0494f99d246c9a4ba123ea12c03f2b0af3967ed8..2cc84dc6a9b2c5e6ac6535770c12ff1f0d3509f1 100644 (file)
@@ -381,16 +381,12 @@ func (p *printer) parameters(fields *ast.FieldList, mode paramMode) {
                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
@@ -402,17 +398,38 @@ func (p *printer) parameters(fields *ast.FieldList, mode paramMode) {
        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
 }
index f19341680ce85e35fa8d0c4b4b4b59828749ea3d..7ddf20b3d18aaea1d7762714c456ad8e59c71316 100644 (file)
@@ -41,6 +41,8 @@ func _[P struct{ f int }, Q *P]()     {}
 
 // 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{}
@@ -51,19 +53,34 @@ type _[P *struct{}] 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)
index 66e1554f7ffb8359838be524849721a9b18c2e09..4940f9319a6b432463670677674937c46739b3ff 100644 (file)
@@ -38,6 +38,8 @@ func _[P struct{f int}, Q *P]() {}
 
 // 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{}
@@ -48,19 +50,34 @@ type _[P *struct{}] 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)