Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 23, 2025

Debug builds fail with "should not be set twice" assertion when compiling LINQ queries containing group by into with property accesses on anonymous types.

Root Cause

In Binder_Query.cs, when processing a group by clause with the optimized form (when the group expression is the range variable identifier), the code was calling MakeQueryInvocation twice with the same receiver. This caused the receiver's property accesses to be checked twice, triggering the "should not be set twice" assertion.

Changes

  • Binder.ValueChecks.cs: Modified CheckValue to create a new BoundPropertyAccess node under DEBUG instead of mutating the original node's WasPropertyBackingFieldAccessChecked flag. To ensure a new node is always created (since Update() returns the same instance if nothing changed), the implementation toggles InitialBindingReceiverIsSubjectToCloning and then restores it, forcing two Update calls. This allows the same node to be passed to CheckValue multiple times safely.

  • Binder_Query.cs: Removed the conditional logic around CheckValue call in MakeQueryInvocation. Now always calls CheckValue unconditionally since it's safe to call multiple times on the same node.

  • Binder_Expressions.cs: Updated cref documentation to match the current signature of MakeQueryInvocation.

  • QueryTests.cs: Added test case for the specific scenario.

Example

using System.Linq;

var x = new { A = new[] { new { B = 1 } } };

// Previously failed assertion during compilation
var y = from a in x.A
        group a by a.B into g
        select 1;

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI changed the title [WIP] Fix failing assert in WasPropertyBackingFieldAccessChecked Fix WasPropertyBackingFieldAccessChecked assertion with query continuations Dec 23, 2025
Copilot AI requested a review from 333fred December 23, 2025 20:39
@333fred
Copy link
Member

333fred commented Dec 23, 2025

@copilot The change in Binder.ValueChecks is incorrect. The assert should remain as is, it is what is showing us the problem exists. You need to find and fix the actual cause.

Copy link
Contributor Author

Copilot AI commented Dec 23, 2025

@copilot The change in Binder.ValueChecks is incorrect. The assert should remain as is, it is what is showing us the problem exists. You need to find and fix the actual cause.

You're right - I've reverted the Binder.ValueChecks.cs change and fixed the actual root cause. In Binder_Query.cs line 279, the optimized GroupBy invocation was passing receiverIsCheckedForRValue: false even though the same receiver was already checked at line 263 in the unoptimized form. Fixed in commit 8f78c88 by passing true to indicate the receiver has already been checked.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • kijvsblobprodcus387.vsblob.vsassets.io
    • Triggering command: /usr/share/dotnet/dotnet dotnet build ./src/Compilers/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.csproj (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@333fred 333fred marked this pull request as ready for review December 26, 2025 19:57
@333fred 333fred requested a review from a team as a code owner December 26, 2025 19:57
@333fred
Copy link
Member

333fred commented Dec 26, 2025

@dotnet/roslyn-compiler for review

// The optimized form. We store the unoptimized form for analysis
unoptimizedForm = result;
result = MakeQueryInvocation(state.selectOrGroup, e, receiverIsCheckedForRValue: false, "GroupBy", lambdaLeft, diagnostics
result = MakeQueryInvocation(state.selectOrGroup, e, receiverIsCheckedForRValue: true, "GroupBy", lambdaLeft, diagnostics
Copy link
Member

Choose a reason for hiding this comment

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

This receiver (e) is checked on line 263. I've re-audited the rest of the call sites of MakeQueryInvocation and didn't see any others that need to be updated.

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 27, 2025

Done with review pass (commit 6) #Closed

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 29, 2025

    /// <see cref="MakeQueryInvocation(CSharpSyntaxNode, BoundExpression, bool, string, SeparatedSyntaxList{TypeSyntax}, ImmutableArray{TypeWithAnnotations}, ImmutableArray{BoundExpression}, BindingDiagnosticBag)"/>.

Should this cref be adjusted as well? #Closed


Refers to: src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs:7767 in ea83009. [](commit_id = ea83009, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 29, 2025

Done with review pass (commit 7) #Closed

…Invocation always calls CheckValue

Per @AlekseyTs's suggestion:
- Modified CheckValue to create a new node under DEBUG instead of mutating the original
- Removed conditional logic in MakeQueryInvocation to always call CheckValue unconditionally
- This allows the same node to be passed to CheckValue multiple times safely

Co-authored-by: 333fred <[email protected]>
Copy link
Contributor Author

Copilot AI commented Dec 29, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • kijvsblobprodcus387.vsblob.vsassets.io
    • Triggering command: /usr/share/dotnet/dotnet dotnet build ./src/Compilers/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.csproj (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from 333fred December 29, 2025 23:49
@333fred
Copy link
Member

333fred commented Dec 30, 2025

@AlekseyTs ready for another review

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 30, 2025

@333fred It looks like there are still errors in XML doc comments #Closed

get { return this.PropertySymbol; }
}

public BoundPropertyAccess Clone()
Copy link
Contributor

Choose a reason for hiding this comment

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

public BoundPropertyAccess Clone()

I forgot when do we prefer to add APIs in this file vs. into a partial declaration dedicated for the specific node type.

Copy link
Member

Choose a reason for hiding this comment

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

I viewed this as effectively an Update method, which is why I put it in this file. If you prefer, I can move it out.

Copy link
Contributor

Choose a reason for hiding this comment

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

I viewed this as effectively an Update method, which is why I put it in this file.

The reason for this conclusion isn't that obvious to me. For example, why Constructors.cs isn't the right place? I just want to make sure that the right rational is applied consistently.

Copy link
Member

Choose a reason for hiding this comment

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

I can see the argument for viewing this as a constructor, but perhaps for the most clarity I'll move it to its own file.

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 13)

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM (commit 14). Still having questions about the new API placement

@333fred
Copy link
Member

333fred commented Jan 6, 2026

@dotnet/roslyn-compiler for a second review.

@jjonescz
Copy link
Member

jjonescz commented Jan 8, 2026

Should there be a link to the issue being fixed in the PR description?

@333fred 333fred merged commit 844d5f3 into main Jan 9, 2026
25 checks passed
@333fred 333fred deleted the copilot/fix-assert-in-property-check branch January 9, 2026 23:59
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Jan 9, 2026
@333fred 333fred linked an issue Jan 10, 2026 that may be closed by this pull request
@davidwengier davidwengier modified the milestones: Next, 18.4 Jan 27, 2026
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.

Failing assert in WasPropertyBackingFieldAccessChecked

5 participants