]> Cypherpunks repositories - gostls13.git/commitdiff
go/parser: parser to accept ~x as unary expression
authorRobert Griesemer <gri@golang.org>
Tue, 3 May 2022 17:49:56 +0000 (10:49 -0700)
committerRobert Griesemer <gri@golang.org>
Wed, 4 May 2022 04:21:29 +0000 (04:21 +0000)
This is a port of CL 402255 from the syntax package to go/parser
with adjustments because of the different AST structure.

Accept ~x as ordinary unary expression in the parser but recognize
such expressions as invalid in the type checker.

This change opens the door to recognizing complex type constraint
literals such as `*E|~int` in `[P *E|~int]` and parse them correctly
instead of reporting a parse error because `P*E|~int` syntactically
looks like an incorrect array length expression (binary expression
where the RHS of | is an invalid unary expression ~int).

As a result, the parser is more forgiving with expressions but the
type checker will reject invalid uses as before.

We could pass extra information into the binary/unary expression
parse functions to prevent the use of ~ in invalid situations but
it doesn't seem worth the trouble. In fact it may be advantageous
to allow a more liberal expression syntax especially in the presence
of errors (better parser synchronization after an error).

Preparation for fixing #52559.

Change-Id: I48562cf40ccf5f14c20fcd92c40a0303b2d8b2b8
Reviewed-on: https://go-review.googlesource.com/c/go/+/403696
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: David Chase <drchase@google.com>
src/go/parser/parser.go
src/go/parser/testdata/typeset.go2
src/go/types/expr.go
src/go/types/testdata/check/expr0.go
src/go/types/testdata/fixedbugs/issue49482.go

index 39ba9b33a7f8bfc61381d113d6404c13b9f54399..3c9be3162611a9366ac1fe051e0a37d6c682d7f1 100644 (file)
@@ -1722,7 +1722,7 @@ func (p *parser) parseUnaryExpr() ast.Expr {
        }
 
        switch p.tok {
-       case token.ADD, token.SUB, token.NOT, token.XOR, token.AND:
+       case token.ADD, token.SUB, token.NOT, token.XOR, token.AND, token.TILDE:
                pos, op := p.pos, p.tok
                p.next()
                x := p.parseUnaryExpr()
index aa18e8ccff05ac07b37af58f6086007fb910a1e8..7844c22212298a306a5347b24078069cdc266e9e 100644 (file)
@@ -48,21 +48,18 @@ type (
 
 // Single-expression type parameter lists and those that don't start
 // with a (type parameter) name are considered array sizes.
-// The term must be a valid expression (it could be a type - and then
-// a type-checker will complain - but we don't allow ~ in the expr).
-// TODO(rfindley): Improve error recover here. In these cases go/parser error
-// recovery is worse than cmd/compile/internal/syntax, and unnecessary type
-// declarations had to be inserted to force synchronization.
-type _[t] t
-type _[~ /* ERROR "expected operand" */ t] t
-type /* ERROR "expected ']'" */ Sync int  // placeholder to synchronize the parser
-type _[t|t] t
-type _[~ /* ERROR "expected operand" */ t|t] t
-type /* ERROR "expected ']'" */ Sync int  // placeholder to synchronize the parser
-type _[t| ~ /* ERROR "expected operand" */ t] t
-type /* ERROR "expected ']'" */ Sync int  // placeholder to synchronize the parser
-type _[~ /* ERROR "expected operand" */ t|~t] t
-type /* ERROR "expected ']'" */ Sync int  // placeholder to synchronize the parser
+// The term must be a valid expression (it could be a type incl. a
+// tilde term) but the type-checker will complain.
+type (
+        _[t] t
+        _[t|t] t
+
+        // These are invalid and the type-checker will complain.
+        _[~t] t
+        _[~t|t] t
+        _[t|~t] t
+        _[~t|~t] t
+)
 
 type _[_ t, t /* ERROR "type parameters must be named" */ ] t
 type _[_ ~t, t /* ERROR "type parameters must be named" */ ] t
index 70914d54852e3aa19cd626f98c312eb8bdbca7f4..14ca0c20d80cbf9dc08ba8ba55fa134093697d9b 100644 (file)
@@ -195,6 +195,12 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) {
                x.typ = ch.elem
                check.hasCallOrRecv = true
                return
+
+       case token.TILDE:
+               // Provide a better error position and message than what check.op below could do.
+               check.error(e, _UndefinedOp, "cannot use ~ outside of interface or type constraint")
+               x.mode = invalid
+               return
        }
 
        if !check.op(unaryOpPredicates, x, e.Op) {
index 1aac726327b28265af426a838d801fe15ed1ab6d..19923777df1d475533dbbaff3a1f3e9b316103f9 100644 (file)
@@ -178,3 +178,10 @@ func _() {
        _ = -g /* ERROR 2-valued g */ ()
        _ = <-g /* ERROR 2-valued g */ ()
 }
+
+// ~ is accepted as unary operator only permitted in interface type elements
+var (
+       _ = ~ /* ERROR cannot use ~ outside of interface or type constraint */ 0
+       _ = ~ /* ERROR cannot use ~ outside of interface or type constraint */ "foo"
+       _ = ~ /* ERROR cannot use ~ outside of interface or type constraint */ i0
+)
index 4c6579ed6856c8aeb5b1480fe9beaa1836a50fe3..f103d3b952f9739d62f9c21607af564370dcbcbb 100644 (file)
@@ -22,7 +22,4 @@ type _[P /* ERROR non-function P */ (*int)] int
 type _[P *struct /* ERROR "expected expression" */ {}| int /* ERROR "not an expression" */ ] struct{}
 
 // The following fails to parse, due to the '~'
-type _[P *struct /* ERROR "expected expression" */ {}|~ /* ERROR "expected operand" */ int] struct{}
-
-// This is fragile: 'var' synchronizes the parser, and we absorb the rest of the errors.
-var /* ERROR "expected ']'" */ _ /* ERROR "value or type" */
+type _[P *struct /* ERROR "expected expression" */ {}|~int /* ERROR "not an expression" */ ] struct{}