Skip to content

Simplify hoisted null-guard fold to reference-type constructor chains#3863

Merged
siegfriedpammer merged 1 commit into
masterfrom
simplify-ctor-nullguard-gate
Jul 5, 2026
Merged

Simplify hoisted null-guard fold to reference-type constructor chains#3863
siegfriedpammer merged 1 commit into
masterfrom
simplify-ctor-nullguard-gate

Conversation

@siegfriedpammer

Copy link
Copy Markdown
Member

Follow-up to #3852.

A value-type constructor chains via this = new TSelf(...), an ordinary body statement, so a hoisted argument null-guard in front of it is legal C# output as-is; folding it back only bought lifting the chain into a this(...) initializer. That cosmetic gain does not justify the extra stobj shape matching, so the guard now stays in the body for structs, and GuardPrecedesChainedConstructorCall (together with its two struct-shape matchers) is inlined away: the gate reduces to ILInlining.IsInConstructorInitializer, and the first-use search to a single ILInlining.FindLoadInNext call on the following statement, which also proves order-safe reachability before anything is mutated. On the bail-out path the operands stolen by the tentative NullCoalescingInstruction are restored to their primary positions, following the existing pattern in TransformThrowExpressionValueTypes.

The struct fixtures now pin the guard-in-body + this = new TSelf(...) form; NullCheckedNullableStructThisChain still folds via the independent TransformThrowExpressionValueTypes path. Full decompiler suite: 2308 passed, 0 failed.

🤖 Generated with Claude Code

A value-type constructor chains via 'this = new TSelf(...)', an ordinary
body statement, so a hoisted argument null-guard in front of it is legal
C# output as-is; folding it back only bought lifting the chain into a
this(...) initializer. That cosmetic gain does not justify the extra
stobj shape matching, so the guard now stays in the body for structs and
the gate reduces to the ChainedConstructorCallILOffset check.

Assisted-by: Claude:claude-fable-5:Claude Code
@siegfriedpammer siegfriedpammer merged commit 5d49a2f into master Jul 5, 2026
13 checks passed
{
firstUse = block.Instructions[i].Descendants.OfType<LdLoc>()
.FirstOrDefault(ld => ld.Variable == paramLoad.Variable);
// reset the primary positions (see remarks on ILInstruction.Parent)

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.

A simpler alternative might be to use the original IfInstruction as inlinedExpression for the FindLoadInNext call; then construct the new expression only if the transform is possible.

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.

2 participants