cmd/compile/internal/syntax: parser to accept ~x as unary expression

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 #49482.

Change-Id: I119e8bd9445dfa6460fcd7e0658e3554a34b2769
Reviewed-on: https://go-review.googlesource.com/c/go/+/402255
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer 2022-04-25 16:26:10 -07:00 committed by Gopher Robot
parent e845750744
commit 09ada1af8f
6 changed files with 24 additions and 9 deletions

View file

@ -827,7 +827,7 @@ func (p *parser) unaryExpr() Expr {
switch p.tok {
case _Operator, _Star:
switch p.op {
case Mul, Add, Sub, Not, Xor:
case Mul, Add, Sub, Not, Xor, Tilde:
x := new(Operation)
x.pos = p.pos()
x.Op = p.op

View file

@ -65,15 +65,17 @@ func _[_ t[t] | t[t]]() {}
// 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).
// 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
_[/* ERROR unexpected ~ */ ~t] t
_[t|t] t
_[/* ERROR unexpected ~ */ ~t|t] t
_[t| /* ERROR unexpected ~ */ ~t] t
_[/* ERROR unexpected ~ */ ~t|~t] t
// These are invalid and the type-checker will complain.
_[~t] t
_[~t|t] t
_[t|~t] t
_[~t|~t] t
)
type (

View file

@ -208,6 +208,12 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) {
x.typ = ch.elem
check.hasCallOrRecv = true
return
case syntax.Tilde:
// Provide a better error position and message than what check.op below could do.
check.error(e, "cannot use ~ outside of interface or type constraint")
x.mode = invalid
return
}
if !check.op(unaryOpPredicates, x, e.Op) {

View 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
)

View file

@ -22,4 +22,4 @@ type _[P /* ERROR non-function P */ (*int)] int
type _[P *struct /* ERROR "not an expression" */ {}| int /* ERROR "not an expression" */ ] struct{}
// The following fails to parse, due to the '~'
type _[P *struct /* ERROR "not an expression" */ {}|~ /* ERROR "unexpected ~" */ int] struct{}
type _[P *struct /* ERROR "not an expression" */ {}|~int /* ERROR "not an expression" */ ] struct{}

View file

@ -7,7 +7,7 @@
package p
func _(x int) {
_ = ~x // ERROR "unexpected ~"
_ = ~x // unary ~ permitted but the type-checker will complain
}
func _(x int) {