Skip to content

Restrict call expression grammar #1134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

absurdhero
Copy link

I made the minimal change to the doc for correctness but I welcome ideas for improvement if anyone feels
the below examples should be called out. I suspect that no additional descriptive text is warranted since
no one has noticed this issue before and commented on it.

This change restricts the CallExpression grammar to match rustc and to resolve a grammatical ambiguity.

CallExpression was described as accepting expressions (including ExpressionWithBlock) as a function operand.
However, rustc parses a block followed by a parenthetical expression as a sequence of Statements, not as a function call.

Furthermore, the Expression ( CallParams? ) grammar leads to an ambiguity as shown:

{expression}(param)

This can be parsed as two statements in sequence or as a function call according to the documented grammar.

Or in this example using if:

if true {String::new} else {String::new}(some_str)

This can be rewritten as an unambiguous CallExpression by wrapping the if block in parenthesis:

(if true {String::new} else {String::new})(some_str)

This change restricts the CallExpression grammar to match rustc and to resolve a grammatical ambiguity.

CallExpression was described as accepting expressions (including ExpressionWithBlock) as a function operand.
However, rustc parses a block followed by a parenthetical expression as a sequence of Statements, not as a function call.

Furthermore, the `Expression ( CallParams? )` grammar leads to an ambiguity as shown:

`{expression}(param)`

This can be parsed as two statements in sequence or as a function call according to the documented grammar.

Or in this example using `if`:

`if true {String::new} else {String::new}(some_str)`

This can be rewritten as an unambiguous CallExpression by wrapping the if block in parenthesis:

`(if true {String::new} else {String::new})(some_str)`
@pushkine
Copy link
Contributor

The documentation is correct although that behavior is a confusing part of the syntax, you can read about it at the third paragraph of the block expression reference

@absurdhero
Copy link
Author

Thanks for the reply, pushkine. I used blocks (the BlockExpression you linked to) as one example of an ExpressionWithBlock but there are others such as the if statement which I also demonstrated.

The third paragraph in the doc you linked is concerned with the semantics of blocks and their return value. But I'm actually talking about an issue with the syntactic description, not the semantics.

I don't think any of the productions for ExpressionWithBlock are valid syntax as part of a function call. BlockExpression.

Can you provide a counter-example where a function call can be expressed as a block followed by parenthesis?

In my testing, the compiler consistently interprets a block (or another kind of ExpressionWithBlock) followed ( CallParams? ) as two statements in sequence.

@pushkine
Copy link
Contributor

Can you provide a counter-example where a function call can be expressed as a block followed by parenthesis?

fn main() {
	println!("{}", if true { || 1 } else { || 2 }())
}

https://replit.com/@pushkine/block-as-callee#src/main.rs

@ehuss
Copy link
Contributor

ehuss commented Jan 31, 2022

Sorry for not responding sooner.

So, the rules here are kind of a mess, and it is not entirely clear if things are working as intended.

Here is an example where a call expression can be a block expressions:

fn foo() {
    println!("hi");
}

fn main() {
    // `x` is a closure that calls `foo`.
    let x = ||{foo}();
    x();
}

I think some of these oddities are related to expr_is_complete.

As a similar thing, #569 discusses an adjacent issue. Part of the problem is the parser is stateful, and the state changes in different contexts.

So, in short, I'm not sure how to resolve this issue, but I don't think a change like this is entirely correct.

@absurdhero
Copy link
Author

Fascinating! Thanks for the examples, both of you.
This does look hairy to document and the reference doesn't seem like the right place for this level of detail.

I was dimly aware of #569 due to its impact on the rustypop grammar. I appreciate the pointer.

I'll close the PR since there isn't anything else to do.

@absurdhero absurdhero closed this Jan 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants