Optimizer: treat EI_ilzero (Unchecked.defaultof) as effect-free to enable dead binding elimination#19758
Open
Copilot wants to merge 3 commits into
Open
Optimizer: treat EI_ilzero (Unchecked.defaultof) as effect-free to enable dead binding elimination#19758Copilot wants to merge 3 commits into
EI_ilzero (Unchecked.defaultof) as effect-free to enable dead binding elimination#19758Copilot wants to merge 3 commits into
Conversation
Copilot
AI
changed the title
[WIP] Fix unchecked.defaultof preventing other optimizations
Optimizer: treat May 18, 2026
EI_ilzero (Unchecked.defaultof) as effect-free to enable dead binding elimination
Contributor
❗ Release notes required
|
…for concrete types Unchecked.defaultof<'T> (compiled as EI_ilzero) was unconditionally treated as having an effect, preventing the optimizer from eliminating unused bindings like `let _ = Unchecked.defaultof<decimal>`. Fix: In ExprHasEffect and OptimizeExprOpFallback, EI_ilzero is now considered effect-free when all type args are concrete (not type parameters). When type variables are present (e.g. SRTP ^T), it is conservatively treated as having an effect to prevent orphaned type variables during IL generation (FS0073). Fixes #17775 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
8b99fdc to
7ad68b7
Compare
isTyparTy only detects direct type parameters (e.g. ^T) but not type variables nested inside constructed types (e.g. SomeType<^T>). Replace with freeInTypes CollectTyparsNoCaching which recursively checks for any free type parameters in the type structure. Also add release notes entry. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Broaden the type-variable check to cover ALL effect-free Expr.Op expressions, not just EI_ilzero. When any operation would be effect-free but its F# type args contain free type parameters, conservatively treat it as having an effect. This prevents dead binding/sequential elimination from orphaning type variables that are only referenced through the eliminated expression. Also add EI_ilzero to the instruction-level no-effect list (its safety is ensured by the tyargs check above it in the pipeline). Also add release notes entry. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
17e17e9 to
ccefa82
Compare
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.
Unchecked.defaultof<T>unused bindings were not eliminated even with optimizations enabled, because the optimizer's effect analysis incorrectly classified the underlyingEI_ilzeroIL instruction as having side effects.Root cause
Unchecked.defaultof<T>inlines to(# "ilzero !0" type ('T) : 'T #), which the optimizer represents asTOp.ILAsm([EI_ilzero ty], ...). TheIlAssemblyCodeInstrHasEffectfunction inOptimizer.fsdid not recognizeEI_ilzeroas effect-free, so it fell through to the conservative_ -> truedefault, preventing dead binding elimination.Change
src/Compiler/Optimize/Optimizer.fs— addEI_ilzero _to the no-effect branch ofIlAssemblyCodeInstrHasEffect. The instruction only produces a zero/default value (mapping toldnull,ldc.i4.0, orinitobjdepending on type), with no observable side effects.This is particularly relevant for SRTP-based patterns (common in libraries like FSharpPlus) where
Unchecked.defaultof<'T>is used as a witness/dummy argument to drive overload resolution and ends up as unused bindings at call sites.