diff options
| author | Robert Griesemer <gri@golang.org> | 2022-05-04 23:02:05 -0700 |
|---|---|---|
| committer | Robert Griesemer <gri@golang.org> | 2022-05-06 16:18:28 +0000 |
| commit | ea6c82845589a40c35f01122ce4e2931694ffc7f (patch) | |
| tree | 81c0c2a2552f5d42bff3e91dc3b4746bf2be187d | |
| parent | 7f71e1fc28f03afe987881c679a925a6f7e99cac (diff) | |
| download | go-ea6c82845589a40c35f01122ce4e2931694ffc7f.tar.xz | |
cmd/compile/internal/syntax: fix printing of ambiguous constraint literals
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>
| -rw-r--r-- | src/cmd/compile/internal/syntax/printer.go | 34 | ||||
| -rw-r--r-- | src/cmd/compile/internal/syntax/printer_test.go | 4 |
2 files changed, 31 insertions, 7 deletions
diff --git a/src/cmd/compile/internal/syntax/printer.go b/src/cmd/compile/internal/syntax/printer.go index ff3fd9bf47..9cf2cc8220 100644 --- a/src/cmd/compile/internal/syntax/printer.go +++ b/src/cmd/compile/internal/syntax/printer.go @@ -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) diff --git a/src/cmd/compile/internal/syntax/printer_test.go b/src/cmd/compile/internal/syntax/printer_test.go index 25155e5cc6..863713c12d 100644 --- a/src/cmd/compile/internal/syntax/printer_test.go +++ b/src/cmd/compile/internal/syntax/printer_test.go @@ -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{}"), |
