From b475ab70870e6d9ee0a2eda97f80972cb7680dd1 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 30 Jul 2024 15:26:41 +0200 Subject: [PATCH] Change constraint mismatch for 'supports null' from warning to error (#17462) --- src/Compiler/Checking/ConstraintSolver.fs | 10 ++++-- .../Nullness/NullableReferenceTypesTests.fs | 36 ++++++++++++++----- ...existing-negative.fs.checknulls_on.err.bsl | 13 ++----- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index 91c17302e98..00f9790d1f0 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -2644,9 +2644,13 @@ and SolveNullnessSupportsNull (csenv: ConstraintSolverEnv) ndeep m2 (trace: Opti | NullnessInfo.AmbivalentToNull -> () | NullnessInfo.WithNull -> () | NullnessInfo.WithoutNull -> - if g.checkNullness then - // TODO nullness: Shouldn't this be an error? We have a 'must support null' situation which is not being met. - return! WarnD(ConstraintSolverNullnessWarningWithType(denv, ty, n1, m, m2)) + if g.checkNullness then + // If a type would allow null in older rules of F#, we can just emit a warning. + // In the opposite case, we keep this as an an error to avoid generating incorrect code (e.g. assigning null to an int) + if (TypeNullIsExtraValue g m ty) then + return! WarnD(ConstraintSolverNullnessWarningWithType(denv, ty, n1, m, m2)) + else + return! ErrorD (ConstraintSolverError(FSComp.SR.csTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2)) } and SolveTypeUseNotSupportsNull (csenv: ConstraintSolverEnv) ndeep m2 trace ty = diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs index 7c57afabd98..de89ba8f633 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs @@ -458,7 +458,7 @@ let maybeAnon2 : {|Hello:string|} | null = null |> shouldFail |> withDiagnostics [ Error 3260, Line 4, Col 18, Line 4, Col 41, "The type '{| Hello: string |}' does not support a nullness qualitification." - Error 3261, Line 4, Col 44, Line 4, Col 48, "Nullness warning: The type '{| Hello: string |}' does not support 'null'."] + Error 43, Line 4, Col 44, Line 4, Col 48, "The type '{| Hello: string |}' does not have 'null' as a proper value"] [] @@ -567,8 +567,8 @@ myGenericFunction myValOfX |> shouldFail |> withDiagnostics [Error 3261, Line 13, Col 19, Line 13, Col 28, "Nullness warning: The type 'string' does not support 'null'." - Error 3261, Line 15, Col 20, Line 15, Col 39, "Nullness warning: The type 'System.DateTime' does not support 'null'." - Error 3261, Line 16, Col 19, Line 16, Col 22, "Nullness warning: The type 'int' does not support 'null'."] + Error 193, Line 15, Col 20, Line 15, Col 39, "The type 'System.DateTime' does not have 'null' as a proper value" + Error 1, Line 16, Col 19, Line 16, Col 22, "The type 'int' does not have 'null' as a proper value"] [] let ``Null assignment in generic code`` () = @@ -602,10 +602,28 @@ myNullReturningFunction myValOfY |> ignore |> shouldFail |> withDiagnostics [Error 3261, Line 17, Col 25, Line 17, Col 34, "Nullness warning: The type 'string' does not support 'null'." - Error 3261, Line 19, Col 26, Line 19, Col 45, "Nullness warning: The type 'System.DateTime' does not support 'null'." - Error 3261, Line 20, Col 25, Line 20, Col 36, "Nullness warning: The type '{| Anon: 'a |}' does not support 'null'." - Error 3261, Line 21, Col 26, Line 21, Col 31, "Nullness warning: The type '('a * 'b * 'c)' does not support 'null'." - Error 3261, Line 23, Col 25, Line 23, Col 33, "Nullness warning: The type 'Y' does not support 'null'."] + Error 193, Line 19, Col 26, Line 19, Col 45, "The type 'System.DateTime' does not have 'null' as a proper value" + Error 1, Line 20, Col 25, Line 20, Col 36, "The type '{| Anon: 'a |}' does not have 'null' as a proper value" + Error 1, Line 21, Col 26, Line 21, Col 31, "The type '('a * 'b * 'c)' does not have 'null' as a proper value" + Error 1, Line 23, Col 25, Line 23, Col 33, "The type 'Y' does not have 'null' as a proper value"] + + +[] +let ``Match null with int`` () = + FSharp """module MyLibrary +let test = + match null with + | null -> true + | 42 -> false + +""" + |> asLibrary + |> typeCheckWithStrictNullness + |> shouldFail + |> withDiagnostics + [Error 1, Line 5, Col 7, Line 5, Col 9, "The type 'int' does not have 'null' as a proper value. See also test.fs(3,10)-(3,14)." + Error 25, Line 3, Col 11, Line 3, Col 15, "Incomplete pattern matches on this expression."] + [] let ``Nullnesss support for F# types`` () = @@ -658,10 +676,10 @@ looseFunc(maybeTuple2) |> ignore |> withDiagnostics [ Error 43, Line 21, Col 12, Line 21, Col 16, "The constraints 'null' and 'not null' are inconsistent" Error 3260, Line 27, Col 18, Line 27, Col 34, "The type '(int * int)' does not support a nullness qualitification." - Error 3261, Line 27, Col 37, Line 27, Col 41, "Nullness warning: The type '(int * int)' does not support 'null'." + Error 43, Line 27, Col 37, Line 27, Col 41, "The type '(int * int)' does not have 'null' as a proper value" Error 3261, Line 29, Col 12, Line 29, Col 19, "Nullness warning: The type 'MyDu | null' supports 'null' but a non-null type is expected." Error 3261, Line 30, Col 12, Line 30, Col 21, "Nullness warning: The type 'MyRecord | null' supports 'null' but a non-null type is expected." - Error 3261, Line 40, Col 36, Line 40, Col 40, "Nullness warning: The type 'Maybe' does not support 'null'."] + Error 43, Line 40, Col 36, Line 40, Col 40, "The type 'Maybe' does not have 'null' as a proper value"] [] let ``Static member on Record with null arg`` () = diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/existing-negative.fs.checknulls_on.err.bsl b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/existing-negative.fs.checknulls_on.err.bsl index cada047d442..eaffe804e94 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/existing-negative.fs.checknulls_on.err.bsl +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/existing-negative.fs.checknulls_on.err.bsl @@ -1,10 +1,3 @@ -existing-negative.fs (10,17)-(10,33) typecheck error Nullness warning: The type '(int * int)' does not support 'null'. -existing-negative.fs (12,17)-(12,28) typecheck error Nullness warning: The type 'int list' does not support 'null'. -existing-negative.fs (14,17)-(14,30) typecheck error Nullness warning: The type '(int -> int)' does not support 'null'. -existing-negative.fs (22,25)-(22,31) typecheck error The type of a field using the 'DefaultValue' attribute must admit default initialization, i.e. have 'null' as a proper value or be a struct type whose fields all admit default initialization. You can use 'DefaultValue(false)' to disable this check -existing-negative.fs (27,25)-(27,31) typecheck error The type of a field using the 'DefaultValue' attribute must admit default initialization, i.e. have 'null' as a proper value or be a struct type whose fields all admit default initialization. You can use 'DefaultValue(false)' to disable this check -existing-negative.fs (32,25)-(32,31) typecheck error The type of a field using the 'DefaultValue' attribute must admit default initialization, i.e. have 'null' as a proper value or be a struct type whose fields all admit default initialization. You can use 'DefaultValue(false)' to disable this check -existing-negative.fs (37,25)-(37,31) typecheck error The type of a field using the 'DefaultValue' attribute must admit default initialization, i.e. have 'null' as a proper value or be a struct type whose fields all admit default initialization. You can use 'DefaultValue(false)' to disable this check -existing-negative.fs (43,25)-(43,31) typecheck error The type of a field using the 'DefaultValue' attribute must admit default initialization, i.e. have 'null' as a proper value or be a struct type whose fields all admit default initialization. You can use 'DefaultValue(false)' to disable this check -existing-negative.fs (47,25)-(47,31) typecheck error The type of a field using the 'DefaultValue' attribute must admit default initialization, i.e. have 'null' as a proper value or be a struct type whose fields all admit default initialization. You can use 'DefaultValue(false)' to disable this check -existing-negative.fs (51,25)-(51,31) typecheck error The type of a field using the 'DefaultValue' attribute must admit default initialization, i.e. have 'null' as a proper value or be a struct type whose fields all admit default initialization. You can use 'DefaultValue(false)' to disable this check \ No newline at end of file +existing-negative.fs (10,17)-(10,33) typecheck error The type '(int * int)' does not have 'null' as a proper value +existing-negative.fs (12,17)-(12,28) typecheck error The type 'int list' does not have 'null' as a proper value +existing-negative.fs (14,17)-(14,30) typecheck error The type '(int -> int)' does not have 'null' as a proper value \ No newline at end of file