From c76c603e681bf4bbc2d4272d2b13129c48d01c8a Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Thu, 27 Jun 2024 13:25:11 -0400 Subject: [PATCH 01/26] C# collection expression support for lists & sets --- VisualFSharp.sln | 15 +++ src/FSharp.Core/prim-types.fs | 41 ++++++ src/FSharp.Core/prim-types.fsi | 64 ++++++++- src/FSharp.Core/set.fs | 19 +++ src/FSharp.Core/set.fsi | 12 ++ .../FSharp.Core.UnitTests.CSharp.csproj | 23 ++++ .../Interop/CollectionExpressionTests.cs | 125 ++++++++++++++++++ 7 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj create mode 100644 tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs diff --git a/VisualFSharp.sln b/VisualFSharp.sln index 4f1ab0fa06f..900a8285dcb 100644 --- a/VisualFSharp.sln +++ b/VisualFSharp.sln @@ -193,6 +193,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "MicroPerf", "tests\benchmar EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroPerfCSharp", "tests\benchmarks\CompiledCodeBenchmarks\MicroPerf\CS\MicroPerfCSharp.csproj", "{9F9DD315-37DA-4413-928E-1CFC6924B64F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharp.Core.UnitTests.CSharp", "tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.csproj", "{BC444300-EE7A-4AEB-96AA-760DCC5BFD45}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1019,6 +1021,18 @@ Global {9F9DD315-37DA-4413-928E-1CFC6924B64F}.Release|Any CPU.Build.0 = Release|Any CPU {9F9DD315-37DA-4413-928E-1CFC6924B64F}.Release|x86.ActiveCfg = Release|Any CPU {9F9DD315-37DA-4413-928E-1CFC6924B64F}.Release|x86.Build.0 = Release|Any CPU + {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Debug|x86.ActiveCfg = Debug|Any CPU + {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Debug|x86.Build.0 = Debug|Any CPU + {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Proto|Any CPU.Build.0 = Debug|Any CPU + {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Proto|x86.ActiveCfg = Debug|Any CPU + {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Proto|x86.Build.0 = Debug|Any CPU + {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Release|Any CPU.Build.0 = Release|Any CPU + {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Release|x86.ActiveCfg = Release|Any CPU + {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1099,6 +1113,7 @@ Global {6734FC6F-B5F3-45E1-9A72-720378BB49C9} = {DFB6ADD7-3149-43D9-AFA0-FC4A818B472B} {601CD5C1-EAFA-4AE3-8FB9-F667B5728213} = {DFB6ADD7-3149-43D9-AFA0-FC4A818B472B} {9F9DD315-37DA-4413-928E-1CFC6924B64F} = {DFB6ADD7-3149-43D9-AFA0-FC4A818B472B} + {BC444300-EE7A-4AEB-96AA-760DCC5BFD45} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {48EDBBBE-C8EE-4E3C-8B19-97184A487B37} diff --git a/src/FSharp.Core/prim-types.fs b/src/FSharp.Core/prim-types.fs index 556fb1bcde5..d42778ce094 100644 --- a/src/FSharp.Core/prim-types.fs +++ b/src/FSharp.Core/prim-types.fs @@ -566,6 +566,9 @@ namespace Microsoft.FSharp.Core open BasicInlinedOperations + // This exists solely so that it can be used in the CollectionBuilderAttribute on List<'T> in prim-types.fsi. + module TypeOfUtils = + let inline typeof<'T> = typeof<'T> module TupleUtils = @@ -4069,6 +4072,28 @@ namespace Microsoft.FSharp.Core and 'T voption = ValueOption<'T> +#if !NET8_0_OR_GREATER +namespace System.Runtime.CompilerServices + open System + open System.ComponentModel + open Microsoft.FSharp.Core + + [] + [] + [] + type CollectionBuilderAttribute (builderType: Type, methodName: string) = + inherit Attribute () + member _.BuilderType = builderType + member _.MethodName = methodName + + [] + [] + [] + [] + type ScopedRefAttribute () = + inherit Attribute () +#endif + namespace Microsoft.FSharp.Collections //------------------------------------------------------------------------- @@ -4086,6 +4111,9 @@ namespace Microsoft.FSharp.Collections open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions open Microsoft.FSharp.Core.BasicInlinedOperations +#if NETSTANDARD2_1_OR_GREATER + [, "Create")>] +#endif [] [>)>] [] @@ -4111,6 +4139,19 @@ namespace Microsoft.FSharp.Collections and 'T list = List<'T> +#if NETSTANDARD2_1_OR_GREATER + and [] List = + [] + static member Create([] items: System.ReadOnlySpan<'T>) = + let mutable list : 'T list = [] + for i = items.Length - 1 downto 0 do + list <- items[i] :: list + list +#endif + //------------------------------------------------------------------------- // List (debug view) //------------------------------------------------------------------------- diff --git a/src/FSharp.Core/prim-types.fsi b/src/FSharp.Core/prim-types.fsi index 2b3bcf1ac7c..7856bd105ef 100644 --- a/src/FSharp.Core/prim-types.fsi +++ b/src/FSharp.Core/prim-types.fsi @@ -1238,6 +1238,10 @@ namespace Microsoft.FSharp.Core /// ByRef and Pointer Types type outref<'T> = byref<'T, ByRefKinds.Out> + // This exists solely so that it can be used in the CollectionBuilderAttribute on List<'T> below. + module internal TypeOfUtils = + val inline typeof<'T>: Type + /// Language primitives associated with the F# language /// /// Language Primitives @@ -2578,12 +2582,52 @@ namespace Microsoft.FSharp.Core /// Represents an Error or a Failure. The code failed with a value of 'TError representing what went wrong. | Error of ErrorValue:'TError +#if !NET8_0_OR_GREATER +namespace System.Runtime.CompilerServices + open System + open System.ComponentModel + open Microsoft.FSharp.Core + + [] + [] + [] + type CollectionBuilderAttribute = + inherit Attribute + + /// Initialize the attribute to refer to the method on the type. + /// The type of the builder to use to construct the collection. + /// The name of the method on the builder to use to construct the collection. + /// + /// must refer to a static method that accepts a single parameter of + /// type and returns an instance of the collection being built containing + /// a copy of the data from that span. In future releases of .NET, additional patterns may be supported. + /// + new: builderType: Type * methodName: string -> CollectionBuilderAttribute + + /// Gets the type of the builder to use to construct the collection. + member BuilderType: Type + + /// Gets the name of the method on the builder to use to construct the collection. + /// This should match the metadata name of the target method. For example, this might be ".ctor" if targeting the type's constructor. + member MethodName: string + + [] + [] + [] + [] + type ScopedRefAttribute = + inherit Attribute + + new: unit -> ScopedRefAttribute +#endif + namespace Microsoft.FSharp.Collections open System open System.Collections open System.Collections.Generic open Microsoft.FSharp.Core + open Microsoft.FSharp.Core.TypeOfUtils /// The type of immutable singly-linked lists. /// @@ -2593,6 +2637,9 @@ namespace Microsoft.FSharp.Collections /// /// /// +#if NETSTANDARD2_1_OR_GREATER + [, "Create")>] +#endif [] [] [] @@ -2646,7 +2693,7 @@ namespace Microsoft.FSharp.Collections /// /// The list with head appended to the front of tail. static member Cons: head: 'T * tail: 'T list -> 'T list - + interface IEnumerable<'T> interface IEnumerable interface IReadOnlyCollection<'T> @@ -2664,6 +2711,21 @@ namespace Microsoft.FSharp.Collections /// and 'T list = List<'T> +#if NETSTANDARD2_1_OR_GREATER + /// Contains methods for compiler use related to lists. + and [] List = + /// Creates a list with the specified items. + /// + /// The items to store in the list. + /// + /// A list containing the specified items. + [] + static member Create: [] items: System.ReadOnlySpan<'T> -> 'T list +#endif + /// An abbreviation for the CLI type type ResizeArray<'T> = System.Collections.Generic.List<'T> diff --git a/src/FSharp.Core/set.fs b/src/FSharp.Core/set.fs index b47bef257c1..8cee5e69f1e 100644 --- a/src/FSharp.Core/set.fs +++ b/src/FSharp.Core/set.fs @@ -697,6 +697,9 @@ module internal SetTree = let ofArray comparer l = Array.fold (fun acc k -> add comparer k acc) empty l +#if NETSTANDARD2_1_OR_GREATER +[, "Create")>] +#endif [] [] [>)>] @@ -1023,6 +1026,22 @@ type Set<[] 'T when 'T: comparison>(comparer: IComparer<' .Append("; ... ]") .ToString() +#if NETSTANDARD2_1_OR_GREATER +and [] Set = + [] + static member Create([] items: System.ReadOnlySpan<'T>) = + let comparer = LanguagePrimitives.FastGenericComparer<'T> + let mutable acc = SetTree.empty + + for item in items do + acc <- SetTree.add LanguagePrimitives.FastGenericComparer<'T> item acc + + Set(comparer, acc) +#endif + and [] SetDebugView<'T when 'T: comparison>(v: Set<'T>) = [] diff --git a/src/FSharp.Core/set.fsi b/src/FSharp.Core/set.fsi index 58615cedaa9..794fcf4b689 100644 --- a/src/FSharp.Core/set.fsi +++ b/src/FSharp.Core/set.fsi @@ -13,6 +13,9 @@ open Microsoft.FSharp.Collections /// See the module for further operations on sets. /// /// All members of this class are thread-safe and may be used concurrently from multiple threads. +#if NETSTANDARD2_1_OR_GREATER +[, "Create")>] +#endif [] [] type Set<[] 'T when 'T: comparison> = @@ -233,6 +236,15 @@ type Set<[] 'T when 'T: comparison> = interface IReadOnlyCollection<'T> override Equals: obj -> bool +#if NETSTANDARD2_1_OR_GREATER +and [] Set = + [] + static member Create: [] items: System.ReadOnlySpan<'T> -> Set<'T> +#endif + namespace Microsoft.FSharp.Collections open System diff --git a/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj b/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj new file mode 100644 index 00000000000..ed8ac2a3952 --- /dev/null +++ b/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj @@ -0,0 +1,23 @@ + + + + $(FSharpNetCoreProductTargetFramework);net472 + $(FSharpNetCoreProductTargetFramework) + enable + enable + 12.0 + xunit + false + true + + + + + + + + + + + + diff --git a/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs b/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs new file mode 100644 index 00000000000..e8738d972f5 --- /dev/null +++ b/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs @@ -0,0 +1,125 @@ +namespace FSharp.Core.UnitTests.CSharp.Interop; + +using Microsoft.FSharp.Collections; + +// The CollectionBuilderAttribute type is only available in .NET 8 and up. +#if NET8_0_OR_GREATER +public class CollectionExpressionTests +{ + private sealed record RecordClass(int X) : IComparable + { + public int CompareTo(object? obj) => obj switch + { + null => 1, + RecordClass { X: var otherX } => X.CompareTo(otherX), + _ => throw new ArgumentException("Invalid comparison.", nameof(obj)) + }; + } + + private readonly record struct RecordStruct(int X) : IComparable + { + public int CompareTo(object? obj) => obj switch + { + null => 1, + RecordStruct { X: var otherX } => X.CompareTo(otherX), + _ => throw new ArgumentException("Invalid comparison.", nameof(obj)) + }; + } + + [Fact] + public void FSharpList_Int_CanCreateUsingCollectionExpression() + { + var expected = ListModule.OfArray([1, 2, 3]); + FSharpList actual = [1, 2, 3]; + + Assert.Equal(expected, actual); + + Assert.Collection( + actual, + item1 => Assert.Equal(1, item1), + item2 => Assert.Equal(2, item2), + item3 => Assert.Equal(3, item3)); + + Assert.True(actual is [1, 2, 3]); + } + + [Fact] + public void FSharpList_RecordClass_CanCreateUsingCollectionExpression() + { + var expected = ListModule.OfArray([new RecordClass(1), new RecordClass(2), new RecordClass(3)]); + FSharpList actual = [new RecordClass(1), new RecordClass(2), new RecordClass(3)]; + + Assert.Equal(expected, actual); + + Assert.Collection( + actual, + item1 => Assert.Equal(new RecordClass(1), item1), + item2 => Assert.Equal(new RecordClass(2), item2), + item3 => Assert.Equal(new RecordClass(3), item3)); + + Assert.True(actual is [RecordClass(1), RecordClass(2), RecordClass(3)]); + } + + [Fact] + public void FSharpList_RecordStruct_CanCreateUsingCollectionExpression() + { + var expected = ListModule.OfArray([new RecordStruct(1), new RecordStruct(2), new RecordStruct(3)]); + FSharpList actual = [new RecordStruct(1), new RecordStruct(2), new RecordStruct(3)]; + + Assert.Equal(expected, actual); + + Assert.Collection( + actual, + item1 => Assert.Equal(new RecordStruct(1), item1), + item2 => Assert.Equal(new RecordStruct(2), item2), + item3 => Assert.Equal(new RecordStruct(3), item3)); + + Assert.True(actual is [RecordStruct(1), RecordStruct(2), RecordStruct(3)]); + } + + [Fact] + public void FSharpSet_CanCreateUsingCollectionExpression() + { + var expected = SetModule.OfArray([1, 2, 3]); + FSharpSet actual = [1, 2, 3]; + + Assert.Equal(expected, actual); + + Assert.Collection( + actual, + item1 => Assert.Equal(1, item1), + item2 => Assert.Equal(2, item2), + item3 => Assert.Equal(3, item3)); + } + + [Fact] + public void FSharpSet_RecordClass_CanCreateUsingCollectionExpression() + { + var expected = SetModule.OfArray([new RecordClass(1), new RecordClass(2), new RecordClass(3)]); + FSharpSet actual = [new RecordClass(1), new RecordClass(2), new RecordClass(3)]; + + Assert.Equal(expected, actual); + + Assert.Collection( + actual, + item1 => Assert.Equal(new RecordClass(1), item1), + item2 => Assert.Equal(new RecordClass(2), item2), + item3 => Assert.Equal(new RecordClass(3), item3)); + } + + [Fact] + public void FSharpSet_RecordStruct_CanCreateUsingCollectionExpression() + { + var expected = SetModule.OfArray([new RecordStruct(1), new RecordStruct(2), new RecordStruct(3)]); + FSharpSet actual = [new RecordStruct(1), new RecordStruct(2), new RecordStruct(3)]; + + Assert.Equal(expected, actual); + + Assert.Collection( + actual, + item1 => Assert.Equal(new RecordStruct(1), item1), + item2 => Assert.Equal(new RecordStruct(2), item2), + item3 => Assert.Equal(new RecordStruct(3), item3)); + } +} +#endif \ No newline at end of file From b2ef7a91d837e2fe0609e5054ee55da89a5c0951 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Thu, 27 Jun 2024 13:28:49 -0400 Subject: [PATCH 02/26] Add trailing newline --- .../Interop/CollectionExpressionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs b/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs index e8738d972f5..568eb6a648a 100644 --- a/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs +++ b/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs @@ -122,4 +122,4 @@ public void FSharpSet_RecordStruct_CanCreateUsingCollectionExpression() item3 => Assert.Equal(new RecordStruct(3), item3)); } } -#endif \ No newline at end of file +#endif From b4facf95323b723e174369e4f05b6e955bbee7cc Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Thu, 27 Jun 2024 14:35:46 -0400 Subject: [PATCH 03/26] These should be `internal` --- src/FSharp.Core/prim-types.fsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FSharp.Core/prim-types.fsi b/src/FSharp.Core/prim-types.fsi index 7856bd105ef..e8dc24abe97 100644 --- a/src/FSharp.Core/prim-types.fsi +++ b/src/FSharp.Core/prim-types.fsi @@ -2591,7 +2591,7 @@ namespace System.Runtime.CompilerServices [] [] [] - type CollectionBuilderAttribute = + type internal CollectionBuilderAttribute = inherit Attribute /// Initialize the attribute to refer to the method on the type. @@ -2615,7 +2615,7 @@ namespace System.Runtime.CompilerServices [] [] [] - type ScopedRefAttribute = + type internal ScopedRefAttribute = inherit Attribute new: unit -> ScopedRefAttribute From 69410bc5e264b43c386d70c9cd0fad5786d9294f Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Thu, 27 Jun 2024 14:36:16 -0400 Subject: [PATCH 04/26] Cleanup --- src/FSharp.Core/prim-types.fs | 8 +++----- src/FSharp.Core/prim-types.fsi | 2 -- .../Interop/CollectionExpressionTests.cs | 8 ++++---- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/FSharp.Core/prim-types.fs b/src/FSharp.Core/prim-types.fs index d42778ce094..6e0de6a886e 100644 --- a/src/FSharp.Core/prim-types.fs +++ b/src/FSharp.Core/prim-types.fs @@ -567,7 +567,7 @@ namespace Microsoft.FSharp.Core open BasicInlinedOperations // This exists solely so that it can be used in the CollectionBuilderAttribute on List<'T> in prim-types.fsi. - module TypeOfUtils = + module internal TypeOfUtils = let inline typeof<'T> = typeof<'T> module TupleUtils = @@ -4078,19 +4078,17 @@ namespace System.Runtime.CompilerServices open System.ComponentModel open Microsoft.FSharp.Core - [] [] [] - type CollectionBuilderAttribute (builderType: Type, methodName: string) = + type internal CollectionBuilderAttribute (builderType: Type, methodName: string) = inherit Attribute () member _.BuilderType = builderType member _.MethodName = methodName - [] [] [] [] - type ScopedRefAttribute () = + type internal ScopedRefAttribute () = inherit Attribute () #endif diff --git a/src/FSharp.Core/prim-types.fsi b/src/FSharp.Core/prim-types.fsi index e8dc24abe97..51ffa6e37d0 100644 --- a/src/FSharp.Core/prim-types.fsi +++ b/src/FSharp.Core/prim-types.fsi @@ -2588,7 +2588,6 @@ namespace System.Runtime.CompilerServices open System.ComponentModel open Microsoft.FSharp.Core - [] [] [] type internal CollectionBuilderAttribute = @@ -2611,7 +2610,6 @@ namespace System.Runtime.CompilerServices /// This should match the metadata name of the target method. For example, this might be ".ctor" if targeting the type's constructor. member MethodName: string - [] [] [] [] diff --git a/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs b/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs index 568eb6a648a..e8256ef0ed2 100644 --- a/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs +++ b/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs @@ -1,7 +1,7 @@ -namespace FSharp.Core.UnitTests.CSharp.Interop; - using Microsoft.FSharp.Collections; +namespace FSharp.Core.UnitTests.CSharp.Interop; + // The CollectionBuilderAttribute type is only available in .NET 8 and up. #if NET8_0_OR_GREATER public class CollectionExpressionTests @@ -11,7 +11,7 @@ private sealed record RecordClass(int X) : IComparable public int CompareTo(object? obj) => obj switch { null => 1, - RecordClass { X: var otherX } => X.CompareTo(otherX), + RecordClass(var otherX) => X.CompareTo(otherX), _ => throw new ArgumentException("Invalid comparison.", nameof(obj)) }; } @@ -21,7 +21,7 @@ private readonly record struct RecordStruct(int X) : IComparable public int CompareTo(object? obj) => obj switch { null => 1, - RecordStruct { X: var otherX } => X.CompareTo(otherX), + RecordStruct(var otherX) => X.CompareTo(otherX), _ => throw new ArgumentException("Invalid comparison.", nameof(obj)) }; } From 170013083873674b2d8b97baf1cf1ef2eee9204e Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Thu, 27 Jun 2024 14:39:04 -0400 Subject: [PATCH 05/26] Update FSharp.Core surface area --- .../FSharp.Core.SurfaceArea.netstandard21.release.bsl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl index 92d85ccd98d..ea111e99abf 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl @@ -217,6 +217,7 @@ Microsoft.FSharp.Collections.ComparisonIdentity: System.Collections.Generic.ICom Microsoft.FSharp.Collections.ComparisonIdentity: System.Collections.Generic.IComparer`1[T] NonStructural$W[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean]], Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean]]) Microsoft.FSharp.Collections.ComparisonIdentity: System.Collections.Generic.IComparer`1[T] NonStructural[T]() Microsoft.FSharp.Collections.ComparisonIdentity: System.Collections.Generic.IComparer`1[T] Structural[T]() +Microsoft.FSharp.Collections.FSharpList: Microsoft.FSharp.Collections.FSharpList`1[T] Create[T](System.ReadOnlySpan`1[T]) Microsoft.FSharp.Collections.FSharpList`1+Tags[T]: Int32 Cons Microsoft.FSharp.Collections.FSharpList`1+Tags[T]: Int32 Empty Microsoft.FSharp.Collections.FSharpList`1[T]: Boolean Equals(Microsoft.FSharp.Collections.FSharpList`1[T]) @@ -274,6 +275,7 @@ Microsoft.FSharp.Collections.FSharpMap`2[TKey,TValue]: System.String ToString() Microsoft.FSharp.Collections.FSharpMap`2[TKey,TValue]: TValue Item [TKey] Microsoft.FSharp.Collections.FSharpMap`2[TKey,TValue]: TValue get_Item(TKey) Microsoft.FSharp.Collections.FSharpMap`2[TKey,TValue]: Void .ctor(System.Collections.Generic.IEnumerable`1[System.Tuple`2[TKey,TValue]]) +Microsoft.FSharp.Collections.FSharpSet: Microsoft.FSharp.Collections.FSharpSet`1[T] Create[T](System.ReadOnlySpan`1[T]) Microsoft.FSharp.Collections.FSharpSet`1[T]: Boolean Contains(T) Microsoft.FSharp.Collections.FSharpSet`1[T]: Boolean Equals(System.Object) Microsoft.FSharp.Collections.FSharpSet`1[T]: Boolean IsEmpty From 5699cb90f5269fbf593a950d633866c36d783ef0 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Thu, 27 Jun 2024 14:41:37 -0400 Subject: [PATCH 06/26] Update release notes --- docs/release-notes/.FSharp.Core/8.0.400.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Core/8.0.400.md b/docs/release-notes/.FSharp.Core/8.0.400.md index 3f9a780974b..a9c307d5ae9 100644 --- a/docs/release-notes/.FSharp.Core/8.0.400.md +++ b/docs/release-notes/.FSharp.Core/8.0.400.md @@ -3,6 +3,7 @@ ### Added * `Random functions for collections` ([RFC #1135](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1135-random-functions-for-collections.md), [PR #17277](https://github.com/dotnet/fsharp/pull/17277)) +* Enable C# collection expression support for F# lists & sets. ([Language suggestion #1355](https://github.com/fsharp/fslang-suggestions/issues/1355), [RFC TODO](TODO), [PR #17359](https://github.com/dotnet/fsharp/pull/17359)) ### Changed From 21b61a1f83bacc8448dc41a7bb53f73456578b0c Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Thu, 27 Jun 2024 14:46:47 -0400 Subject: [PATCH 07/26] Meant to use that --- src/FSharp.Core/set.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharp.Core/set.fs b/src/FSharp.Core/set.fs index 8cee5e69f1e..4efc3fa92df 100644 --- a/src/FSharp.Core/set.fs +++ b/src/FSharp.Core/set.fs @@ -1037,7 +1037,7 @@ and [ item acc + acc <- SetTree.add comparer item acc Set(comparer, acc) #endif From 29ab91e22337799bbf502e990a1d9332bb72753f Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Thu, 27 Jun 2024 14:48:33 -0400 Subject: [PATCH 08/26] Remove redundant attribute * The `EditorBrowsable` attribute on the `ScopedRefAttribute` definition only exists on the BCL implementation because the `scope` keyword is meant to be used in C# instead of this attribute. Since F# doesn't have the `scoped` keyword, this reasoning doesn't apply. --- src/FSharp.Core/prim-types.fs | 2 -- src/FSharp.Core/prim-types.fsi | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/FSharp.Core/prim-types.fs b/src/FSharp.Core/prim-types.fs index 6e0de6a886e..a97a3953cf9 100644 --- a/src/FSharp.Core/prim-types.fs +++ b/src/FSharp.Core/prim-types.fs @@ -4075,7 +4075,6 @@ namespace Microsoft.FSharp.Core #if !NET8_0_OR_GREATER namespace System.Runtime.CompilerServices open System - open System.ComponentModel open Microsoft.FSharp.Core [] @@ -4085,7 +4084,6 @@ namespace System.Runtime.CompilerServices member _.BuilderType = builderType member _.MethodName = methodName - [] [] [] type internal ScopedRefAttribute () = diff --git a/src/FSharp.Core/prim-types.fsi b/src/FSharp.Core/prim-types.fsi index 51ffa6e37d0..33505b6ab4b 100644 --- a/src/FSharp.Core/prim-types.fsi +++ b/src/FSharp.Core/prim-types.fsi @@ -2585,7 +2585,6 @@ namespace Microsoft.FSharp.Core #if !NET8_0_OR_GREATER namespace System.Runtime.CompilerServices open System - open System.ComponentModel open Microsoft.FSharp.Core [] @@ -2610,7 +2609,6 @@ namespace System.Runtime.CompilerServices /// This should match the metadata name of the target method. For example, this might be ".ctor" if targeting the type's constructor. member MethodName: string - [] [] [] type internal ScopedRefAttribute = From d84ab1cf205fb854fd980d66d5c3619c6499ee20 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Thu, 27 Jun 2024 14:56:30 -0400 Subject: [PATCH 09/26] Add doc comments --- src/FSharp.Core/set.fsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/FSharp.Core/set.fsi b/src/FSharp.Core/set.fsi index 794fcf4b689..b9ba1042945 100644 --- a/src/FSharp.Core/set.fsi +++ b/src/FSharp.Core/set.fsi @@ -237,10 +237,16 @@ type Set<[] 'T when 'T: comparison> = override Equals: obj -> bool #if NETSTANDARD2_1_OR_GREATER +/// Contains methods for compiler use related to sets. and [] Set = + /// Creates a set with the specified items. + /// + /// The items to store in the set. + /// + /// A set containing the specified items. [] static member Create: [] items: System.ReadOnlySpan<'T> -> Set<'T> #endif From 4c3ab922eb6f4d17c89311e07b228e5d1396a3f0 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Thu, 27 Jun 2024 18:56:35 -0400 Subject: [PATCH 10/26] Missed that test name --- .../Interop/CollectionExpressionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs b/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs index e8256ef0ed2..06f8e074e8e 100644 --- a/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs +++ b/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs @@ -78,7 +78,7 @@ public void FSharpList_RecordStruct_CanCreateUsingCollectionExpression() } [Fact] - public void FSharpSet_CanCreateUsingCollectionExpression() + public void FSharpSet_Int_CanCreateUsingCollectionExpression() { var expected = SetModule.OfArray([1, 2, 3]); FSharpSet actual = [1, 2, 3]; From 7d114f8ed3451393207a7dca57f0a129031fdc65 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Fri, 28 Jun 2024 08:23:02 -0400 Subject: [PATCH 11/26] Add runner JSON --- .../FSharp.Core.UnitTests.CSharp.csproj | 1 + tests/FSharp.Core.UnitTests.CSharp/xunit.runner.json | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 tests/FSharp.Core.UnitTests.CSharp/xunit.runner.json diff --git a/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj b/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj index ed8ac2a3952..98f005704be 100644 --- a/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj +++ b/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj @@ -17,6 +17,7 @@ + diff --git a/tests/FSharp.Core.UnitTests.CSharp/xunit.runner.json b/tests/FSharp.Core.UnitTests.CSharp/xunit.runner.json new file mode 100644 index 00000000000..743febb7028 --- /dev/null +++ b/tests/FSharp.Core.UnitTests.CSharp/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "appDomain": "ifAvailable", + "shadowCopy": false +} From 94a8541fee190538252c07dcd343b090689f94f9 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Fri, 28 Jun 2024 08:30:45 -0400 Subject: [PATCH 12/26] Replace ifdef with comment --- src/FSharp.Core/prim-types.fs | 3 +-- src/FSharp.Core/prim-types.fsi | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/FSharp.Core/prim-types.fs b/src/FSharp.Core/prim-types.fs index a97a3953cf9..2f04704a11f 100644 --- a/src/FSharp.Core/prim-types.fs +++ b/src/FSharp.Core/prim-types.fs @@ -4072,7 +4072,7 @@ namespace Microsoft.FSharp.Core and 'T voption = ValueOption<'T> -#if !NET8_0_OR_GREATER +// These attributes only exist in .NET 8 and up. namespace System.Runtime.CompilerServices open System open Microsoft.FSharp.Core @@ -4088,7 +4088,6 @@ namespace System.Runtime.CompilerServices [] type internal ScopedRefAttribute () = inherit Attribute () -#endif namespace Microsoft.FSharp.Collections diff --git a/src/FSharp.Core/prim-types.fsi b/src/FSharp.Core/prim-types.fsi index 33505b6ab4b..a1207e8cd6d 100644 --- a/src/FSharp.Core/prim-types.fsi +++ b/src/FSharp.Core/prim-types.fsi @@ -2582,7 +2582,7 @@ namespace Microsoft.FSharp.Core /// Represents an Error or a Failure. The code failed with a value of 'TError representing what went wrong. | Error of ErrorValue:'TError -#if !NET8_0_OR_GREATER +// These attributes only exist in .NET 8 and up. namespace System.Runtime.CompilerServices open System open Microsoft.FSharp.Core @@ -2613,9 +2613,7 @@ namespace System.Runtime.CompilerServices [] type internal ScopedRefAttribute = inherit Attribute - new: unit -> ScopedRefAttribute -#endif namespace Microsoft.FSharp.Collections From d93e696dfdf20ee26242e92a449a4c5e690c84e3 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Fri, 28 Jun 2024 09:03:05 -0400 Subject: [PATCH 13/26] Add direct tests for non-generic List & Set types --- .../Microsoft.FSharp.Collections/ListType.fs | 13 +++++++++++++ .../Microsoft.FSharp.Collections/SetType.fs | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListType.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListType.fs index 653f9b3a40b..f0eb9ff83ec 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListType.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/ListType.fs @@ -347,3 +347,16 @@ type ListType() = Assert.AreEqual(lst.[-3..(-4)], ([]: int list)) Assert.AreEqual(lst.[-4..(-3)], ([]: int list)) +#if NET8_0_OR_GREATER + +#nowarn "1204" // FS1204: This type/method is for compiler use and should not be used directly. + +/// Tests for methods on the static, non-generic List type. +module FSharpList = + [] + let ``List.Create creates a list from a ReadOnlySpan`` () = + let expected = [1..10] + let span = ReadOnlySpan [|1..10|] + let actual = List.Create span + Assert.Equal(expected, actual) +#endif diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SetType.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SetType.fs index ac3c213be80..f4141416d10 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SetType.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/SetType.fs @@ -331,3 +331,17 @@ type SetType() = Assert.AreEqual(sec.MaximumElement, 7) Assert.AreEqual(Set.maxElement fir, 6) Assert.AreEqual(Set.maxElement sec, 7) + +#if NET8_0_OR_GREATER + +#nowarn "1204" // FS1204: This type/method is for compiler use and should not be used directly. + +/// Tests for methods on the static, non-generic Set type. +module FSharpSet = + [] + let ``Set.Create creates a set from a ReadOnlySpan`` () = + let expected = set [1..10] + let span = ReadOnlySpan [|1..10|] + let actual = Set.Create span + Assert.Equal>(expected, actual) +#endif From eb0e1a23f6ab8af4eeb6396f5c9a454a1b820d40 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Fri, 28 Jun 2024 14:52:10 -0400 Subject: [PATCH 14/26] Link RFC PR --- docs/release-notes/.FSharp.Core/8.0.400.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release-notes/.FSharp.Core/8.0.400.md b/docs/release-notes/.FSharp.Core/8.0.400.md index a9c307d5ae9..ba5d2414dd1 100644 --- a/docs/release-notes/.FSharp.Core/8.0.400.md +++ b/docs/release-notes/.FSharp.Core/8.0.400.md @@ -3,7 +3,7 @@ ### Added * `Random functions for collections` ([RFC #1135](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1135-random-functions-for-collections.md), [PR #17277](https://github.com/dotnet/fsharp/pull/17277)) -* Enable C# collection expression support for F# lists & sets. ([Language suggestion #1355](https://github.com/fsharp/fslang-suggestions/issues/1355), [RFC TODO](TODO), [PR #17359](https://github.com/dotnet/fsharp/pull/17359)) +* Enable C# collection expression support for F# lists & sets. ([Language suggestion #1355](https://github.com/fsharp/fslang-suggestions/issues/1355), [RFC FS-1145 (PR#776)](https://github.com/fsharp/fslang-design/pull/776), [PR #17359](https://github.com/dotnet/fsharp/pull/17359)) ### Changed From fca5827fa7dfec6855aef57c3c394ac0901f779c Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Fri, 28 Jun 2024 15:44:23 -0400 Subject: [PATCH 15/26] Make test project work --- TESTGUIDE.md | 2 ++ buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt | 3 ++- eng/Build.ps1 | 4 ++++ eng/build.sh | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/TESTGUIDE.md b/TESTGUIDE.md index cebd809a915..745d8abcaf6 100644 --- a/TESTGUIDE.md +++ b/TESTGUIDE.md @@ -104,6 +104,8 @@ The F# tests are split as follows: * [FSharp.Core.UnitTests](tests/FSharp.Core.UnitTests) - Validation of the core F# types and the public surface area of `FSharp.Core.dll`. +* [FSharp.Core.UnitTests.CSharp](tests/FSharp.Core.UnitTests.CSharp) - Validation of interop of F# types from `FSharp.Core.dll` in C#. + * [FSharp.Compiler.Service.Tests](tests/FSharp.Compiler.Service.Tests) - Validation of compiler internals. * [FSharp.Compiler.ComponentTests](tests/FSharp.Compiler.ComponentTests) - Validation of compiler APIs. diff --git a/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt b/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt index 2961d6963f2..f504ef070e0 100644 --- a/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt +++ b/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt @@ -6,4 +6,5 @@ FSharp.Test.Utilities.dll FSharp.Compiler.Private.Scripting.UnitTests.dll FSharp.Compiler.Service.Tests.dll FSharp.Core.UnitTests.dll -FSharpSuite.Tests.dll \ No newline at end of file +FSharp.Core.UnitTests.CSharp.dll +FSharpSuite.Tests.dll diff --git a/eng/Build.ps1 b/eng/Build.ps1 index 4f6d3b767b5..6cd78200a6a 100644 --- a/eng/Build.ps1 +++ b/eng/Build.ps1 @@ -588,6 +588,7 @@ try { TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Compiler.Private.Scripting.UnitTests\FSharp.Compiler.Private.Scripting.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.Private.Scripting.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Build.UnitTests\FSharp.Build.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Build.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" + TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" # Collect output from background jobs Wait-job $bgJob | out-null @@ -602,6 +603,7 @@ try { TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Compiler.Private.Scripting.UnitTests\FSharp.Compiler.Private.Scripting.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.Private.Scripting.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Build.UnitTests\FSharp.Build.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Build.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" + TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" # Collect output from background jobs Wait-job $bgJob | out-null @@ -638,6 +640,8 @@ try { if ($testFSharpCore) { TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" + TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" + TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" } if ($testCompiler) { diff --git a/eng/build.sh b/eng/build.sh index ce85f543ef9..bb2ce8474cd 100755 --- a/eng/build.sh +++ b/eng/build.sh @@ -319,6 +319,7 @@ if [[ "$test_core_clr" == true ]]; then Test --testproject "$repo_root/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharp.Compiler.Private.Scripting.UnitTests.fsproj" --targetframework $coreclrtestframework Test --testproject "$repo_root/tests/FSharp.Build.UnitTests/FSharp.Build.UnitTests.fsproj" --targetframework $coreclrtestframework Test --testproject "$repo_root/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj" --targetframework $coreclrtestframework + Test --testproject "$repo_root/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.fsproj" --targetframework $coreclrtestframework fi if [[ "$test_compilercomponent_tests" == true ]]; then From 6368265a1f9d134675267965b1019a2a6353f740 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Fri, 28 Jun 2024 17:05:47 -0400 Subject: [PATCH 16/26] Copy/paste bug --- eng/Build.ps1 | 8 ++++---- eng/build.sh | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Build.ps1 b/eng/Build.ps1 index 2e25c6ed1e1..68e4064940b 100644 --- a/eng/Build.ps1 +++ b/eng/Build.ps1 @@ -588,7 +588,7 @@ try { TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Compiler.Private.Scripting.UnitTests\FSharp.Compiler.Private.Scripting.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.Private.Scripting.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Build.UnitTests\FSharp.Build.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Build.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" + TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.csproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" # Collect output from background jobs Wait-job $bgJob | out-null @@ -603,7 +603,7 @@ try { TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Compiler.Private.Scripting.UnitTests\FSharp.Compiler.Private.Scripting.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.Private.Scripting.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Build.UnitTests\FSharp.Build.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Build.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" + TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.csproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" # Collect output from background jobs Wait-job $bgJob | out-null @@ -640,8 +640,8 @@ try { if ($testFSharpCore) { TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" + TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.csproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" + TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.csproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" } if ($testCompiler) { diff --git a/eng/build.sh b/eng/build.sh index 863335cb968..98463110dff 100755 --- a/eng/build.sh +++ b/eng/build.sh @@ -324,7 +324,7 @@ if [[ "$test_core_clr" == true ]]; then Test --testproject "$repo_root/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharp.Compiler.Private.Scripting.UnitTests.fsproj" --targetframework $coreclrtestframework Test --testproject "$repo_root/tests/FSharp.Build.UnitTests/FSharp.Build.UnitTests.fsproj" --targetframework $coreclrtestframework Test --testproject "$repo_root/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj" --targetframework $coreclrtestframework - Test --testproject "$repo_root/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.fsproj" --targetframework $coreclrtestframework + Test --testproject "$repo_root/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj" --targetframework $coreclrtestframework fi if [[ "$test_compilercomponent_tests" == true ]]; then From 44e0dcda5930e5216e45ee2791f1b86ebdbfd744 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Sat, 29 Jun 2024 14:38:03 -0400 Subject: [PATCH 17/26] Probably also not --- .../FSharp.Core.UnitTests.CSharp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj b/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj index 98f005704be..5435b284ec6 100644 --- a/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj +++ b/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj @@ -7,7 +7,7 @@ enable 12.0 xunit - false + true true From e4a1f08f53951d79f6c50e86e5a1a7d4fc89dad5 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Sat, 29 Jun 2024 15:07:41 -0400 Subject: [PATCH 18/26] Not that either --- .../FSharp.Core.UnitTests.CSharp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj b/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj index 5435b284ec6..98f005704be 100644 --- a/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj +++ b/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj @@ -7,7 +7,7 @@ enable 12.0 xunit - true + false true From 976f140c4b5707d7bde5e3d0db61a6b16730ac77 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 8 Jul 2024 12:52:22 -0400 Subject: [PATCH 19/26] Add C# 12 language version case --- tests/FSharp.Test.Utilities/Compiler.fs | 7 +------ tests/FSharp.Test.Utilities/CompilerAssert.fs | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 1cbf3a3aade..a9275d15f99 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -777,12 +777,7 @@ module rec Compiler = let references = TargetFrameworkUtil.getReferences csSource.TargetFramework - let lv = - match csSource.LangVersion with - | CSharpLanguageVersion.CSharp8 -> LanguageVersion.CSharp8 - | CSharpLanguageVersion.CSharp9 -> LanguageVersion.CSharp9 - | CSharpLanguageVersion.Preview -> LanguageVersion.Preview - | _ -> LanguageVersion.Default + let lv = CSharpLanguageVersion.toLanguageVersion csSource.LangVersion let outputKind, extension = match csSource.OutputType with diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index a97b214acde..681f812a0ed 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -172,20 +172,25 @@ type CSharpLanguageVersion = | CSharp8 = 0 | CSharp9 = 1 | CSharp11 = 11 + | CSharp12 = 12 | Preview = 99 +module CSharpLanguageVersion = + /// Converts the given C# language version to a Roslyn language version value. + let toLanguageVersion lv = + match lv with + | CSharpLanguageVersion.CSharp8 -> LanguageVersion.CSharp8 + | CSharpLanguageVersion.CSharp9 -> LanguageVersion.CSharp9 + | CSharpLanguageVersion.CSharp11 -> LanguageVersion.CSharp11 + | CSharpLanguageVersion.CSharp12 -> LanguageVersion.CSharp12 + | CSharpLanguageVersion.Preview -> LanguageVersion.Preview + | _ -> LanguageVersion.Default + [] type CompilationUtil private () = static let createCSharpCompilation (source: SourceCodeFileKind, lv, tf, additionalReferences, name) = - let lv = - match lv with - | CSharpLanguageVersion.CSharp8 -> LanguageVersion.CSharp8 - | CSharpLanguageVersion.CSharp9 -> LanguageVersion.CSharp9 - | CSharpLanguageVersion.CSharp11 -> LanguageVersion.CSharp11 - | CSharpLanguageVersion.Preview -> LanguageVersion.Preview - | _ -> LanguageVersion.Default - + let lv = CSharpLanguageVersion.toLanguageVersion lv let tf = defaultArg tf TargetFramework.NetStandard20 let source = match source.GetSourceText with From 2d0f88f8cfb438f51006f4d222e95d8684f24126 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 8 Jul 2024 12:52:46 -0400 Subject: [PATCH 20/26] Add interop tests --- .../FSharp.Core.UnitTests.fsproj | 1 + .../Interop/CSharpCollectionExpressions.fs | 211 ++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 tests/FSharp.Core.UnitTests/Interop/CSharpCollectionExpressions.fs diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj b/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj index 9acbcef398e..93143ca4103 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj @@ -90,6 +90,7 @@ + diff --git a/tests/FSharp.Core.UnitTests/Interop/CSharpCollectionExpressions.fs b/tests/FSharp.Core.UnitTests/Interop/CSharpCollectionExpressions.fs new file mode 100644 index 00000000000..926f1057d06 --- /dev/null +++ b/tests/FSharp.Core.UnitTests/Interop/CSharpCollectionExpressions.fs @@ -0,0 +1,211 @@ +module FSharp.Core.UnitTests.Interop.CSharp.CollectionExpressions + +open FSharp.Test +open FSharp.Test.Compiler +open Xunit + +/// Usings for the C# tests. +/// These must be prepended to each test's C# source text. +let csUsings = + """ + using System; + using System.Linq; + using Microsoft.FSharp.Collections; + + #nullable enable + """ + +/// Utility types and functions for the C# tests. +/// These must be appended to each test's C# source text. +let csUtils = + """ + public sealed record RecordClass(int X) : IComparable + { + public int CompareTo(object? obj) => obj switch + { + null => 1, + RecordClass(var otherX) => X.CompareTo(otherX), + _ => throw new ArgumentException("Invalid comparison.", nameof(obj)) + }; + } + + public readonly record struct RecordStruct(int X) : IComparable + { + public int CompareTo(object? obj) => obj switch + { + null => 1, + RecordStruct(var otherX) => X.CompareTo(otherX), + _ => throw new ArgumentException("Invalid comparison.", nameof(obj)) + }; + } + + public sealed class EqualException(string message) : Exception(message) { } + + public sealed class TrueException(string message) : Exception(message) { } + + public static class Assert + { + public static void Equal(T expected, T actual) + { + switch ((expected, actual)) + { + case (null, null): return; + case (null, not null): + case (not null, null): + case var _ when !expected.Equals(actual): throw new EqualException($"Expected '{expected}' but got '{actual}'."); + } + } + + public static void True(bool b) + { + if (!b) + { + throw new TrueException("Expected true but got false."); + } + } + } + """ + +[] +let ``FSharpList: can create using C# collection expression`` () = + CSharp $$""" + {{csUsings}} + + var expected = ListModule.OfArray([1, 2, 3]); + FSharpList actual = [1, 2, 3]; + + Assert.Equal(expected, actual); + Assert.Equal(expected.Length, actual.Length); + + for (var i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i], actual[i]); + } + + Assert.True(actual is [1, 2, 3]); + + {{csUtils}} + """ + |> withCSharpLanguageVersion CSharpLanguageVersion.CSharp12 + |> withName "Test" + |> compileExeAndRun + |> shouldSucceed + +[] +let ``FSharpList: can create using C# collection expression`` () = + CSharp $$""" + {{csUsings}} + + var expected = ListModule.OfArray([new RecordClass(1), new RecordClass(2), new RecordClass(3)]); + FSharpList actual = [new RecordClass(1), new RecordClass(2), new RecordClass(3)]; + + Assert.Equal(expected, actual); + Assert.Equal(expected.Length, actual.Length); + + for (var i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i], actual[i]); + } + + Assert.True(actual is [RecordClass(1), RecordClass(2), RecordClass(3)]); + + {{csUtils}} + """ + |> withCSharpLanguageVersion CSharpLanguageVersion.CSharp12 + |> withName "Test" + |> compileExeAndRun + |> shouldSucceed + +[] +let ``FSharpList: can create using C# collection expression`` () = + CSharp $$""" + {{csUsings}} + + var expected = ListModule.OfArray([new RecordStruct(1), new RecordStruct(2), new RecordStruct(3)]); + FSharpList actual = [new RecordStruct(1), new RecordStruct(2), new RecordStruct(3)]; + + Assert.Equal(expected, actual); + Assert.Equal(expected.Length, actual.Length); + + for (var i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i], actual[i]); + } + + Assert.True(actual is [RecordStruct(1), RecordStruct(2), RecordStruct(3)]); + + {{csUtils}} + """ + |> withCSharpLanguageVersion CSharpLanguageVersion.CSharp12 + |> withName "Test" + |> compileExeAndRun + |> shouldSucceed + +[] +let ``FSharpSet: can create using C# collection expression`` () = + CSharp $$""" + {{csUsings}} + + var expected = SetModule.OfArray([1, 2, 3]); + FSharpSet actual = [1, 2, 3]; + + Assert.Equal(expected, actual); + Assert.Equal(expected.Count, actual.Count); + + foreach (var (e, a) in expected.Zip(actual)) + { + Assert.Equal(e, a); + } + + {{csUtils}} + """ + |> withCSharpLanguageVersion CSharpLanguageVersion.CSharp12 + |> withName "Test" + |> compileExeAndRun + |> shouldSucceed + +[] +let ``FSharpSet: can create using C# collection expression`` () = + CSharp $$""" + {{csUsings}} + + var expected = SetModule.OfArray([new RecordClass(1), new RecordClass(2), new RecordClass(3)]); + FSharpSet actual = [new RecordClass(1), new RecordClass(2), new RecordClass(3)]; + + Assert.Equal(expected, actual); + Assert.Equal(expected.Count, actual.Count); + + foreach (var (e, a) in expected.Zip(actual)) + { + Assert.Equal(e, a); + } + + {{csUtils}} + """ + |> withCSharpLanguageVersion CSharpLanguageVersion.CSharp12 + |> withName "Test" + |> compileExeAndRun + |> shouldSucceed + +[] +let ``FSharpSet: can create using C# collection expression`` () = + CSharp $$""" + {{csUsings}} + + var expected = SetModule.OfArray([new RecordStruct(1), new RecordStruct(2), new RecordStruct(3)]); + FSharpSet actual = [new RecordStruct(1), new RecordStruct(2), new RecordStruct(3)]; + + Assert.Equal(expected, actual); + Assert.Equal(expected.Count, actual.Count); + + foreach (var (e, a) in expected.Zip(actual)) + { + Assert.Equal(e, a); + } + + {{csUtils}} + """ + |> withCSharpLanguageVersion CSharpLanguageVersion.CSharp12 + |> withName "Test" + |> compileExeAndRun + |> shouldSucceed From e5e67c85aa575de5dbd53c042c6a084d0cefd8c0 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 8 Jul 2024 12:54:33 -0400 Subject: [PATCH 21/26] Remove separate C# test project --- VisualFSharp.sln | 15 --- .../FSharp.Core.UnitTests.CSharp.csproj | 24 ---- .../Interop/CollectionExpressionTests.cs | 125 ------------------ .../xunit.runner.json | 5 - 4 files changed, 169 deletions(-) delete mode 100644 tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj delete mode 100644 tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs delete mode 100644 tests/FSharp.Core.UnitTests.CSharp/xunit.runner.json diff --git a/VisualFSharp.sln b/VisualFSharp.sln index 900a8285dcb..4f1ab0fa06f 100644 --- a/VisualFSharp.sln +++ b/VisualFSharp.sln @@ -193,8 +193,6 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "MicroPerf", "tests\benchmar EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroPerfCSharp", "tests\benchmarks\CompiledCodeBenchmarks\MicroPerf\CS\MicroPerfCSharp.csproj", "{9F9DD315-37DA-4413-928E-1CFC6924B64F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharp.Core.UnitTests.CSharp", "tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.csproj", "{BC444300-EE7A-4AEB-96AA-760DCC5BFD45}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1021,18 +1019,6 @@ Global {9F9DD315-37DA-4413-928E-1CFC6924B64F}.Release|Any CPU.Build.0 = Release|Any CPU {9F9DD315-37DA-4413-928E-1CFC6924B64F}.Release|x86.ActiveCfg = Release|Any CPU {9F9DD315-37DA-4413-928E-1CFC6924B64F}.Release|x86.Build.0 = Release|Any CPU - {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Debug|x86.ActiveCfg = Debug|Any CPU - {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Debug|x86.Build.0 = Debug|Any CPU - {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Proto|Any CPU.ActiveCfg = Debug|Any CPU - {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Proto|Any CPU.Build.0 = Debug|Any CPU - {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Proto|x86.ActiveCfg = Debug|Any CPU - {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Proto|x86.Build.0 = Debug|Any CPU - {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Release|Any CPU.Build.0 = Release|Any CPU - {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Release|x86.ActiveCfg = Release|Any CPU - {BC444300-EE7A-4AEB-96AA-760DCC5BFD45}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1113,7 +1099,6 @@ Global {6734FC6F-B5F3-45E1-9A72-720378BB49C9} = {DFB6ADD7-3149-43D9-AFA0-FC4A818B472B} {601CD5C1-EAFA-4AE3-8FB9-F667B5728213} = {DFB6ADD7-3149-43D9-AFA0-FC4A818B472B} {9F9DD315-37DA-4413-928E-1CFC6924B64F} = {DFB6ADD7-3149-43D9-AFA0-FC4A818B472B} - {BC444300-EE7A-4AEB-96AA-760DCC5BFD45} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {48EDBBBE-C8EE-4E3C-8B19-97184A487B37} diff --git a/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj b/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj deleted file mode 100644 index 98f005704be..00000000000 --- a/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - $(FSharpNetCoreProductTargetFramework);net472 - $(FSharpNetCoreProductTargetFramework) - enable - enable - 12.0 - xunit - false - true - - - - - - - - - - - - - diff --git a/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs b/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs deleted file mode 100644 index 06f8e074e8e..00000000000 --- a/tests/FSharp.Core.UnitTests.CSharp/Interop/CollectionExpressionTests.cs +++ /dev/null @@ -1,125 +0,0 @@ -using Microsoft.FSharp.Collections; - -namespace FSharp.Core.UnitTests.CSharp.Interop; - -// The CollectionBuilderAttribute type is only available in .NET 8 and up. -#if NET8_0_OR_GREATER -public class CollectionExpressionTests -{ - private sealed record RecordClass(int X) : IComparable - { - public int CompareTo(object? obj) => obj switch - { - null => 1, - RecordClass(var otherX) => X.CompareTo(otherX), - _ => throw new ArgumentException("Invalid comparison.", nameof(obj)) - }; - } - - private readonly record struct RecordStruct(int X) : IComparable - { - public int CompareTo(object? obj) => obj switch - { - null => 1, - RecordStruct(var otherX) => X.CompareTo(otherX), - _ => throw new ArgumentException("Invalid comparison.", nameof(obj)) - }; - } - - [Fact] - public void FSharpList_Int_CanCreateUsingCollectionExpression() - { - var expected = ListModule.OfArray([1, 2, 3]); - FSharpList actual = [1, 2, 3]; - - Assert.Equal(expected, actual); - - Assert.Collection( - actual, - item1 => Assert.Equal(1, item1), - item2 => Assert.Equal(2, item2), - item3 => Assert.Equal(3, item3)); - - Assert.True(actual is [1, 2, 3]); - } - - [Fact] - public void FSharpList_RecordClass_CanCreateUsingCollectionExpression() - { - var expected = ListModule.OfArray([new RecordClass(1), new RecordClass(2), new RecordClass(3)]); - FSharpList actual = [new RecordClass(1), new RecordClass(2), new RecordClass(3)]; - - Assert.Equal(expected, actual); - - Assert.Collection( - actual, - item1 => Assert.Equal(new RecordClass(1), item1), - item2 => Assert.Equal(new RecordClass(2), item2), - item3 => Assert.Equal(new RecordClass(3), item3)); - - Assert.True(actual is [RecordClass(1), RecordClass(2), RecordClass(3)]); - } - - [Fact] - public void FSharpList_RecordStruct_CanCreateUsingCollectionExpression() - { - var expected = ListModule.OfArray([new RecordStruct(1), new RecordStruct(2), new RecordStruct(3)]); - FSharpList actual = [new RecordStruct(1), new RecordStruct(2), new RecordStruct(3)]; - - Assert.Equal(expected, actual); - - Assert.Collection( - actual, - item1 => Assert.Equal(new RecordStruct(1), item1), - item2 => Assert.Equal(new RecordStruct(2), item2), - item3 => Assert.Equal(new RecordStruct(3), item3)); - - Assert.True(actual is [RecordStruct(1), RecordStruct(2), RecordStruct(3)]); - } - - [Fact] - public void FSharpSet_Int_CanCreateUsingCollectionExpression() - { - var expected = SetModule.OfArray([1, 2, 3]); - FSharpSet actual = [1, 2, 3]; - - Assert.Equal(expected, actual); - - Assert.Collection( - actual, - item1 => Assert.Equal(1, item1), - item2 => Assert.Equal(2, item2), - item3 => Assert.Equal(3, item3)); - } - - [Fact] - public void FSharpSet_RecordClass_CanCreateUsingCollectionExpression() - { - var expected = SetModule.OfArray([new RecordClass(1), new RecordClass(2), new RecordClass(3)]); - FSharpSet actual = [new RecordClass(1), new RecordClass(2), new RecordClass(3)]; - - Assert.Equal(expected, actual); - - Assert.Collection( - actual, - item1 => Assert.Equal(new RecordClass(1), item1), - item2 => Assert.Equal(new RecordClass(2), item2), - item3 => Assert.Equal(new RecordClass(3), item3)); - } - - [Fact] - public void FSharpSet_RecordStruct_CanCreateUsingCollectionExpression() - { - var expected = SetModule.OfArray([new RecordStruct(1), new RecordStruct(2), new RecordStruct(3)]); - FSharpSet actual = [new RecordStruct(1), new RecordStruct(2), new RecordStruct(3)]; - - Assert.Equal(expected, actual); - - Assert.Collection( - actual, - item1 => Assert.Equal(new RecordStruct(1), item1), - item2 => Assert.Equal(new RecordStruct(2), item2), - item3 => Assert.Equal(new RecordStruct(3), item3)); - } -} -#endif diff --git a/tests/FSharp.Core.UnitTests.CSharp/xunit.runner.json b/tests/FSharp.Core.UnitTests.CSharp/xunit.runner.json deleted file mode 100644 index 743febb7028..00000000000 --- a/tests/FSharp.Core.UnitTests.CSharp/xunit.runner.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", - "appDomain": "ifAvailable", - "shadowCopy": false -} From edc1db762b5639350a9d4a5e32698fd3908bf895 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 8 Jul 2024 12:57:29 -0400 Subject: [PATCH 22/26] Remove refs to removed test project --- TESTGUIDE.md | 2 -- buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt | 1 - eng/Build.ps1 | 4 ---- eng/build.sh | 1 - 4 files changed, 8 deletions(-) diff --git a/TESTGUIDE.md b/TESTGUIDE.md index 745d8abcaf6..cebd809a915 100644 --- a/TESTGUIDE.md +++ b/TESTGUIDE.md @@ -104,8 +104,6 @@ The F# tests are split as follows: * [FSharp.Core.UnitTests](tests/FSharp.Core.UnitTests) - Validation of the core F# types and the public surface area of `FSharp.Core.dll`. -* [FSharp.Core.UnitTests.CSharp](tests/FSharp.Core.UnitTests.CSharp) - Validation of interop of F# types from `FSharp.Core.dll` in C#. - * [FSharp.Compiler.Service.Tests](tests/FSharp.Compiler.Service.Tests) - Validation of compiler internals. * [FSharp.Compiler.ComponentTests](tests/FSharp.Compiler.ComponentTests) - Validation of compiler APIs. diff --git a/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt b/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt index f504ef070e0..7af2a7f58cb 100644 --- a/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt +++ b/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt @@ -6,5 +6,4 @@ FSharp.Test.Utilities.dll FSharp.Compiler.Private.Scripting.UnitTests.dll FSharp.Compiler.Service.Tests.dll FSharp.Core.UnitTests.dll -FSharp.Core.UnitTests.CSharp.dll FSharpSuite.Tests.dll diff --git a/eng/Build.ps1 b/eng/Build.ps1 index 68e4064940b..6003c7f7cb3 100644 --- a/eng/Build.ps1 +++ b/eng/Build.ps1 @@ -588,7 +588,6 @@ try { TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Compiler.Private.Scripting.UnitTests\FSharp.Compiler.Private.Scripting.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.Private.Scripting.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Build.UnitTests\FSharp.Build.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Build.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.csproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" # Collect output from background jobs Wait-job $bgJob | out-null @@ -603,7 +602,6 @@ try { TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Compiler.Private.Scripting.UnitTests\FSharp.Compiler.Private.Scripting.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.Private.Scripting.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Build.UnitTests\FSharp.Build.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Build.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.csproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" # Collect output from background jobs Wait-job $bgJob | out-null @@ -640,8 +638,6 @@ try { if ($testFSharpCore) { TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.csproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests.CSharp\FSharp.Core.UnitTests.CSharp.csproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests.CSharp\" } if ($testCompiler) { diff --git a/eng/build.sh b/eng/build.sh index 98463110dff..68a55be91e2 100755 --- a/eng/build.sh +++ b/eng/build.sh @@ -324,7 +324,6 @@ if [[ "$test_core_clr" == true ]]; then Test --testproject "$repo_root/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharp.Compiler.Private.Scripting.UnitTests.fsproj" --targetframework $coreclrtestframework Test --testproject "$repo_root/tests/FSharp.Build.UnitTests/FSharp.Build.UnitTests.fsproj" --targetframework $coreclrtestframework Test --testproject "$repo_root/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj" --targetframework $coreclrtestframework - Test --testproject "$repo_root/tests/FSharp.Core.UnitTests.CSharp/FSharp.Core.UnitTests.CSharp.csproj" --targetframework $coreclrtestframework fi if [[ "$test_compilercomponent_tests" == true ]]; then From 889222985dec7795a26265ba50a869528d710460 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 8 Jul 2024 13:02:45 -0400 Subject: [PATCH 23/26] Move release notes --- docs/release-notes/.FSharp.Core/8.0.400.md | 3 +-- docs/release-notes/.FSharp.Core/9.0.100.md | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/.FSharp.Core/8.0.400.md b/docs/release-notes/.FSharp.Core/8.0.400.md index 30cf1ca765c..cd6f26bd981 100644 --- a/docs/release-notes/.FSharp.Core/8.0.400.md +++ b/docs/release-notes/.FSharp.Core/8.0.400.md @@ -3,7 +3,6 @@ ### Added * `Random functions for collections` ([RFC #1135](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1135-random-functions-for-collections.md), [PR #17277](https://github.com/dotnet/fsharp/pull/17277)) -* Enable C# collection expression support for F# lists & sets. ([Language suggestion #1355](https://github.com/fsharp/fslang-suggestions/issues/1355), [RFC FS-1145 (PR#776)](https://github.com/fsharp/fslang-design/pull/776), [PR #17359](https://github.com/dotnet/fsharp/pull/17359)) ### Changed @@ -13,4 +12,4 @@ ### Breaking Changes -* Fixed argument exception throwing inconsistency - accessing an out-of-bounds collection index will now throw `ArgumentOutOfRangeException` instead of `ArgumentException` ([#17328](https://github.com/dotnet/fsharp/pull/17328)) \ No newline at end of file +* Fixed argument exception throwing inconsistency - accessing an out-of-bounds collection index will now throw `ArgumentOutOfRangeException` instead of `ArgumentException` ([#17328](https://github.com/dotnet/fsharp/pull/17328)) diff --git a/docs/release-notes/.FSharp.Core/9.0.100.md b/docs/release-notes/.FSharp.Core/9.0.100.md index 7050bf1a666..eb8a8e5b052 100644 --- a/docs/release-notes/.FSharp.Core/9.0.100.md +++ b/docs/release-notes/.FSharp.Core/9.0.100.md @@ -2,6 +2,8 @@ ### Added +* Enable C# collection expression support for F# lists & sets. ([Language suggestion #1355](https://github.com/fsharp/fslang-suggestions/issues/1355), [RFC FS-1145 (PR#776)](https://github.com/fsharp/fslang-design/pull/776), [PR #17359](https://github.com/dotnet/fsharp/pull/17359)) + ### Changed * Change compiler default setting realsig+ when building assemblies ([Issue #17384](https://github.com/dotnet/fsharp/issues/17384), [PR #17378](https://github.com/dotnet/fsharp/pull/17385)) * Change compiler default setting for compressedMetadata ([Issue #17379](https://github.com/dotnet/fsharp/issues/17379), [PR #17383](https://github.com/dotnet/fsharp/pull/17383)) From bf1a1b7f8915c2886287de24e8827173dec21afb Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 8 Jul 2024 13:55:52 -0400 Subject: [PATCH 24/26] Update trimmed size --- tests/AheadOfTime/Trimming/check.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/AheadOfTime/Trimming/check.ps1 b/tests/AheadOfTime/Trimming/check.ps1 index c9699c66c1b..4c0f2fbe6cb 100644 --- a/tests/AheadOfTime/Trimming/check.ps1 +++ b/tests/AheadOfTime/Trimming/check.ps1 @@ -42,7 +42,7 @@ function CheckTrim($root, $tfm, $outputfile, $expected_len) { # error NETSDK1124: Trimming assemblies requires .NET Core 3.0 or higher. # Check net7.0 trimmed assemblies -CheckTrim -root "SelfContained_Trimming_Test" -tfm "net8.0" -outputfile "FSharp.Core.dll" -expected_len 284672 +CheckTrim -root "SelfContained_Trimming_Test" -tfm "net8.0" -outputfile "FSharp.Core.dll" -expected_len 285184 # Check net8.0 trimmed assemblies CheckTrim -root "StaticLinkedFSharpCore_Trimming_Test" -tfm "net8.0" -outputfile "StaticLinkedFSharpCore_Trimming_Test.dll" -expected_len 8818176 From 24d1df6f3c9df6a6741a9374687bd3ea61085c16 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 8 Jul 2024 14:02:10 -0400 Subject: [PATCH 25/26] Need span --- .../Interop/CSharpCollectionExpressions.fs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/FSharp.Core.UnitTests/Interop/CSharpCollectionExpressions.fs b/tests/FSharp.Core.UnitTests/Interop/CSharpCollectionExpressions.fs index 926f1057d06..99f40530703 100644 --- a/tests/FSharp.Core.UnitTests/Interop/CSharpCollectionExpressions.fs +++ b/tests/FSharp.Core.UnitTests/Interop/CSharpCollectionExpressions.fs @@ -1,5 +1,8 @@ module FSharp.Core.UnitTests.Interop.CSharp.CollectionExpressions +// These tests require types available only in netstandard2.1 and up. +#if NET8_0_OR_GREATER + open FSharp.Test open FSharp.Test.Compiler open Xunit @@ -209,3 +212,5 @@ let ``FSharpSet: can create using C# collection expression`` () = |> withName "Test" |> compileExeAndRun |> shouldSucceed + +#endif From d3583144d203731571512555e7d846685580ad52 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 8 Jul 2024 14:32:54 -0400 Subject: [PATCH 26/26] Update trimmed size --- tests/AheadOfTime/Trimming/check.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/AheadOfTime/Trimming/check.ps1 b/tests/AheadOfTime/Trimming/check.ps1 index 4c0f2fbe6cb..028c39a7929 100644 --- a/tests/AheadOfTime/Trimming/check.ps1 +++ b/tests/AheadOfTime/Trimming/check.ps1 @@ -45,4 +45,4 @@ function CheckTrim($root, $tfm, $outputfile, $expected_len) { CheckTrim -root "SelfContained_Trimming_Test" -tfm "net8.0" -outputfile "FSharp.Core.dll" -expected_len 285184 # Check net8.0 trimmed assemblies -CheckTrim -root "StaticLinkedFSharpCore_Trimming_Test" -tfm "net8.0" -outputfile "StaticLinkedFSharpCore_Trimming_Test.dll" -expected_len 8818176 +CheckTrim -root "StaticLinkedFSharpCore_Trimming_Test" -tfm "net8.0" -outputfile "StaticLinkedFSharpCore_Trimming_Test.dll" -expected_len 8818688