Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.0.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Fixed

* 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))
* Scripts: Fix resolving the dotnet host path when an SDK directory is specified. ([PR #18960](https://github.com/dotnet/fsharp/pull/18960))
* Fix excessive StackGuard thread jumping ([PR #18971](https://github.com/dotnet/fsharp/pull/18971))
* Adjust conservative method-overload duplicate detection rules for nativeptr types ([PR #18911](https://github.com/dotnet/fsharp/pull/18911))
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/Checking/Expressions/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10761,6 +10761,9 @@ and TcMatchClause cenv inputTy (resultTy: OverallTy) env isFirst tpenv synMatchC

let inputTypeForNextPatterns=
let removeNull t =
// Strip type equations (including abbreviations) and set nullness to non-null.
// For type abbreviations like `type objnull = obj | null`, we need to expand
// the abbreviation and apply non-null to the underlying type.
let stripped = stripTyEqns cenv.g t
replaceNullnessOfTy KnownWithoutNull stripped
let rec isWild (p:Pattern) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1554,3 +1554,72 @@ let y = x :> IEquatable<string> // Should not warn about nullness
|> asLibrary
|> typeCheckWithStrictNullness
|> shouldSucceed

// Regression for https://github.com/dotnet/fsharp/issues/18488
// This tests the exact scenario from the issue with obj | null return type
[<FSharp.Test.FactForNETCOREAPPAttribute>]
let ``Match null branch should refine variable to non-null - exact issue scenario`` () =
FSharp """module Test

let bar : obj =
let getEnvironmentVariable : string -> (obj|null) = fun _ -> null

match "ENVVAR" |> getEnvironmentVariable with
| null -> obj() // return some obj in null case
| x -> x // x should be obj, not obj | null - no warning expected
"""
|> asLibrary
|> typeCheckWithStrictNullness
|> shouldSucceed

// Regression for https://github.com/dotnet/fsharp/issues/18488
[<FSharp.Test.FactForNETCOREAPPAttribute>]
let ``Match null branch should refine variable to non-null in subsequent branches - type alias`` () =
FSharp """module Test

// Type alias for nullable obj
type objnull = obj | null

let getEnvAliasObj (_: string) : objnull = failwith "stub"

// After matching null case, x should be refined to non-null
let valueAliasObj =
match getEnvAliasObj "ENVVAR" with
| null -> "missing"
| x -> x.GetType().Name // x should be obj, not obj | null - no warning expected
"""
|> asLibrary
|> typeCheckWithStrictNullness
|> shouldSucceed

// Regression for https://github.com/dotnet/fsharp/issues/18488
[<FSharp.Test.FactForNETCOREAPPAttribute>]
let ``Match null branch should refine variable to non-null in subsequent branches - direct nullable type`` () =
FSharp """module Test

let getValue () : string | null = failwith "stub"

// After matching null case, x should be refined to non-null
let result =
match getValue () with
| null -> "missing"
| x -> x.ToUpper() // x should be string, not string | null - no warning expected
"""
|> asLibrary
|> typeCheckWithStrictNullness
|> shouldSucceed

// Regression for https://github.com/dotnet/fsharp/issues/18488
[<FSharp.Test.FactForNETCOREAPPAttribute>]
let ``Match null branch should refine variable to non-null - Environment.GetEnvironmentVariable`` () =
FSharp """module Test

// Real-world scenario from issue #18488
let value =
match System.Environment.GetEnvironmentVariable "ENVVAR" with
| null -> "missing"
| x -> x.ToLower() // x should be string, not string | null - no warning expected
"""
|> asLibrary
|> typeCheckWithStrictNullness
|> shouldSucceed
4 changes: 3 additions & 1 deletion tests/ILVerify/ilverify.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ Write-Host "Checking whether running on Windows: $IsWindows"
Write-Host "Repository path: $repo_path"

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

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

# Set configurations to build
[string[]] $configurations = @("Debug", "Release")
Expand Down
Loading