Commit ee4793e
* Move empty-string match optimization to Optimizer so quotations stay clean
The empty-string lowering added in #19189 produced `if x <> null then x.Length = 0
else false` inside `BuildSwitch`, which leaked through pattern-match compilation
into captured quotations (#19873). Move the rewrite to `OptimizeExprOp`, which
already skips `Expr.Quote` bodies. `match s with "" -> _` keeps the same IL and
quotations now see `op_Equality(s, "")`. As a side effect, `if s = "" then _`
gets the same null-safe length-check IL — improvement, not regression.
Also adds a generic `verifyOutputAgainstBaseline` helper in `Compiler.fs` and a
`.bsl`-driven quotation rendering test suite under `Conformance/Expressions/
ExpressionQuotations/QuotationRendering/` to catch future leaks of pattern-match
lowerings into quotation ASTs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Lock in IL trade-offs from rubber-duck review; tighten optimizer guard
Adversarial reviews flagged two IL-quality concerns. Verified both by
inspecting actual ildasm output, accepted both, and added IL tests
locking the current behavior in so future changes are conscious:
1. --optimize- (debug) builds no longer get the null+Length fast path
for `match s with "" -> _` because F#'s `(=)` is only inlined when
LocalOptimizationsEnabled is true. The fallback `String.Equals(s,"")`
call is still correct; JIT tiered compilation reaches the fast path.
2. `match s with null | "" -> _` emits one redundant `brfalse` on the
empty-string branch because the optimizer cannot see the enclosing
null-filtered context that BuildSwitch's `isNullFiltered` flag tracked.
JIT trivially eliminates the redundant branch.
Also tightens `IsILMethodRefSystemStringEquals` to require the call to
be static (rejects hypothetical instance/user-defined `System.String`
type with a 2-arg static `Equals`) and fixes a stale function-name
comment in PatternMatchCompilation.fs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Polish quotation rendering test suite
Driver: extract `printerProgram` helper using a triple-quoted F# string
(no more `\"\"` escape hell); group tests into Regression / NoLeakReference
/ SideEffect / PreExistingError submodules.
Baselines: drop the `Match` suffix (folder is already `QuotationRendering`),
delete redundant `SparseIntMatch` (same code path as Consecutive), shrink
all multi-case tests to the minimum that still demonstrates the point:
- ConsecutiveInts: 1..10 → 1..3 (was 41 lines, now 7)
- Chars, NonEmptyString: 3 → 2 cases
- Int64, Float, Decimal: 3 → 1 case
Result: 9 baselines totalling 37 lines, each fits on screen.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Tighten test comments; collapse no-leak quotation tests to Theory
The empty-string regression tests and surrounding trade-off comments grew
beyond what they need to convey. This trims prose to the causal mechanism
only, flattens four banner-only sub-modules into one, collapses the five
no-leak reference tests into a parametrised Theory, and corrects a stale
docstring claim that Int64/Float/Decimal each take a distinct BuildSwitch
arm (Int64 and Float share the mkILAsmCeq path, so Float.bsl was redundant
with Int64.bsl and is removed). Release note shrinks from a paragraph to
the one user-observable change.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Refactor quotation rendering tests to in-process literals after multi-architect review
Three rounds of adversarial multi-model (gpt-5.5, opus-4.7-xhigh, opus-4.8) architect
review converged on a cleaner design for the empty-string regression coverage:
- Quotation literals now live directly in test method bodies (`<@ … @>`) instead of
inside a generated FSI script. They are desugared at test-project compile time by the
proto fsc, which converts a runtime baseline diff into a compile-time gate: if #19873
ever regresses, the test source itself fails to compile with FS0452.
- Removes the shared-FSI session, the no-op `RunTestCasesInSequence` cargo cult, the
string-templated printer with its triple-quote escape guard, and the unused
`verifyOutputAgainstBaseline` helper this PR briefly introduced.
- Baselines carry a `// <name>` provenance header so a `.bsl` opened in isolation
identifies itself in PR diffs.
- Adds an orphan-guard `[<Fact>]` that fails when `*.bsl` on disk drifts from the
test method set (skipped during `TEST_UPDATE_BSL=1` to avoid racing with writes).
- Adds a structural Expr walker as belt-and-suspenders alongside the baseline for the
two known leaked-lowering shapes (`String.Length`, `String.Equals`).
- Adds a convergence assertion that the `match x with ""` and `if x = ""` quotations
desugar to byte-identical Exprs, using shared module-level let bindings so the
proof and the baselines cannot drift apart.
- Splits the FS0452 array-pattern diagnostic test into its own file since it uses a
different harness (`FSharp |> typecheck |> shouldFail`, not a `.bsl` snapshot).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Use tname_* constants instead of magic strings in IL method-ref matchers
`tname_String`, `tname_Bool`, `tname_Type` already exist in `src/Compiler/AbstractIL/il.fs`
as `[<Literal>]` constants alongside ~20 other type-name literals, but they were not
exposed via `il.fsi` so callers in `Optimize/Optimizer.fs` and `Symbols/Exprs.fs` had
been duplicating the magic strings (`"System.String"`, `"System.Boolean"`, `"System.Type"`).
Exposes the three names actually referenced outside `il.fs` via `il.fsi` and switches
the four affected `IL{TypeRef,MethodRef}.Name` comparisons to use them. No behavior
change. The fix applies to the empty-string-equals matcher this PR added plus the
pre-existing `String.Concat` / `String.GetHashCode` / `Type.GetTypeFromHandle` matchers.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Trim bloated comments in QuotationRenderingTests.fs
5-line walker docstring, 3-line section-divider blocks, 6-line bullet list above
the no-leak reference Facts, and a 12-line "Design notes" header were rationalising
self-evident code or restating test names. Trimmed to the essentials: top-of-file
purpose + bootstrap caveat, one-line note next to the shared `let` bindings, and
the one genuinely-non-obvious comment (Int64 takes the mkILAsmCeq arm).
Net effect: file shrinks from 156 to 100 lines, signal density up, tests unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove orphan-baseline guard Fact
It tested filesystem bookkeeping (does *.bsl on disk equal a hand-maintained
expectedBaselines set), not the compiler. The drift it catches — a deleted
test leaving its .bsl behind — is already visible in `git diff` and adds zero
signal anyone would actually use to find a regression.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Cut surviving garbage after multi-agent adversarial scan
Three garbage hunters (production / tests / prose) found redundancies that
slipped through the earlier iterations:
Production:
- Merge two String.Equals(_, "")/("", _) optimizer arms into one or-pattern.
- Drop the redundant CallingConv.IsStatic guard in IsILMethodRefSystemStringEquals
(ArgCount=2 + both args String + return Bool already pin the static overload).
- Inline single-use let bindings in MakeOptimizedStringEqualsEmptyCall.
- Shrink 4-line docstring to 2 lines; shrink match-arm comment to one line.
- Shrink il.fsi block comment and PatternMatchCompilation.fs trade-off comment.
Tests:
- Delete QuotationUnsupportedConstructsTests.fs (FS0452 array-pattern coverage
already exists in Regressions/E_DecomposeArray01.fs — pure duplication).
- Delete IfEqualEmpty.bsl + its test + the convergence Fact + the shared `let`
bindings + assertNoEmptyStringLowering walker; all duplicated EmptyString.bsl
coverage either as identical AST or as belt-and-suspenders restating the
baseline diff.
- Strip the `// <name>` provenance header from the printer and from all 7
remaining baselines — the test method name already identifies each baseline.
- Trim trade-off comment tails in TypeTestsInPatternMatching.fs (drop the
JIT-folds-the-duplicate / tiered-compilation trivia).
Release notes: drop the implementation-detail bullet about `if s = ""` getting
the same null-safe IL — implied by moving the rewrite to the optimizer.
Net: -115/+30 across 16 files. 14 tests still pass; build clean.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Refactor String IL-method matchers via active pattern + InlineIfLambda
Extracts a `(|StringTy|_|)` partial active pattern over `ILType` and a small
`isILMethodRefOnSystemString` helper that bakes in `tname_String` as the
declaring type and takes the per-arg-shape check as an `[<InlineIfLambda>]`.
Each `IsILMethodRefSystem*` now reads as the literal shape it matches:
`[StringTy; StringTy]` for Equals(string, string); the three overload arities
of String.Concat as alternative list patterns; `[ILType.Array (shape, StringTy)]`
for Concat(string[]). No `ArgCount` book-keeping or `List.forall` walks left.
Behaviour unchanged: build clean, 14 quotation + IL tests still green.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review + fix CI: drop misplaced comment, run quotations via FSI
- PatternMatchCompilation.fs: remove the misplaced `// Empty-string is rewritten…`
comment on the (now-pristine) string arm (review feedback from @abonie).
- QuotationRenderingTests.fs: replace literal `<@…@>` quotations with source-string
evaluation through a shared FSI session. The bootstrap fsc that builds the test
project still has the pre-fix desugar and rejects literal `match s with ""`
quotations with FS0452 — the literals only become valid AFTER this PR is in the
bootstrap. FSI uses the just-built FCS (with the fix), so source-as-string
evaluation is bootstrap-immune. The .bsl baselines are unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Adam Boniecki <20281641+abonie@users.noreply.github.com>
1 parent b8b40f5 commit ee4793e
15 files changed
Lines changed: 261 additions & 27 deletions
File tree
- docs/release-notes/.FSharp.Compiler.Service
- src/Compiler
- AbstractIL
- Checking
- Optimize
- Symbols
- tests/FSharp.Compiler.ComponentTests
- Conformance/Expressions/ExpressionQuotations/QuotationRendering
- EmittedIL
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| 78 | + | |
78 | 79 | | |
79 | 80 | | |
80 | 81 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2471 | 2471 | | |
2472 | 2472 | | |
2473 | 2473 | | |
| 2474 | + | |
| 2475 | + | |
| 2476 | + | |
| 2477 | + | |
| 2478 | + | |
| 2479 | + | |
| 2480 | + | |
| 2481 | + | |
| 2482 | + | |
| 2483 | + | |
| 2484 | + | |
2474 | 2485 | | |
2475 | 2486 | | |
2476 | 2487 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
810 | 810 | | |
811 | 811 | | |
812 | 812 | | |
813 | | - | |
814 | | - | |
815 | | - | |
816 | | - | |
817 | | - | |
818 | | - | |
819 | | - | |
820 | 813 | | |
821 | 814 | | |
822 | 815 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2291 | 2291 | | |
2292 | 2292 | | |
2293 | 2293 | | |
| 2294 | + | |
| 2295 | + | |
| 2296 | + | |
| 2297 | + | |
| 2298 | + | |
| 2299 | + | |
| 2300 | + | |
| 2301 | + | |
| 2302 | + | |
| 2303 | + | |
| 2304 | + | |
| 2305 | + | |
| 2306 | + | |
| 2307 | + | |
| 2308 | + | |
| 2309 | + | |
| 2310 | + | |
| 2311 | + | |
2294 | 2312 | | |
2295 | | - | |
2296 | | - | |
2297 | | - | |
2298 | | - | |
2299 | | - | |
2300 | | - | |
2301 | | - | |
| 2313 | + | |
| 2314 | + | |
| 2315 | + | |
| 2316 | + | |
| 2317 | + | |
2302 | 2318 | | |
2303 | 2319 | | |
2304 | | - | |
2305 | | - | |
2306 | | - | |
2307 | | - | |
2308 | | - | |
2309 | | - | |
2310 | | - | |
2311 | | - | |
2312 | | - | |
2313 | | - | |
2314 | | - | |
| 2320 | + | |
| 2321 | + | |
| 2322 | + | |
2315 | 2323 | | |
2316 | 2324 | | |
2317 | 2325 | | |
| |||
2540 | 2548 | | |
2541 | 2549 | | |
2542 | 2550 | | |
| 2551 | + | |
| 2552 | + | |
| 2553 | + | |
| 2554 | + | |
| 2555 | + | |
| 2556 | + | |
| 2557 | + | |
| 2558 | + | |
2543 | 2559 | | |
2544 | 2560 | | |
2545 | 2561 | | |
| |||
2611 | 2627 | | |
2612 | 2628 | | |
2613 | 2629 | | |
| 2630 | + | |
| 2631 | + | |
| 2632 | + | |
| 2633 | + | |
| 2634 | + | |
| 2635 | + | |
2614 | 2636 | | |
2615 | 2637 | | |
2616 | 2638 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
759 | 759 | | |
760 | 760 | | |
761 | 761 | | |
762 | | - | |
| 762 | + | |
763 | 763 | | |
764 | 764 | | |
765 | 765 | | |
766 | 766 | | |
767 | 767 | | |
768 | 768 | | |
769 | | - | |
| 769 | + | |
770 | 770 | | |
771 | 771 | | |
772 | 772 | | |
| |||
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
Lines changed: 7 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
Lines changed: 6 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
Lines changed: 3 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
Lines changed: 3 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
0 commit comments