Skip to content

Commit 24a4308

Browse files
authored
Fix nullness flow for type aliases after null pattern (#18852)
1 parent def2b82 commit 24a4308

File tree

4 files changed

+76
-1
lines changed

4 files changed

+76
-1
lines changed

docs/release-notes/.FSharp.Compiler.Service/11.0.0.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
### Fixed
22

3+
* Nullness: Fix nullness refinement in match expressions to correctly narrow type to non-null after matching null case. ([Issue #18488](https://github.com/dotnet/fsharp/issues/18488), [PR #18852](https://github.com/dotnet/fsharp/pull/18852))
34
* Scripts: Fix resolving the dotnet host path when an SDK directory is specified. ([PR #18960](https://github.com/dotnet/fsharp/pull/18960))
45
* Fix excessive StackGuard thread jumping ([PR #18971](https://github.com/dotnet/fsharp/pull/18971))
56
* Adjust conservative method-overload duplicate detection rules for nativeptr types ([PR #18911](https://github.com/dotnet/fsharp/pull/18911))

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10761,6 +10761,9 @@ and TcMatchClause cenv inputTy (resultTy: OverallTy) env isFirst tpenv synMatchC
1076110761

1076210762
let inputTypeForNextPatterns=
1076310763
let removeNull t =
10764+
// Strip type equations (including abbreviations) and set nullness to non-null.
10765+
// For type abbreviations like `type objnull = obj | null`, we need to expand
10766+
// the abbreviation and apply non-null to the underlying type.
1076410767
let stripped = stripTyEqns cenv.g t
1076510768
replaceNullnessOfTy KnownWithoutNull stripped
1076610769
let rec isWild (p:Pattern) =

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

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,3 +1554,72 @@ let y = x :> IEquatable<string> // Should not warn about nullness
15541554
|> asLibrary
15551555
|> typeCheckWithStrictNullness
15561556
|> shouldSucceed
1557+
1558+
// Regression for https://github.com/dotnet/fsharp/issues/18488
1559+
// This tests the exact scenario from the issue with obj | null return type
1560+
[<FSharp.Test.FactForNETCOREAPPAttribute>]
1561+
let ``Match null branch should refine variable to non-null - exact issue scenario`` () =
1562+
FSharp """module Test
1563+
1564+
let bar : obj =
1565+
let getEnvironmentVariable : string -> (obj|null) = fun _ -> null
1566+
1567+
match "ENVVAR" |> getEnvironmentVariable with
1568+
| null -> obj() // return some obj in null case
1569+
| x -> x // x should be obj, not obj | null - no warning expected
1570+
"""
1571+
|> asLibrary
1572+
|> typeCheckWithStrictNullness
1573+
|> shouldSucceed
1574+
1575+
// Regression for https://github.com/dotnet/fsharp/issues/18488
1576+
[<FSharp.Test.FactForNETCOREAPPAttribute>]
1577+
let ``Match null branch should refine variable to non-null in subsequent branches - type alias`` () =
1578+
FSharp """module Test
1579+
1580+
// Type alias for nullable obj
1581+
type objnull = obj | null
1582+
1583+
let getEnvAliasObj (_: string) : objnull = failwith "stub"
1584+
1585+
// After matching null case, x should be refined to non-null
1586+
let valueAliasObj =
1587+
match getEnvAliasObj "ENVVAR" with
1588+
| null -> "missing"
1589+
| x -> x.GetType().Name // x should be obj, not obj | null - no warning expected
1590+
"""
1591+
|> asLibrary
1592+
|> typeCheckWithStrictNullness
1593+
|> shouldSucceed
1594+
1595+
// Regression for https://github.com/dotnet/fsharp/issues/18488
1596+
[<FSharp.Test.FactForNETCOREAPPAttribute>]
1597+
let ``Match null branch should refine variable to non-null in subsequent branches - direct nullable type`` () =
1598+
FSharp """module Test
1599+
1600+
let getValue () : string | null = failwith "stub"
1601+
1602+
// After matching null case, x should be refined to non-null
1603+
let result =
1604+
match getValue () with
1605+
| null -> "missing"
1606+
| x -> x.ToUpper() // x should be string, not string | null - no warning expected
1607+
"""
1608+
|> asLibrary
1609+
|> typeCheckWithStrictNullness
1610+
|> shouldSucceed
1611+
1612+
// Regression for https://github.com/dotnet/fsharp/issues/18488
1613+
[<FSharp.Test.FactForNETCOREAPPAttribute>]
1614+
let ``Match null branch should refine variable to non-null - Environment.GetEnvironmentVariable`` () =
1615+
FSharp """module Test
1616+
1617+
// Real-world scenario from issue #18488
1618+
let value =
1619+
match System.Environment.GetEnvironmentVariable "ENVVAR" with
1620+
| null -> "missing"
1621+
| x -> x.ToLower() // x should be string, not string | null - no warning expected
1622+
"""
1623+
|> asLibrary
1624+
|> typeCheckWithStrictNullness
1625+
|> shouldSucceed

tests/ILVerify/ilverify.ps1

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ Write-Host "Checking whether running on Windows: $IsWindows"
2727
Write-Host "Repository path: $repo_path"
2828

2929
[string] $script = if ($IsWindows) { Join-Path $repo_path "build.cmd" } else { Join-Path $repo_path "build.sh" }
30-
[string] $additional_arguments = if ($IsWindows) { "-noVisualStudio" } else { "" }
30+
[string] $additional_arguments = if ($IsWindows) { "-noVisualStudio -ci -bootstrap" } else { "" }
3131

3232
# Set environment variable to disable UpdateXlf target (not needed for IL verification)
3333
$env:UpdateXlfOnBuild = "false"
34+
# Disable PDB conversion for SymStore (not needed for IL verification)
35+
$env:PublishWindowsPdb = "false"
3436

3537
# Set configurations to build
3638
[string[]] $configurations = @("Debug", "Release")

0 commit comments

Comments
 (0)