From bc36b3d61797932f4e5d6a106eccee78ca3ce89a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 7 Jul 2025 10:59:48 +0000 Subject: [PATCH 1/6] Initial plan From 5dca8c7faffd8862f13dd67bfef4c5dacc8cb459 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 7 Jul 2025 11:18:54 +0000 Subject: [PATCH 2/6] Re-enable UnmanagedConstraintCsharpInterop for F# 9.0 Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com> --- src/Compiler/Facilities/LanguageFeatures.fs | 2 +- .../Conformance/Constraints/Unmanaged.fs | 49 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index daabcd21d1b..98743549dc7 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -223,6 +223,7 @@ type LanguageVersion(versionText) = LanguageFeature.LowerSimpleMappingsInComprehensionsToFastLoops, languageVersion90 LanguageFeature.ParsedHashDirectiveArgumentNonQuotes, languageVersion90 LanguageFeature.EmptyBodiedComputationExpressions, languageVersion90 + LanguageFeature.UnmanagedConstraintCsharpInterop, languageVersion90 // F# 10.0 LanguageFeature.EnforceAttributeTargets, languageVersion100 @@ -238,7 +239,6 @@ type LanguageVersion(versionText) = LanguageFeature.AllowTypedLetOrUseBang, languageVersion100 // F# preview (still preview in 10.0) - LanguageFeature.UnmanagedConstraintCsharpInterop, previewVersion // not enabled because: https://github.com/dotnet/fsharp/issues/17509 LanguageFeature.FromEndSlicing, previewVersion // Unfinished features --- needs work LanguageFeature.AllowAccessModifiersToAutoPropertiesGettersAndSetters, previewVersion // Stopped printing arguments to indexed properties ] diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index 5b9af3c8f20..64226264e73 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -471,6 +471,22 @@ printf "%s" (CsharpStruct.Hi()) IL_0000: ret } """] + [] + let ``FSharp generates modreq for CSharp to consume in v9`` () = + Fsx "let testMyFunction (x: 'TUnmanaged when 'TUnmanaged : unmanaged) = ()" + |> withLangVersion90 + |> compile + |> shouldSucceed + |> verifyIL [""" + .method public static void testMyFunction(!!TUnmanaged x) cil managed + { + .param type TUnmanaged + .custom instance void [runtime]System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ret + } """] + [] let ``FSharp does not generate modreq for VBNET to consume in v7`` () = Fsx "let testMyFunction (x: 'TUnmanaged when 'TUnmanaged : unmanaged) = ()" @@ -485,6 +501,39 @@ printf "%s" (CsharpStruct.Hi()) IL_0000: ret } """] + [] + let ``FSharp does not generate modreq for VBNET to consume in v8`` () = + Fsx "let testMyFunction (x: 'TUnmanaged when 'TUnmanaged : unmanaged) = ()" + |> withLangVersion80 + |> compile + |> shouldSucceed + |> verifyIL [""" + .method public static void testMyFunction(!!TUnmanaged x) cil managed + { + + .maxstack 8 + IL_0000: ret + } """] + + [] + let ``Unmanaged constraint in lambda works with F# 9.0`` () = + Fsx """ +open System + +let printTypeConstraintsNative<'T when 'T : unmanaged> () = printf $"Hello: {typeof<'T>.FullName} is unmanaged" + +let Main() = + let func (x:int) : 'T when 'T : unmanaged = Unchecked.defaultof<'T> + let initFinite = Seq.init 3 func + printfn "%A" initFinite + +printTypeConstraintsNative() +Main() + """ + |> withLangVersion90 + |> compile + |> shouldSucceed + [] let ``C# can consume F#-defined struct with unmanaged constraint - valid`` () = From 9f670d06a60a79f45329d513594155302d44ff54 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 7 Jul 2025 11:53:48 +0000 Subject: [PATCH 3/6] Add tests to reproduce issue #17509 with UnmanagedConstraintCsharpInterop - Add test that reproduces the original lambda issue reported in #17509 - Add test that verifies the invalid IL generation in Specialize method - Tests show that when UnmanagedConstraintCsharpInterop is enabled with preview version, it generates modreq constraint in the Specialize method IL which causes TypeLoadException - This demonstrates the root cause of the issue: invalid IL generation in lambda constraints Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com> --- .../Conformance/Constraints/Unmanaged.fs | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index 64226264e73..3e1adb6b0f0 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -516,7 +516,10 @@ printf "%s" (CsharpStruct.Hi()) } """] [] - let ``Unmanaged constraint in lambda works with F# 9.0`` () = + let ``Unmanaged constraint in lambda reproduces issue 17509`` () = + // This test reproduces the issue https://github.com/dotnet/fsharp/issues/17509 + // When UnmanagedConstraintCsharpInterop is enabled, it generates invalid IL + // causing a TypeLoadException at runtime Fsx """ open System @@ -530,10 +533,39 @@ let Main() = printTypeConstraintsNative() Main() """ - |> withLangVersion90 + |> withLangVersionPreview + |> asExe |> compile |> shouldSucceed + [] + let ``Unmanaged constraint in lambda generates invalid IL for Specialize method with preview version`` () = + Fsx """ +let Main() = + let func (x:int) : 'T when 'T : unmanaged = Unchecked.defaultof<'T> + let initFinite = Seq.init 3 func + printfn "%A" initFinite +Main() + """ + |> withLangVersionPreview + |> asExe + |> compile + |> shouldSucceed + |> verifyIL [""" + .method public strict virtual instance object Specialize() cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + .param type T + .custom instance void [runtime]System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: newobj instance void class Test/func@3T::.ctor(class Test/func@3) + IL_0006: box class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2 + IL_000b: ret + } """] + [] let ``C# can consume F#-defined struct with unmanaged constraint - valid`` () = From 690c65d1e362feaf3c4f219219bccd2e99d8d2de Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 7 Jul 2025 12:58:59 +0000 Subject: [PATCH 4/6] Address PR feedback: move feature to F# 10.0, clean up tests, and expand IL verification Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com> --- src/Compiler/Facilities/LanguageFeatures.fs | 2 +- .../Conformance/Constraints/Unmanaged.fs | 62 ++++++++++++++----- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index 98743549dc7..5ea044b603c 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -223,7 +223,6 @@ type LanguageVersion(versionText) = LanguageFeature.LowerSimpleMappingsInComprehensionsToFastLoops, languageVersion90 LanguageFeature.ParsedHashDirectiveArgumentNonQuotes, languageVersion90 LanguageFeature.EmptyBodiedComputationExpressions, languageVersion90 - LanguageFeature.UnmanagedConstraintCsharpInterop, languageVersion90 // F# 10.0 LanguageFeature.EnforceAttributeTargets, languageVersion100 @@ -237,6 +236,7 @@ type LanguageVersion(versionText) = LanguageFeature.BetterAnonymousRecordParsing, languageVersion100 LanguageFeature.ScopedNowarn, languageVersion100 LanguageFeature.AllowTypedLetOrUseBang, languageVersion100 + LanguageFeature.UnmanagedConstraintCsharpInterop, languageVersion100 // F# preview (still preview in 10.0) LanguageFeature.FromEndSlicing, previewVersion // Unfinished features --- needs work diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index 3e1adb6b0f0..9bf56eb1b59 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -501,19 +501,7 @@ printf "%s" (CsharpStruct.Hi()) IL_0000: ret } """] - [] - let ``FSharp does not generate modreq for VBNET to consume in v8`` () = - Fsx "let testMyFunction (x: 'TUnmanaged when 'TUnmanaged : unmanaged) = ()" - |> withLangVersion80 - |> compile - |> shouldSucceed - |> verifyIL [""" - .method public static void testMyFunction(!!TUnmanaged x) cil managed - { - - .maxstack 8 - IL_0000: ret - } """] + [] let ``Unmanaged constraint in lambda reproduces issue 17509`` () = @@ -535,7 +523,7 @@ Main() """ |> withLangVersionPreview |> asExe - |> compile + |> compileAndRun |> shouldSucceed [] @@ -552,7 +540,35 @@ Main() |> compile |> shouldSucceed |> verifyIL [""" - .method public strict virtual instance object Specialize() cil managed + .class auto ansi serializable sealed nested assembly beforefieldinit func@3 + extends class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2 + { + .field static assembly initonly class Test/func@3 @_instance + .method assembly specialname rtspecialname + instance void .ctor() cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2::.ctor() + IL_0006: ret + } + + .method public strict virtual instance class [runtime]System.IntPtr + Invoke(int32 x) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldsfld class [runtime]System.IntPtr [runtime]System.IntPtr::Zero + IL_0005: ret + } + + .method public strict virtual instance object + Specialize() cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -564,7 +580,21 @@ Main() IL_0001: newobj instance void class Test/func@3T::.ctor(class Test/func@3) IL_0006: box class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2 IL_000b: ret - } """] + } + + .method private specialname rtspecialname static + void .cctor() cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 10 + IL_0000: newobj instance void Test/func@3::.ctor() + IL_0005: stsfld class Test/func@3 Test/func@3::@_instance + IL_000a: ret + } + } + """] [] From d2b747e71717ccac2a2901188a49f0e2f26bd635 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 11 Jul 2025 15:15:01 +0200 Subject: [PATCH 5/6] Fix closure generation with `unmanaged` --- src/Compiler/TypedTree/TypedTreeOps.fs | 1 + .../Conformance/Constraints/Unmanaged.fs | 72 ++----------------- 2 files changed, 7 insertions(+), 66 deletions(-) diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index b14dbd753b1..368dd9a99ce 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9970,6 +9970,7 @@ let isCompiledOrWitnessPassingConstraint (g: TcGlobals) cx = | TyparConstraint.IsNonNullableStruct _ | TyparConstraint.IsReferenceType _ | TyparConstraint.RequiresDefaultConstructor _ + | TyparConstraint.IsUnmanaged _ // implies "struct" and also causes a modreq | TyparConstraint.CoercesTo _ -> true | TyparConstraint.MayResolveMember _ when g.langVersion.SupportsFeature LanguageFeature.WitnessPassing -> true | _ -> false diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index 9bf56eb1b59..917469a883f 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -474,7 +474,7 @@ printf "%s" (CsharpStruct.Hi()) [] let ``FSharp generates modreq for CSharp to consume in v9`` () = Fsx "let testMyFunction (x: 'TUnmanaged when 'TUnmanaged : unmanaged) = ()" - |> withLangVersion90 + |> withLangVersion10 |> compile |> shouldSucceed |> verifyIL [""" @@ -487,21 +487,6 @@ printf "%s" (CsharpStruct.Hi()) IL_0000: ret } """] - [] - let ``FSharp does not generate modreq for VBNET to consume in v7`` () = - Fsx "let testMyFunction (x: 'TUnmanaged when 'TUnmanaged : unmanaged) = ()" - |> withLangVersion70 - |> compile - |> shouldSucceed - |> verifyIL [""" - .method public static void testMyFunction(!!TUnmanaged x) cil managed - { - - .maxstack 8 - IL_0000: ret - } """] - - [] let ``Unmanaged constraint in lambda reproduces issue 17509`` () = @@ -525,6 +510,7 @@ Main() |> asExe |> compileAndRun |> shouldSucceed + |> verifyOutput "Hello: System.IntPtr is unmanagedseq [0n; 0n; 0n]" [] let ``Unmanaged constraint in lambda generates invalid IL for Specialize method with preview version`` () = @@ -540,61 +526,15 @@ Main() |> compile |> shouldSucceed |> verifyIL [""" - .class auto ansi serializable sealed nested assembly beforefieldinit func@3 - extends class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2 - { - .field static assembly initonly class Test/func@3 @_instance - .method assembly specialname rtspecialname - instance void .ctor() cil managed + .method assembly strict virtual instance class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2 DirectInvoke() cil managed { - .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - - .maxstack 8 - IL_0000: ldarg.0 - IL_0001: call instance void class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2::.ctor() - IL_0006: ret - } - - .method public strict virtual instance class [runtime]System.IntPtr - Invoke(int32 x) cil managed - { - .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - - .maxstack 8 - IL_0000: ldsfld class [runtime]System.IntPtr [runtime]System.IntPtr::Zero - IL_0005: ret - } - - .method public strict virtual instance object - Specialize() cil managed - { - .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) .param type T .custom instance void [runtime]System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 - IL_0000: ldarg.0 - IL_0001: newobj instance void class Test/func@3T::.ctor(class Test/func@3) - IL_0006: box class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2 - IL_000b: ret - } - - .method private specialname rtspecialname static - void .cctor() cil managed - { - .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - - .maxstack 10 - IL_0000: newobj instance void Test/func@3::.ctor() - IL_0005: stsfld class Test/func@3 Test/func@3::@_instance - IL_000a: ret - } - } - """] + IL_0000: ldsfld class Test/'func@3-1' class Test/'func@3-1'::@_instance + IL_0005: ret + } """] [] From 5c4bbf69fdd2f3a25d3794d65d9f49b8e3918f42 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 11 Jul 2025 18:22:03 +0200 Subject: [PATCH 6/6] Update tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs --- .../Conformance/Constraints/Unmanaged.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs index 917469a883f..173f18537a5 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs @@ -501,7 +501,7 @@ let printTypeConstraintsNative<'T when 'T : unmanaged> () = printf $"Hello: {typ let Main() = let func (x:int) : 'T when 'T : unmanaged = Unchecked.defaultof<'T> let initFinite = Seq.init 3 func - printfn "%A" initFinite + printf "%A" initFinite printTypeConstraintsNative() Main()