Skip to content

Commit f44e2b2

Browse files
authored
Nullness - downcasting and typetests should understand nullness information (#17965)
1 parent 8a4c053 commit f44e2b2

37 files changed

+1189
-34
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* Fix concurrency issue in `ILPreTypeDefImpl` ([PR #17812](https://github.com/dotnet/fsharp/pull/17812))
1010
* Fix nullness inference for member val and other OO scenarios ([PR #17845](https://github.com/dotnet/fsharp/pull/17845))
1111
* Fix internal error when analyzing incomplete inherit member ([PR #17905](https://github.com/dotnet/fsharp/pull/17905))
12+
* Add warning when downcasting from nullable type to non-nullable ([PR #17965](https://github.com/dotnet/fsharp/pull/17965))
1213
* Fix missing nullness warning in case of method resolution multiple candidates ([PR #17917](https://github.com/dotnet/fsharp/pull/17918))
1314
* Fix failure to use bound values in `when` clauses of `try-with` in `seq` expressions ([# 17990](https://github.com/dotnet/fsharp/pull/17990))
1415

src/Compiler/AbstractIL/il.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -481,11 +481,11 @@ type ILAssemblyRef(data) =
481481
override x.GetHashCode() = uniqueStamp
482482

483483
override x.Equals yobj =
484-
((yobj :?> ILAssemblyRef).UniqueStamp = uniqueStamp)
484+
((!!yobj :?> ILAssemblyRef).UniqueStamp = uniqueStamp)
485485

486486
interface IComparable with
487487
override x.CompareTo yobj =
488-
compare (yobj :?> ILAssemblyRef).UniqueStamp uniqueStamp
488+
compare (!!yobj :?> ILAssemblyRef).UniqueStamp uniqueStamp
489489

490490
static member Create(name, hash, publicKey, retargetable, version, locale) =
491491
ILAssemblyRef
@@ -750,7 +750,7 @@ type ILTypeRef =
750750
override x.GetHashCode() = x.hashCode
751751

752752
override x.Equals yobj =
753-
let y = (yobj :?> ILTypeRef)
753+
let y = (!!yobj :?> ILTypeRef)
754754

755755
(x.ApproxId = y.ApproxId)
756756
&& (x.Scope = y.Scope)
@@ -793,7 +793,7 @@ type ILTypeRef =
793793
interface IComparable with
794794

795795
override x.CompareTo yobj =
796-
let y = (yobj :?> ILTypeRef)
796+
let y = (!!yobj :?> ILTypeRef)
797797
let c = compare x.ApproxId y.ApproxId
798798

799799
if c <> 0 then

src/Compiler/AbstractIL/ilreflect.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1834,7 +1834,7 @@ let rec buildMethodPass2 cenv tref (typB: TypeBuilder) emEnv (mdef: ILMethodDef)
18341834
let methB =
18351835
System.Diagnostics.Debug.Assert(not (isNull definePInvokeMethod), "Runtime does not have DefinePInvokeMethod") // Absolutely can't happen
18361836

1837-
(!!definePInvokeMethod)
1837+
!!(!!definePInvokeMethod)
18381838
.Invoke(
18391839
typB,
18401840
[|

src/Compiler/Checking/ConstraintSolver.fs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2681,8 +2681,7 @@ and SolveTypeUseNotSupportsNull (csenv: ConstraintSolverEnv) ndeep m2 trace ty =
26812681
do! WarnD (ConstraintSolverNullnessWarning(FSComp.SR.csTypeHasNullAsTrueValue(NicePrint.minimalStringOfType denv ty), m, m2))
26822682
elif TypeNullIsExtraValueNew g m ty then
26832683
if g.checkNullness then
2684-
let denv = { denv with showNullnessAnnotations = Some true }
2685-
do! WarnD (ConstraintSolverNullnessWarning(FSComp.SR.csTypeHasNullAsExtraValue(NicePrint.minimalStringOfType denv ty), m, m2))
2684+
do! WarnD (ConstraintSolverNullnessWarning(FSComp.SR.csTypeHasNullAsExtraValue(NicePrint.minimalStringOfTypeWithNullness denv ty), m, m2))
26862685
else
26872686
match tryDestTyparTy g ty with
26882687
| ValueSome tp ->
@@ -2709,8 +2708,7 @@ and SolveNullnessNotSupportsNull (csenv: ConstraintSolverEnv) ndeep m2 (trace: O
27092708
| NullnessInfo.WithoutNull -> ()
27102709
| NullnessInfo.WithNull ->
27112710
if g.checkNullness && TypeNullIsExtraValueNew g m ty then
2712-
let denv = { denv with showNullnessAnnotations = Some true }
2713-
return! WarnD(ConstraintSolverNullnessWarning(FSComp.SR.csTypeHasNullAsExtraValue(NicePrint.minimalStringOfType denv ty), m, m2))
2711+
return! WarnD(ConstraintSolverNullnessWarning(FSComp.SR.csTypeHasNullAsExtraValue(NicePrint.minimalStringOfTypeWithNullness denv ty), m, m2))
27142712
}
27152713

27162714
and SolveTypeCanCarryNullness (csenv: ConstraintSolverEnv) ty nullness =

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2984,11 +2984,20 @@ let TcRuntimeTypeTest isCast isOperator (cenv: cenv) denv m tgtTy srcTy =
29842984
else
29852985
error(Error(FSComp.SR.tcTypeTestErased(NicePrint.minimalStringOfType denv tgtTy, NicePrint.minimalStringOfType denv (stripTyEqnsWrtErasure EraseAll g tgtTy)), m))
29862986
else
2987-
for ety in getErasedTypes g tgtTy true do
2987+
let checkTrgtNullness =
2988+
match (srcTy,g),(tgtTy,g) with
2989+
| (NullableRefType|NullTrueValue|NullableTypar), WithoutNullRefType when g.checkNullness && isCast ->
2990+
let srcNice = NicePrint.minimalStringOfTypeWithNullness denv srcTy
2991+
let tgtNice = NicePrint.minimalStringOfTypeWithNullness denv tgtTy
2992+
warning(Error(FSComp.SR.tcDowncastFromNullableToWithoutNull(srcNice,tgtNice,tgtNice), m))
2993+
false
2994+
| (NullableRefType|NullTrueValue|NullableTypar), (NullableRefType|NullTrueValue|NullableTypar) -> not isCast //a type test (unlike type cast) will never return true for null in the source, therefore adding |null to target does not help => keep the erasure warning
2995+
| _ -> true
2996+
for ety in getErasedTypes g tgtTy checkTrgtNullness do
29882997
if isMeasureTy g ety then
29892998
warning(Error(FSComp.SR.tcTypeTestLosesMeasures(NicePrint.minimalStringOfType denv ety), m))
29902999
else
2991-
warning(Error(FSComp.SR.tcTypeTestLossy(NicePrint.minimalStringOfType denv ety, NicePrint.minimalStringOfType denv (stripTyEqnsWrtErasure EraseAll g ety)), m))
3000+
warning(Error(FSComp.SR.tcTypeTestLossy(NicePrint.minimalStringOfTypeWithNullness denv ety, NicePrint.minimalStringOfType denv (stripTyEqnsWrtErasure EraseAll g ety)), m))
29923001

29933002
/// Checks, warnings and constraint assertions for upcasts
29943003
let TcStaticUpcast (cenv: cenv) denv m tgtTy srcTy =
@@ -6118,7 +6127,10 @@ and TcExprDowncast (cenv: cenv) overallTy env tpenv (synExpr, synInnerExpr, m) =
61186127

61196128
// TcRuntimeTypeTest ensures tgtTy is a nominal type. Hence we can insert a check here
61206129
// based on the nullness semantics of the nominal type.
6121-
let expr = mkCallUnbox g m tgtTy innerExpr
6130+
let expr =
6131+
match (tgtTy,g) with
6132+
| NullTrueValue | NullableRefType | NullableTypar when g.checkNullness -> mkCallUnboxFast g m tgtTy innerExpr
6133+
| _ -> mkCallUnbox g m tgtTy innerExpr
61226134
expr, tpenv
61236135

61246136
and TcExprLazy (cenv: cenv) overallTy env tpenv (synInnerExpr, m) =

src/Compiler/Checking/MethodCalls.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1771,7 +1771,7 @@ module ProvidedMethodCalls =
17711771
| _ when typeEquiv g normTy g.float32_ty -> Const.Single(v :?> float32)
17721772
| _ when typeEquiv g normTy g.float_ty -> Const.Double(v :?> float)
17731773
| _ when typeEquiv g normTy g.char_ty -> Const.Char(v :?> char)
1774-
| _ when typeEquiv g normTy g.string_ty -> Const.String(v :?> string)
1774+
| _ when typeEquiv g normTy g.string_ty -> Const.String(!!v :?> string)
17751775
| _ when typeEquiv g normTy g.decimal_ty -> Const.Decimal(v :?> decimal)
17761776
| _ when typeEquiv g normTy g.unit_ty -> Const.Unit
17771777
| _ -> fail()

src/Compiler/Checking/NicePrint.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2954,3 +2954,6 @@ let minimalStringOfType denv ty =
29542954
let denv = suppressNullnessAnnotations denv
29552955
let denvMin = { denv with showInferenceTyparAnnotations=false; showStaticallyResolvedTyparAnnotations=false }
29562956
showL (PrintTypes.layoutTypeWithInfoAndPrec denvMin SimplifyTypes.typeSimplificationInfo0 2 ty)
2957+
2958+
let minimalStringOfTypeWithNullness denv ty =
2959+
minimalStringOfType {denv with showNullnessAnnotations = Some true} ty

src/Compiler/Checking/NicePrint.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,5 @@ val minimalStringsOfTwoValues:
173173
denv: DisplayEnv -> infoReader: InfoReader -> vref1: ValRef -> vref2: ValRef -> string * string
174174

175175
val minimalStringOfType: denv: DisplayEnv -> ty: TType -> string
176+
177+
val minimalStringOfTypeWithNullness: denv: DisplayEnv -> ty: TType -> string

src/Compiler/DependencyManager/AssemblyResolveHandler.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type AssemblyResolveHandlerCoreclr(assemblyProbingPaths: AssemblyResolutionProbe
4141

4242
member _.ResolveAssemblyNetStandard (ctxt: 'T) (assemblyName: AssemblyName) : Assembly =
4343
let loadAssembly path =
44-
loadFromAssemblyPathMethod.Invoke(ctxt, [| path |]) :?> Assembly
44+
!! loadFromAssemblyPathMethod.Invoke(ctxt, [| path |]) :?> Assembly
4545

4646
let assemblyPaths =
4747
match assemblyProbingPaths with

src/Compiler/DependencyManager/DependencyProvider.fs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ type ReflectionDependencyManagerProvider
168168
let keyProperty (x: objnull) = x |> keyProperty.GetValue |> string
169169

170170
let helpMessagesProperty (x: objnull) =
171-
let toStringArray (o: objnull) = o :?> string[]
171+
let toStringArray (o: objnull) = !!o :?> string[]
172172

173173
match helpMessagesProperty with
174174
| Some helpMessagesProperty -> x |> helpMessagesProperty.GetValue |> toStringArray
@@ -334,31 +334,31 @@ type ReflectionDependencyManagerProvider
334334
member _.StdOut =
335335
match getInstanceProperty<string[]> (result.GetType()) "StdOut" with
336336
| None -> [||]
337-
| Some p -> p.GetValue(result) :?> string[]
337+
| Some p -> !! p.GetValue(result) :?> string[]
338338

339339
/// The resolution error log (* process stderror *)
340340
member _.StdError =
341341
match getInstanceProperty<string[]> (result.GetType()) "StdError" with
342342
| None -> [||]
343-
| Some p -> p.GetValue(result) :?> string[]
343+
| Some p -> !! p.GetValue(result) :?> string[]
344344

345345
/// The resolution paths
346346
member _.Resolutions =
347347
match getInstanceProperty<seq<string>> (result.GetType()) "Resolutions" with
348348
| None -> Seq.empty<string>
349-
| Some p -> p.GetValue(result) :?> seq<string>
349+
| Some p -> !! p.GetValue(result) :?> seq<string>
350350

351351
/// The source code file paths
352352
member _.SourceFiles =
353353
match getInstanceProperty<seq<string>> (result.GetType()) "SourceFiles" with
354354
| None -> Seq.empty<string>
355-
| Some p -> p.GetValue(result) :?> seq<string>
355+
| Some p -> !! p.GetValue(result) :?> seq<string>
356356

357357
/// The roots to package directories
358358
member _.Roots =
359359
match getInstanceProperty<seq<string>> (result.GetType()) "Roots" with
360360
| None -> Seq.empty<string>
361-
| Some p -> p.GetValue(result) :?> seq<string>
361+
| Some p -> !! p.GetValue(result) :?> seq<string>
362362
}
363363

364364
static member MakeResultFromFields
@@ -473,8 +473,8 @@ type ReflectionDependencyManagerProvider
473473
match tupleFields |> Array.length with
474474
| 3 ->
475475
tupleFields[0] :?> bool,
476-
tupleFields[1] :?> string list |> List.toSeq,
477-
tupleFields[2] :?> string list |> List.distinct |> List.toSeq
476+
!!tupleFields[1] :?> string list |> List.toSeq,
477+
!!tupleFields[2] :?> string list |> List.distinct |> List.toSeq
478478
| _ -> false, seqEmpty, seqEmpty
479479

480480
ReflectionDependencyManagerProvider.MakeResultFromFields(success, [||], [||], Seq.empty, sourceFiles, packageRoots)

0 commit comments

Comments
 (0)