]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/syntax: fix printing of ambiguous constraint literals
authorRobert Griesemer <gri@golang.org>
Thu, 5 May 2022 06:02:05 +0000 (23:02 -0700)
committerRobert Griesemer <gri@golang.org>
Fri, 6 May 2022 16:18:28 +0000 (16:18 +0000)
Without this change, the type parameter list "[P T | T]" is printed
as "[P T | T,]" in an attempt to avoid an ambiguity. But the type
parameter P cannot syntactically combine with the constraint T | T
and make a new valid expression.

This change introduces a specific combinesWithName predicate that
reports whether a constraint expression can combine with a type
parameter name to form a new valid (value) expression.

Use combinesWithName to accurately determine when a comma is needed.

For #49482.

Change-Id: Id1d17a18f0c9af04495da7b0453e83798f32b04a
Reviewed-on: https://go-review.googlesource.com/c/go/+/404397
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/syntax/printer.go
src/cmd/compile/internal/syntax/printer_test.go

index ff3fd9bf472aa29feb6d976be80d4aa5f3cc5520..9cf2cc8220737a553a04f389087398a612af62e4 100644 (file)
@@ -919,16 +919,38 @@ func (p *printer) printParameterList(list []*Field, tok token) {
                }
                p.printNode(unparen(f.Type)) // no need for (extra) parentheses around parameter types
        }
-       // A type parameter list [P *T] where T is not a type element requires a comma as in [P *T,]
-       // so that it's not parsed as [P*T].
-       if tok == _Type && len(list) == 1 {
-               if t, _ := list[0].Type.(*Operation); t != nil && !isTypeElem(t) {
-                       p.print(_Comma)
-               }
+       // 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].
+       if tok == _Type && len(list) == 1 && combinesWithName(list[0].Type) {
+               p.print(_Comma)
        }
        p.print(close)
 }
 
+// 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 Expr) bool {
+       switch x := x.(type) {
+       case *Operation:
+               if x.Y == nil {
+                       // name *x.X combines to name*x.X if x.X is not a type element
+                       return x.Op == Mul && !isTypeElem(x.X)
+               }
+               // binary expressions
+               return combinesWithName(x.X) && !isTypeElem(x.Y)
+       case *ParenExpr:
+               // name(x) combines but we are making sure at
+               // the call site that x is never parenthesized.
+               panic("unexpected parenthesized expression")
+       }
+       return false
+}
+
 func (p *printer) printStmtList(list []Stmt, braces bool) {
        for i, x := range list {
                p.print(x, _Semi)
index 25155e5cc69ff41ab9c86542adc5846a5eafa1e0..863713c12da50c7f57f5f89529898c90415a379f 100644 (file)
@@ -76,6 +76,8 @@ var stringTests = [][2]string{
 
        // a type literal in an |-expression indicates a type parameter list (blank after type parameter list and type)
        dup("package p; type _[P *[]int] struct{}"),
+       dup("package p; type _[P T | T] struct{}"),
+       dup("package p; type _[P T | T | T | T] struct{}"),
        dup("package p; type _[P *T | T, Q T] struct{}"),
        dup("package p; type _[P *[]T | T] struct{}"),
        dup("package p; type _[P *T | T | T | T | ~T] struct{}"),
@@ -84,7 +86,7 @@ var stringTests = [][2]string{
        dup("package p; type _[P <-chan int] struct{}"),
        dup("package p; type _[P *T | struct{} | T] struct{}"),
 
-       // a trailing comma always indicates a type parameter list (blank after type parameter list and type)
+       // a trailing comma always indicates a (possibly invalid) type parameter list (blank after type parameter list and type)
        dup("package p; type _[P *T,] struct{}"),
        dup("package p; type _[P *T | T,] struct{}"),
        dup("package p; type _[P *T | <-T | T,] struct{}"),