diff --git a/docs/release-notes/.FSharp.Core/10.0.300.md b/docs/release-notes/.FSharp.Core/10.0.300.md index 90fd2f1e9ad..6271cd349eb 100644 --- a/docs/release-notes/.FSharp.Core/10.0.300.md +++ b/docs/release-notes/.FSharp.Core/10.0.300.md @@ -6,6 +6,7 @@ * Fix tuple/multi-value projections in queries to use Queryable.Select instead of Enumerable.Select when the source is IQueryable, preserving query composition and enabling async operations like ToListAsync() in Entity Framework Core. ([Issue #3782](https://github.com/dotnet/fsharp/issues/3782), [Issue #15133](https://github.com/dotnet/fsharp/issues/15133)) * Fix EvaluateQuotation to handle Sequential expressions, void method calls (unit return), and other patterns that were previously throwing NotSupportedException. Also properly handles unit-returning expressions by using Action delegates instead of Func delegates. ([Issue #19099](https://github.com/dotnet/fsharp/issues/19099)) * Fix query conditionals without else branch (if-then only) that were causing type mismatch errors. Now properly extracts element type from IQueryable for creating empty sequences. ([Issue #3445](https://github.com/dotnet/fsharp/issues/3445)) +* Fix `Seq.empty` rendering as `"EmptyEnumerable"` in serializers by delegating to `System.Linq.Enumerable.Empty<'T>()` instead of using a custom DU type. ([Issue #17864](https://github.com/dotnet/fsharp/issues/17864), [PR #19317](https://github.com/dotnet/fsharp/pull/19317)) ### Added diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index c05dbdc3701..ed0842488ef 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -608,7 +608,7 @@ module Seq = mkUnfoldSeq generator state [] - let empty<'T> = (EmptyEnumerable :> seq<'T>) + let empty<'T> = System.Linq.Enumerable.Empty<'T>() [] let initInfinite initializer = diff --git a/src/FSharp.Core/seqcore.fs b/src/FSharp.Core/seqcore.fs index 1445f9b16c0..784bdb60ea2 100644 --- a/src/FSharp.Core/seqcore.fs +++ b/src/FSharp.Core/seqcore.fs @@ -313,9 +313,9 @@ module RuntimeHelpers = let rec takeOuter() = if outerEnum.MoveNext() then let ie = outerEnum.Current - // Optimization to detect the statically-allocated empty IEnumerables + // Optimization to detect empty IEnumerables without calling GetEnumerator match box ie with - | :? EmptyEnumerable<'T> -> + | :? ICollection<'T> as c when c.Count = 0 -> // This one is empty, just skip, don't call GetEnumerator, try again takeOuter() | _ -> diff --git a/tests/AheadOfTime/Trimming/check.ps1 b/tests/AheadOfTime/Trimming/check.ps1 index ec64b57863f..2b15c468c2c 100644 --- a/tests/AheadOfTime/Trimming/check.ps1 +++ b/tests/AheadOfTime/Trimming/check.ps1 @@ -63,10 +63,10 @@ function CheckTrim($root, $tfm, $outputfile, $expected_len, $callerLineNumber) { $allErrors = @() # Check net9.0 trimmed assemblies -$allErrors += CheckTrim -root "SelfContained_Trimming_Test" -tfm "net9.0" -outputfile "FSharp.Core.dll" -expected_len 311296 -callerLineNumber 66 +$allErrors += CheckTrim -root "SelfContained_Trimming_Test" -tfm "net9.0" -outputfile "FSharp.Core.dll" -expected_len 310784 -callerLineNumber 66 # Check net9.0 trimmed assemblies with static linked FSharpCore -$allErrors += CheckTrim -root "StaticLinkedFSharpCore_Trimming_Test" -tfm "net9.0" -outputfile "StaticLinkedFSharpCore_Trimming_Test.dll" -expected_len 9169408 -callerLineNumber 69 +$allErrors += CheckTrim -root "StaticLinkedFSharpCore_Trimming_Test" -tfm "net9.0" -outputfile "StaticLinkedFSharpCore_Trimming_Test.dll" -expected_len 9168384 -callerLineNumber 69 # Check net9.0 trimmed assemblies with F# metadata resources removed $allErrors += CheckTrim -root "FSharpMetadataResource_Trimming_Test" -tfm "net9.0" -outputfile "FSharpMetadataResource_Trimming_Test.dll" -expected_len 7609344 -callerLineNumber 72 diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs index 5d37b1ecdf2..ba70556361b 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SeqModule.fs @@ -1523,4 +1523,17 @@ type SeqModule() = let choice = seq |> Seq.randomSampleBy (fun () -> 1.0) 0 - Assert.AreEqual(0, choice |> Seq.length) \ No newline at end of file + Assert.AreEqual(0, choice |> Seq.length) + + [] + member _.``Seq.empty is not a discriminated union type``() = + let empty = Seq.empty + let ty = empty.GetType() + let isUnion = Microsoft.FSharp.Reflection.FSharpType.IsUnion(ty) + Assert.False(isUnion, "Seq.empty should not be a discriminated union type") + + [] + member _.``Seq.concat with empty elements works``() = + let result = Seq.concat [ Seq.empty; seq { 1; 2 }; Seq.empty; seq { 3 }; Seq.empty ] + let expected = [| 1; 2; 3 |] + Assert.AreEqual(expected, result |> Seq.toArray) \ No newline at end of file