Skip to content

Binder.CheckValue - adjust an assert for DiscardExpression to handle ref assignment.#63534

Merged
AlekseyTs merged 1 commit intodotnet:mainfrom
AlekseyTs:Issue63446
Aug 24, 2022
Merged

Binder.CheckValue - adjust an assert for DiscardExpression to handle ref assignment.#63534
AlekseyTs merged 1 commit intodotnet:mainfrom
AlekseyTs:Issue63446

Conversation

@AlekseyTs
Copy link
Copy Markdown
Contributor

Fixes #63446.

@AlekseyTs
Copy link
Copy Markdown
Contributor Author

@dotnet/roslyn-compiler For the second review.


static void Test()
{
_ = ref F();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a spec for this behavior anywhere that explains why this isn't a compile error?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a spec for this behavior anywhere that explains why this isn't a compile error?

I do not think this needs a specific mention in a spec. A spec for a regular discard support should be sufficient. There is nothing special about this scenario in my opinion - the value being assigned is evaluated and then it is discarded (dropped on the floor).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is nothing special about this scenario in my opinion - the value being assigned is evaluated and then it is discarded (dropped on the floor).

I do not agree. I went and looked at our speclet for this feature, and it does not imply this code should compile.

The right operand must be an expression that yields an lvalue designating a value of the same type as the left operand.

A discard expression is not an expression that yields an lvalue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A discard expression is not an expression that yields an lvalue.

Feel free to adjust the speclet, if you feel it needs that. As you can see this PR doesn't change behavior for the scenario and there was no intention to do so.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, how is the fact that "discard expression is not an expression that yields an lvalue" different for non-ref assignments?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are probably lacking speclet for Discard Expression (or the speclet itself) relaxing the statement for Assignment operator to include discard expression as valid left operand: "The left operand of an assignment shall be an expression classified as a variable, a property access, an indexer access, or an event access."

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, how is the fact that "discard expression is not an expression that yields an lvalue" different for non-ref assignments?

We have no spec for discard assignments at all.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

. I went and looked at our speclet for this feature, and it does not imply this code should compile.

Don't think that applies here. That is a speclet for ref reassignment. Effectively when you're taking an existing ref local and repointing it at a new location. A discard is not an existing value. I know there is no formal spec for them but the LDM notes that do exists for discards seem to strongly indicate that

Discards are like unassigned variables, and do not have a value.
In general, whenever use of standalone _ as a discard is precluded, either by syntactic restrictions or by _ being declared in the scope, var _ is often a fine substitute with the same meaning

I thought a bit about whether allowing _ = ref M() was an intended behavior or not based on the available notes or if the intent was to not allow it and force developers to do _ = M() instead. But it's impossible to decide that from the notes. Eventually I decided to think about whether or not it's a useful behavior. Eventually I concluded that it is a useful behavior. Consider the following

struct ReallyReallyBigStruct { 
 ... 
}

ref ReallyReallyBigStruct M() { ... } 

In the case you want to invoke M to get the side effects it's valuable to use _ = ref M() instead of _ = M() because it avoids the senseless copying of the ReallyReallyBigStruct instance.

We have no spec for discard assignments at all.

This is the root problem IMHO. This feature was done before we got more rigorous with spec work. As such we're left to infer meaning. In this case the syntax's been allowed for 5+ years now, it's a useful distinction so should just go about ensuring it has the intended semantics.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think that applies here. That is a speclet for ref reassignment.

The reason I went to that spec is because that's the one that added = ref expr to the allowable set of things on the right-hand side of an assignment_expression, which is what we're talking about here.

In the case you want to invoke M to get the side effects it's valuable to use _ = ref M() instead of _ = M() because it avoids the senseless copying of the ReallyReallyBigStruct instance.

I suppose this does make sense. Given that, I'll approve, though I think we'll want @MadsTorgersen / @BillWagner to make sure the ECMA committee considers discard assignments when spec'ing out ref-reassignment.


static void Test()
{
_ = ref F();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think that applies here. That is a speclet for ref reassignment.

The reason I went to that spec is because that's the one that added = ref expr to the allowable set of things on the right-hand side of an assignment_expression, which is what we're talking about here.

In the case you want to invoke M to get the side effects it's valuable to use _ = ref M() instead of _ = M() because it avoids the senseless copying of the ReallyReallyBigStruct instance.

I suppose this does make sense. Given that, I'll approve, though I think we'll want @MadsTorgersen / @BillWagner to make sure the ECMA committee considers discard assignments when spec'ing out ref-reassignment.

@AlekseyTs AlekseyTs merged commit ba3d7fd into dotnet:main Aug 24, 2022
@ghost ghost added this to the Next milestone Aug 24, 2022
@dibarbet dibarbet modified the milestones: Next, 17.4 P2 Sep 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Assert failure in Binder.CheckValue() compiling discarded ref assignment

5 participants