Skip to content

Commit 7cf3268

Browse files
committed
go/parser: parser to accept ~x as unary expression
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 <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent 4f89884 commit 7cf3268

File tree

5 files changed

+27
-20
lines changed

5 files changed

+27
-20
lines changed

src/go/parser/parser.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1722,7 +1722,7 @@ func (p *parser) parseUnaryExpr() ast.Expr {
17221722
}
17231723

17241724
switch p.tok {
1725-
case token.ADD, token.SUB, token.NOT, token.XOR, token.AND:
1725+
case token.ADD, token.SUB, token.NOT, token.XOR, token.AND, token.TILDE:
17261726
pos, op := p.pos, p.tok
17271727
p.next()
17281728
x := p.parseUnaryExpr()

src/go/parser/testdata/typeset.go2

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,18 @@ type (
4848

4949
// Single-expression type parameter lists and those that don't start
5050
// with a (type parameter) name are considered array sizes.
51-
// The term must be a valid expression (it could be a type - and then
52-
// a type-checker will complain - but we don't allow ~ in the expr).
53-
// TODO(rfindley): Improve error recover here. In these cases go/parser error
54-
// recovery is worse than cmd/compile/internal/syntax, and unnecessary type
55-
// declarations had to be inserted to force synchronization.
56-
type _[t] t
57-
type _[~ /* ERROR "expected operand" */ t] t
58-
type /* ERROR "expected ']'" */ Sync int // placeholder to synchronize the parser
59-
type _[t|t] t
60-
type _[~ /* ERROR "expected operand" */ t|t] t
61-
type /* ERROR "expected ']'" */ Sync int // placeholder to synchronize the parser
62-
type _[t| ~ /* ERROR "expected operand" */ t] t
63-
type /* ERROR "expected ']'" */ Sync int // placeholder to synchronize the parser
64-
type _[~ /* ERROR "expected operand" */ t|~t] t
65-
type /* ERROR "expected ']'" */ Sync int // placeholder to synchronize the parser
51+
// The term must be a valid expression (it could be a type incl. a
52+
// tilde term) but the type-checker will complain.
53+
type (
54+
_[t] t
55+
_[t|t] t
56+
57+
// These are invalid and the type-checker will complain.
58+
_[~t] t
59+
_[~t|t] t
60+
_[t|~t] t
61+
_[~t|~t] t
62+
)
6663

6764
type _[_ t, t /* ERROR "type parameters must be named" */ ] t
6865
type _[_ ~t, t /* ERROR "type parameters must be named" */ ] t

src/go/types/expr.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) {
195195
x.typ = ch.elem
196196
check.hasCallOrRecv = true
197197
return
198+
199+
case token.TILDE:
200+
// Provide a better error position and message than what check.op below could do.
201+
check.error(e, _UndefinedOp, "cannot use ~ outside of interface or type constraint")
202+
x.mode = invalid
203+
return
198204
}
199205

200206
if !check.op(unaryOpPredicates, x, e.Op) {

src/go/types/testdata/check/expr0.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,10 @@ func _() {
178178
_ = -g /* ERROR 2-valued g */ ()
179179
_ = <-g /* ERROR 2-valued g */ ()
180180
}
181+
182+
// ~ is accepted as unary operator only permitted in interface type elements
183+
var (
184+
_ = ~ /* ERROR cannot use ~ outside of interface or type constraint */ 0
185+
_ = ~ /* ERROR cannot use ~ outside of interface or type constraint */ "foo"
186+
_ = ~ /* ERROR cannot use ~ outside of interface or type constraint */ i0
187+
)

src/go/types/testdata/fixedbugs/issue49482.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,4 @@ type _[P /* ERROR non-function P */ (*int)] int
2222
type _[P *struct /* ERROR "expected expression" */ {}| int /* ERROR "not an expression" */ ] struct{}
2323

2424
// The following fails to parse, due to the '~'
25-
type _[P *struct /* ERROR "expected expression" */ {}|~ /* ERROR "expected operand" */ int] struct{}
26-
27-
// This is fragile: 'var' synchronizes the parser, and we absorb the rest of the errors.
28-
var /* ERROR "expected ']'" */ _ /* ERROR "value or type" */
25+
type _[P *struct /* ERROR "expected expression" */ {}|~int /* ERROR "not an expression" */ ] struct{}

0 commit comments

Comments
 (0)