Simplify hoisted null-guard fold to reference-type constructor chains#3863
Merged
Conversation
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
dgrunwald
reviewed
Jul 5, 2026
| { | ||
| firstUse = block.Instructions[i].Descendants.OfType<LdLoc>() | ||
| .FirstOrDefault(ld => ld.Variable == paramLoad.Variable); | ||
| // reset the primary positions (see remarks on ILInstruction.Parent) |
Member
There was a problem hiding this comment.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 athis(...)initializer. That cosmetic gain does not justify the extra stobj shape matching, so the guard now stays in the body for structs, andGuardPrecedesChainedConstructorCall(together with its two struct-shape matchers) is inlined away: the gate reduces toILInlining.IsInConstructorInitializer, and the first-use search to a singleILInlining.FindLoadInNextcall on the following statement, which also proves order-safe reachability before anything is mutated. On the bail-out path the operands stolen by the tentativeNullCoalescingInstructionare restored to their primary positions, following the existing pattern inTransformThrowExpressionValueTypes.The struct fixtures now pin the guard-in-body +
this = new TSelf(...)form;NullCheckedNullableStructThisChainstill folds via the independentTransformThrowExpressionValueTypespath. Full decompiler suite: 2308 passed, 0 failed.🤖 Generated with Claude Code