From 8582675462442595ff9cbef43c23d56a9af9324e Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Mon, 17 Feb 2025 19:24:07 +0100 Subject: [PATCH 1/2] Type parameter constraint `null` implies `not struct` --- src/Compiler/Checking/ConstraintSolver.fs | 2 ++ src/Compiler/CodeGen/IlxGen.fs | 1 + .../EmittedIL/Nullness/SupportsNull.fs | 1 + .../Language/Nullness/NullableReferenceTypesTests.fs | 11 +++++++++++ 4 files changed, 15 insertions(+) diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index 50026fe382a..2578107176d 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -2478,6 +2478,8 @@ and CheckConstraintImplication (csenv: ConstraintSolverEnv) tpc1 tpc2 = | TyparConstraint.SupportsEquality _, TyparConstraint.SupportsEquality _ // comparison implies equality | TyparConstraint.SupportsComparison _, TyparConstraint.SupportsEquality _ + // 'null' implies reference type ('not struct') + | TyparConstraint.SupportsNull _, TyparConstraint.IsReferenceType _ | TyparConstraint.SupportsNull _, TyparConstraint.SupportsNull _ | TyparConstraint.NotSupportsNull _, TyparConstraint.NotSupportsNull _ | TyparConstraint.IsNonNullableStruct _, TyparConstraint.IsNonNullableStruct _ diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 53cfb3e77cd..1faeaddf82f 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -5679,6 +5679,7 @@ and GenGenericParam cenv eenv (tp: Typar) = tp.Constraints |> List.exists (function | TyparConstraint.IsReferenceType _ + // 'null' automatically implies 'not struct' | TyparConstraint.SupportsNull _ -> true | _ -> false) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/SupportsNull.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/SupportsNull.fs index 0e2484fa142..fd107853ce4 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/SupportsNull.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/SupportsNull.fs @@ -26,6 +26,7 @@ let iAcceptNullWithNullAnnotation(arg: 'a | null when 'a: not struct) = else 0 +// This test case emits the `class T` constraint in IL let iAcceptNullExplicitAnnotation(arg: 'T when 'T:null) = if isNull arg then 1 diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs index 018b81c9384..07736241189 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs @@ -17,6 +17,17 @@ let typeCheckWithStrictNullness cu = |> withNullnessOptions |> typecheck + +[] +let ``Can imply notstruct for classconstraint`` () = + FSharp """module Foo = + let failIfNull<'a when 'a : null> (a : 'a) : 'a option = + (a |> Option.ofObj) + """ + |> asLibrary + |> typecheck // This has nullable off! + |> shouldSucceed + [] let ``Warning on nullness hidden behind interface upcast`` () = FSharp """module Test From 5d889756cf58fe723212c04d5a753d37532b5968 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 21 Feb 2025 10:53:04 +0100 Subject: [PATCH 2/2] notes --- docs/release-notes/.FSharp.Compiler.Service/9.0.300.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md index f64f090744b..f01cb5d5e9d 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md @@ -17,6 +17,7 @@ * Add support for C# `Experimental` attribute. ([PR #18253](https://github.com/dotnet/fsharp/pull/18253)) * Nullness warnings are issued for signature<>implementation conformance ([PR #18186](https://github.com/dotnet/fsharp/pull/18186)) * Symbols: Add FSharpAssembly.IsFSharp ([PR #18290](https://github.com/dotnet/fsharp/pull/18290)) +* Type parameter constraint `null` in generic code will now automatically imply `not struct` ([Issue #18320](https://github.com/dotnet/fsharp/issues/18320), [PR #18323](https://github.com/dotnet/fsharp/pull/18323)) ### Changed