Optimize empty string pattern matching with null-safe length checks#19189
Conversation
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
…ion in BuildSwitch only Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
…ompatibility Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
…, move release notes to 10.0.200 Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
…ontexts Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
Head branch was pushed to by a user without write access
|
/run ilverify |
🔧 CLI Command Report
✅ Patch applied: |
|
Even with Nullable enabled, pattern matching against "" generates a runtime null check: let foo2 (x: string) =
match x with
| "" -> 0
| x -> x.LengthThe generated IL is: The emitted IL checks x != null before comparing the string length. Since x is declared as a non-nullable string, this defensive null check seems unnecessary and adds overhead. Is this intentional? Could the compiler elide the null check when nullable reference types are enabled and the input is known to be non-nullable? |
|
Consider the following code: let foo2 (x: string) =
match x with
| "a" -> 100
| "" -> 0
| x -> x.LengthThe generated logic is roughly equivalent to: if (!string.Equals(x, "a"))
{
if (x != null && x.Length == 0)
return 0;
return x.Length;
}
return 100;I'm not a fan of this transformation. In my opinion, optimizations like this are better left to the JIT/IL optimizer rather than being hardcoded into the pattern-match lowering logic. A small performance gain may be temporary, but ugly generated code lasts forever. 😄 I'm not convinced this optimization is worthwhile. The generated code becomes more complicated and introduces additional null-handling logic, even when nullable reference types are enabled. I'd rather see the compiler emit simpler and more straightforward IL and let the JIT decide whether further optimizations are profitable. Performance concerns may change over time as runtimes evolve, but unnecessary complexity in the generated code tends to stay around for a very long time. |
|
There is no I am afraid the null check has to be there - nullable reference types are on a best-effort basis. You can have calling code with an older compiler, nullness checking turned off, or simple ignoring (e.g. like C#'s We are definitely happy to support a contribution that tries to improve this further, happy to discuss options 👍 . |
String Empty Pattern Match Optimization
This PR optimizes empty string pattern matching to use null-safe
.Length == 0checks instead of string equality comparisons for better performance.Implementation
The optimization is implemented purely as a code generation optimization in
BuildSwitch(PatternMatchCompilation.fs), whereConst (String "")patterns are detected and compiled to emit:brfalse) - skipped if we're in a null-filtered contextcallvirt get_Length()followed by comparison with zero)Key Features
Const (String "")discriminator - no special handlingString.EqualsisNullFilteredparameter tracks when we're in a null-filtered context (after an IsNull test), allowing redundant null checks to be skipped| "" -> ...(optimized with null check + length check)| null -> ... | "" -> ...(both cases handled separately)| null | "" -> ...(bundled clauses - single null check from IsNull, then length check without additional null test)Changes Made
isNullFilteredboolean parameter to track null-safe contextsnullFiltered=trueafter IsNull test and passes to recursive calls.Length == 0check, conditionally skipping null test whenisNullFiltered=truemkGetStringLengthto support the optimizationTesting
--optimize+flag| null | "" -> ...and| "" | null -> ...)Generated IL Example
Before (string equality):
After (null-safe length check):
The changes are minimal and surgical, affecting only the code generation path for empty string constants without any changes to pattern analysis, type definitions, or data structures.
Original prompt
This pull request was created from Copilot chat.
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.