Skip to content

Commit 6ebd419

Browse files
CopilotT-Gro
andcommitted
Fix nullness flow for type aliases after null pattern - targeted approach
Co-authored-by: T-Gro <[email protected]>
1 parent 9da5dd9 commit 6ebd419

5 files changed

Lines changed: 9 additions & 102 deletions

File tree

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10733,18 +10733,15 @@ and TcMatchClause cenv inputTy (resultTy: OverallTy) env isFirst tpenv synMatchC
1073310733

1073410734
let inputTypeForNextPatterns=
1073510735
let removeNull t =
10736-
// Preserve original type structure while refining nullness
10737-
match stripTyEqns cenv.g t with
10738-
| TType_app (tcref, _, _) when not tcref.Deref.IsStructOrEnumTycon ->
10739-
// Apply to original type to preserve aliases
10740-
match t with
10741-
| TType_app (tcrefOrig, tinstOrig, _) -> TType_app (tcrefOrig, tinstOrig, KnownWithoutNull)
10742-
| _ -> replaceNullnessOfTy KnownWithoutNull t
10743-
| TType_var _ ->
10744-
match t with
10745-
| TType_var (tpOrig, _) -> TType_var (tpOrig, KnownWithoutNull)
10746-
| _ -> replaceNullnessOfTy KnownWithoutNull t
10747-
| _ -> t
10736+
// Check if this is a type abbreviation that we should preserve
10737+
match t with
10738+
| TType_app (tcref, tinst, _) when tcref.Deref.IsTypeAbbrev ->
10739+
// Preserve the type abbreviation structure while refining nullness
10740+
TType_app (tcref, tinst, KnownWithoutNull)
10741+
| _ ->
10742+
// Use existing logic for non-abbreviation types
10743+
let stripped = stripTyEqns cenv.g t
10744+
replaceNullnessOfTy KnownWithoutNull stripped
1074810745
let rec isWild (p:Pattern) =
1074910746
match p with
1075010747
| TPat_wild _ -> true

src/Compiler/TypedTree/TypedTreeOps.fs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -782,27 +782,6 @@ let rec stripTyEqnsA g canShortcut ty =
782782

783783
let stripTyEqns g ty = stripTyEqnsA g false ty
784784

785-
/// Try to refine a type by removing 'null' from its top-level nullness, preserving any type abbreviations.
786-
/// - Strip type equations/abbreviations only for the purpose of deciding if we can remove 'null'.
787-
/// - If applicable, apply the refinement to the original 'ty' using replaceNullnessOfTy, so aliases are not discarded.
788-
/// - Only refine reference-like heads (including type variables).
789-
let tryRefineToNonNullPreservingAbbrev (g: TcGlobals) (ty: TType) : TType option =
790-
// Use stripTyEqns to decide if we can refine, but apply to original type
791-
let stripped = stripTyEqns g ty
792-
match stripped with
793-
| TType_app (tcref, _, _) when not tcref.Deref.IsStructOrEnumTycon ->
794-
// Apply refinement to original type structure to preserve aliases
795-
match ty with
796-
| TType_app (tcrefOrig, tinstOrig, _) -> Some (TType_app (tcrefOrig, tinstOrig, KnownWithoutNull))
797-
| TType_var (tpOrig, _) -> Some (TType_var (tpOrig, KnownWithoutNull))
798-
| TType_fun (dOrig, rOrig, _) -> Some (TType_fun (dOrig, rOrig, KnownWithoutNull))
799-
| _ -> Some (replaceNullnessOfTy KnownWithoutNull ty)
800-
| TType_var _ ->
801-
match ty with
802-
| TType_var (tpOrig, _) -> Some (TType_var (tpOrig, KnownWithoutNull))
803-
| _ -> Some (replaceNullnessOfTy KnownWithoutNull ty)
804-
| _ -> None
805-
806785
let evalTupInfoIsStruct aexpr =
807786
match aexpr with
808787
| TupInfo.Const b -> b

src/Compiler/TypedTree/TypedTreeOps.fsi

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -609,12 +609,6 @@ val stripTyEqnsA: TcGlobals -> bool -> TType -> TType
609609

610610
val stripTyEqns: TcGlobals -> TType -> TType
611611

612-
/// Try to refine a type by removing 'null' from its top-level nullness, preserving any type abbreviations.
613-
/// - Strip type equations/abbreviations only for the purpose of deciding if we can remove 'null'.
614-
/// - If applicable, apply the refinement to the original 'ty' using replaceNullnessOfTy, so aliases are not discarded.
615-
/// - Only refine reference-like heads (including type variables).
616-
val tryRefineToNonNullPreservingAbbrev: TcGlobals -> TType -> TType option
617-
618612
val stripTyEqnsAndMeasureEqns: TcGlobals -> TType -> TType
619613

620614
val tryNormalizeMeasureInType: TcGlobals -> TType -> TType

tests/FSharp.Compiler.ComponentTests/Language/Nullness/Match_Null_DefaultingAndAlias.fs

Lines changed: 0 additions & 28 deletions
This file was deleted.

tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,38 +1554,3 @@ let y = x :> IEquatable<string> // Should not warn about nullness
15541554
|> asLibrary
15551555
|> typeCheckWithStrictNullness
15561556
|> shouldSucceed
1557-
1558-
[<Fact>]
1559-
let ``Match null pattern refines type to non-null preserving aliases`` () =
1560-
FSharp """module Test
1561-
1562-
type objnull = obj | null
1563-
type stringnull = string | null
1564-
1565-
// 1) Defaulting case: result unconstrained; null pattern forces a nullable top type.
1566-
let getEnvDefault (_: string) = failwith ""
1567-
1568-
let valueDefault =
1569-
match "ENVVAR" |> getEnvDefault with
1570-
| null -> "missing"
1571-
| x -> x.ToString() // x must be refined to obj (non-null)
1572-
1573-
// 2) Alias to obj | null
1574-
let getEnvAliasObj (_: string) : objnull = failwith "stub"
1575-
1576-
let valueAliasObj =
1577-
match getEnvAliasObj "ENVVAR" with
1578-
| null -> "missing"
1579-
| x -> x.ToString() // x must be refined to obj (non-null)
1580-
1581-
// 3) Alias to string | null
1582-
let getEnvAliasStr (_: string) : stringnull = failwith "stub"
1583-
1584-
let valueAliasStr =
1585-
match getEnvAliasStr "ENVVAR" with
1586-
| null -> 0
1587-
| s -> s.Length // s must be refined to string (non-null)
1588-
"""
1589-
|> withNullnessOptions
1590-
|> typecheck
1591-
|> shouldSucceed

0 commit comments

Comments
 (0)