Skip to content
Open
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.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
* Fix parallel compilation of scripts ([PR #19649](https://github.com/dotnet/fsharp/pull/19649))
* Fix parser recovery, name resolution, and code completion for unfinished enum patterns ([PR #19708](https://github.com/dotnet/fsharp/pull/19708))
* Parser: fix unexpected diagnostics in debug builds, improve error messages ([PR #19730](https://github.com/dotnet/fsharp/pull/19730))
* Allow `| null` nullable annotation on a `[<MeasureAnnotatedAbbreviation>]` over a reference type (e.g. the FSharp.UMX `type string<[<Measure>] 'm> = string` pattern). ([Issue #19657](https://github.com/dotnet/fsharp/issues/19657))

### Added

Expand Down
4 changes: 3 additions & 1 deletion src/Compiler/Checking/ConstraintSolver.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2990,7 +2990,9 @@ and SolveTypeIsReferenceType (csenv: ConstraintSolverEnv) ndeep m2 trace ty =
| ValueSome destTypar ->
AddConstraint csenv ndeep m2 trace destTypar (TyparConstraint.IsReferenceType m)
| _ ->
if isRefTy g ty then CompleteD
// Strip measure equations so we test the underlying erased representation — see dotnet/fsharp#19657.
let underlyingTy = stripTyEqnsAndMeasureEqns g ty
if isRefTy g underlyingTy then CompleteD
else ErrorD (ConstraintSolverError(FSComp.SR.csGenericConstructRequiresReferenceSemantics(NicePrint.minimalStringOfType denv ty), m, m))

and SolveTypeRequiresDefaultConstructor (csenv: ConstraintSolverEnv) ndeep m2 trace origTy =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2065,6 +2065,146 @@ let processMyStr (x:mystring<mykg>) =
Error 3261, Line 12, Col 27, Line 12, Col 39, "Nullness warning: A non-nullable 'string' was expected but this expression is nullable. Consider either changing the target to also be nullable, or use pattern matching to safely handle the null case of this expression."
]

[<FSharp.Test.FactForNETCOREAPPAttribute>]
let ``MeasureAnnotatedAbbreviation over string allows nullable annotation in all positions`` () =
FSharp """module MyLibrary

[<MeasureAnnotatedAbbreviation>]
type string<[<Measure>] 'm> = string

[<Measure>] type test
type TestType = string<test>

let x: (TestType | null) = Unchecked.defaultof<TestType>

let consume (s: TestType | null) = ()

let produce () : TestType | null = null

type NullableTestType = TestType | null

let y: (string<test> | null) = null
"""
|> asLibrary
|> typeCheckWithStrictNullness
|> shouldSucceed

[<FSharp.Test.FactForNETCOREAPPAttribute>]
let ``MeasureAnnotatedAbbreviation over user reference type allows nullable annotation`` () =
FSharp """module MyLibrary

type MyRef() = member _.Hello = "hi"

[<MeasureAnnotatedAbbreviation>]
type MyRef<[<Measure>] 'm> = MyRef

[<Measure>] type tag
type Tagged = MyRef<tag>

let f (x: Tagged | null) : Tagged | null = x
let g : Tagged | null = null
"""
|> asLibrary
|> typeCheckWithStrictNullness
|> shouldSucceed

[<FSharp.Test.FactForNETCOREAPPAttribute>]
let ``MeasureAnnotatedAbbreviation over value type rejects nullable annotation`` () =
FSharp """module MyLibrary

[<MeasureAnnotatedAbbreviation>]
type int<[<Measure>] 'm> = int

[<MeasureAnnotatedAbbreviation>]
type DateTime<[<Measure>] 'm> = System.DateTime

[<Measure>] type kg
[<Measure>] type s

let bad1 : (int<kg> | null) = null
let bad2 : (DateTime<s> | null) = null
"""
|> asLibrary
|> typeCheckWithStrictNullness
|> shouldFail
|> withDiagnostics [
Error 3260, Line 12, Col 13, Line 12, Col 27, "The type 'int<kg>' does not support a nullness qualification."
Error 43, Line 12, Col 13, Line 12, Col 27, "A generic construct requires that the type 'int<kg>' have reference semantics, but it does not, i.e. it is a struct"
Error 3260, Line 13, Col 13, Line 13, Col 31, "The type 'DateTime<s>' does not support a nullness qualification."
Error 43, Line 13, Col 13, Line 13, Col 31, "A generic construct requires that the type 'DateTime<s>' have reference semantics, but it does not, i.e. it is a struct"
]

[<FSharp.Test.FactForNETCOREAPPAttribute>]
let ``Nullness flow and not-null constraints work with MeasureAnnotatedAbbreviation over string`` () =
FSharp """module MyLibrary

[<MeasureAnnotatedAbbreviation>]
type string<[<Measure>] 'm> = string

[<Measure>] type tag
type S = string<tag>

let onlyNotNull (s: S) = ()

let widen (x: S) : S | null = x

let narrowBad (x: S | null) : S = x

let matched (x: S | null) =
match x with
| null -> ()
| nn -> onlyNotNull nn
"""
|> asLibrary
|> typeCheckWithStrictNullness
|> shouldFail
|> withDiagnostics [
Error 3261, Line 13, Col 35, Line 13, Col 36, "Nullness warning: A non-nullable 'S' was expected but this expression is nullable. Consider either changing the target to also be nullable, or use pattern matching to safely handle the null case of this expression."
]

[<FSharp.Test.FactForNETCOREAPPAttribute>]
let ``MeasureAnnotatedAbbreviation satisfies not-struct constraint`` () =
FSharp """module MyLibrary

[<MeasureAnnotatedAbbreviation>]
type string<[<Measure>] 'm> = string

[<Measure>] type tag
type S = string<tag>

let needsRef<'T when 'T : not struct> (x: 'T) = ()
let callIt (s: S) = needsRef s
let callNullable (s: S | null) = needsRef s
"""
|> asLibrary
|> typeCheckWithStrictNullness
|> shouldSucceed

[<FSharp.Test.FactForNETCOREAPPAttribute>]
let ``MeasureAnnotatedAbbreviation over obj, interface, and chained abbreviation allows nullable`` () =
FSharp """module MyLibrary

[<MeasureAnnotatedAbbreviation>]
type obj<[<Measure>] 'm> = obj

[<MeasureAnnotatedAbbreviation>]
type IDisposable<[<Measure>] 'm> = System.IDisposable

[<MeasureAnnotatedAbbreviation>]
type string<[<Measure>] 'm> = string

type chainedString<[<Measure>] 'm> = string<'m>

[<Measure>] type tag

let a : obj<tag> | null = null
let b : IDisposable<tag> | null = null
let c : chainedString<tag> | null = null
"""
|> asLibrary
|> typeCheckWithStrictNullness
|> shouldSucceed

[<FSharp.Test.FactForNETCOREAPPAttribute>]
let ``ToString on reference type still returns nullable string`` () =
FSharp """module MyLibrary
Expand Down
Loading