Skip to content

Short-circuit a null-conditional operator after a cast - #22174

Merged
grokys merged 1 commit into
AvaloniaUI:mainfrom
Evan260:fix-null-conditional-after-cast
Sep 9, 2026
Merged

grokys merged 1 commit into
AvaloniaUI:mainfrom
Evan260:fix-null-conditional-after-cast

Conversation

@Evan260

@Evan260 Evan260 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

What does the pull request do?

Follow-up to #22082. A null-conditional operator after a cast now short-circuits instead of logging an error.

What is the current behavior?

<Button Content="{Binding $self.((MenuFlyout)Flyout)?.IsOpen}">

logs:

An error occurred binding 'Content' to 'Flyout.IsOpen' at 'Flyout': 'Value is null.'

Without the cast it's fine.

What is the updated/expected behavior with this PR?

No error, same as without the cast.

How was the solution implemented (if it's not obvious)?

#22082 added the short-circuit to the property accessor nodes, but the cast nodes still called ValidateNonNullSource, so the cast reported the error before the ?. could suppress it. There are two of them: ReflectionTypeCastNode for reflection bindings and FuncTransformNode for compiled ones, the latter only ever being used for casts.

Rather than thread an acceptsNull flag through the grammar, casting null now just produces null, like it does in C#. Any error then belongs to the member access that follows, which is where ?. gets a say. I checked that a path without ?. still reports the error.

It has to be SetValue(null) and not ClearValue(). ClearValue gives UnsetValue, which the next node still errors on, so nothing short-circuits.

Two things for you to weigh

  • You solved this in Short-circuit the binding chain on a null-conditional operator #22082 with an explicit flag per node. This does it by making the cast null-transparent instead, which is smaller but a different shape. Tell me if you'd rather it matched.
  • In ReflectionTypeCastNode a null source now takes SetValue(null) while a type mismatch still takes ClearValue(). That difference was already there, but this change puts the two next to each other.

Checklist

One test in NullConditionalBindingTests, covering compiled and reflection bindings. Both fail on main with the error above.

Breaking changes

For a null cast with no ?., the error is now reported at the member access rather than at the cast. Same message, same failure.

Fixed issues

Fixes #22136

Follow-up to AvaloniaUI#22082, which added the short-circuit to the property accessor
nodes. The cast nodes still called ValidateNonNullSource, so a cast reported
"Value is null" before the following ?. could suppress it.

Casting null now produces null, as in C#, leaving any error to the member
access that follows.
@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.2.999-cibuild0069600-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@MrJul MrJul added area-bindings backport-candidate-12.1.x Consider this PR for backporting to 12.1 branch bug labels Sep 8, 2026
@grokys grokys self-assigned this Sep 9, 2026

@grokys grokys left a comment

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.

LGTM!

/// <summary>
/// A top-level type used to test casting in a binding path.
/// </summary>
public class DerivedSecond : NullConditionalBindingTests.Second

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.

Not crazy about having a top-level type derive from a nested type here, but I guess they're only unit tests.

@grokys
grokys added this pull request to the merge queue Sep 9, 2026
Merged via the queue into AvaloniaUI:main with commit fedb14c Sep 9, 2026
11 checks passed
@Evan260
Evan260 deleted the fix-null-conditional-after-cast branch September 9, 2026 17:04
MrJul pushed a commit to MrJul/Avalonia that referenced this pull request Sep 22, 2026
)

Follow-up to AvaloniaUI#22082, which added the short-circuit to the property accessor
nodes. The cast nodes still called ValidateNonNullSource, so a cast reported
"Value is null" before the following ?. could suppress it.

Casting null now produces null, as in C#, leaving any error to the member
access that follows.
@MrJul MrJul added backported-12.1.x and removed backport-candidate-12.1.x Consider this PR for backporting to 12.1 branch labels Sep 22, 2026
grokys pushed a commit to Evan260/Avalonia that referenced this pull request Oct 7, 2026
)

Follow-up to AvaloniaUI#22082, which added the short-circuit to the property accessor
nodes. The cast nodes still called ValidateNonNullSource, so a cast reported
"Value is null" before the following ?. could suppress it.

Casting null now produces null, as in C#, leaving any error to the member
access that follows.
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.

"Value is null" logspam persists when a binding path casts a value

4 participants