diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md index b5a9dff25f0..66c2c26926c 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md @@ -9,6 +9,7 @@ * Fix concurrency issue in `ILPreTypeDefImpl` ([PR #17812](https://github.com/dotnet/fsharp/pull/17812)) * Fix nullness inference for member val and other OO scenarios ([PR #17845](https://github.com/dotnet/fsharp/pull/17845)) * Fix internal error when analyzing incomplete inherit member ([PR #17905](https://github.com/dotnet/fsharp/pull/17905)) +* Add warning when downcasting from nullable type to non-nullable ([PR #17965](https://github.com/dotnet/fsharp/pull/17965)) * Fix missing nullness warning in case of method resolution multiple candidates ([PR #17917](https://github.com/dotnet/fsharp/pull/17918)) * Fix failure to use bound values in `when` clauses of `try-with` in `seq` expressions ([# 17990](https://github.com/dotnet/fsharp/pull/17990)) diff --git a/src/Compiler/AbstractIL/il.fs b/src/Compiler/AbstractIL/il.fs index 82faea51c5e..0017bdc651d 100644 --- a/src/Compiler/AbstractIL/il.fs +++ b/src/Compiler/AbstractIL/il.fs @@ -481,11 +481,11 @@ type ILAssemblyRef(data) = override x.GetHashCode() = uniqueStamp override x.Equals yobj = - ((yobj :?> ILAssemblyRef).UniqueStamp = uniqueStamp) + ((!!yobj :?> ILAssemblyRef).UniqueStamp = uniqueStamp) interface IComparable with override x.CompareTo yobj = - compare (yobj :?> ILAssemblyRef).UniqueStamp uniqueStamp + compare (!!yobj :?> ILAssemblyRef).UniqueStamp uniqueStamp static member Create(name, hash, publicKey, retargetable, version, locale) = ILAssemblyRef @@ -750,7 +750,7 @@ type ILTypeRef = override x.GetHashCode() = x.hashCode override x.Equals yobj = - let y = (yobj :?> ILTypeRef) + let y = (!!yobj :?> ILTypeRef) (x.ApproxId = y.ApproxId) && (x.Scope = y.Scope) @@ -793,7 +793,7 @@ type ILTypeRef = interface IComparable with override x.CompareTo yobj = - let y = (yobj :?> ILTypeRef) + let y = (!!yobj :?> ILTypeRef) let c = compare x.ApproxId y.ApproxId if c <> 0 then diff --git a/src/Compiler/AbstractIL/ilreflect.fs b/src/Compiler/AbstractIL/ilreflect.fs index 5a52ddc017a..c6c5e5ab992 100644 --- a/src/Compiler/AbstractIL/ilreflect.fs +++ b/src/Compiler/AbstractIL/ilreflect.fs @@ -1834,7 +1834,7 @@ let rec buildMethodPass2 cenv tref (typB: TypeBuilder) emEnv (mdef: ILMethodDef) let methB = System.Diagnostics.Debug.Assert(not (isNull definePInvokeMethod), "Runtime does not have DefinePInvokeMethod") // Absolutely can't happen - (!!definePInvokeMethod) + !!(!!definePInvokeMethod) .Invoke( typB, [| diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index 6d59c68c7e8..8be7ec551ea 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -2681,8 +2681,7 @@ and SolveTypeUseNotSupportsNull (csenv: ConstraintSolverEnv) ndeep m2 trace ty = do! WarnD (ConstraintSolverNullnessWarning(FSComp.SR.csTypeHasNullAsTrueValue(NicePrint.minimalStringOfType denv ty), m, m2)) elif TypeNullIsExtraValueNew g m ty then if g.checkNullness then - let denv = { denv with showNullnessAnnotations = Some true } - do! WarnD (ConstraintSolverNullnessWarning(FSComp.SR.csTypeHasNullAsExtraValue(NicePrint.minimalStringOfType denv ty), m, m2)) + do! WarnD (ConstraintSolverNullnessWarning(FSComp.SR.csTypeHasNullAsExtraValue(NicePrint.minimalStringOfTypeWithNullness denv ty), m, m2)) else match tryDestTyparTy g ty with | ValueSome tp -> @@ -2709,8 +2708,7 @@ and SolveNullnessNotSupportsNull (csenv: ConstraintSolverEnv) ndeep m2 (trace: O | NullnessInfo.WithoutNull -> () | NullnessInfo.WithNull -> if g.checkNullness && TypeNullIsExtraValueNew g m ty then - let denv = { denv with showNullnessAnnotations = Some true } - return! WarnD(ConstraintSolverNullnessWarning(FSComp.SR.csTypeHasNullAsExtraValue(NicePrint.minimalStringOfType denv ty), m, m2)) + return! WarnD(ConstraintSolverNullnessWarning(FSComp.SR.csTypeHasNullAsExtraValue(NicePrint.minimalStringOfTypeWithNullness denv ty), m, m2)) } and SolveTypeCanCarryNullness (csenv: ConstraintSolverEnv) ty nullness = diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index c6b1372843d..b52b4b2ad2c 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -2984,11 +2984,20 @@ let TcRuntimeTypeTest isCast isOperator (cenv: cenv) denv m tgtTy srcTy = else error(Error(FSComp.SR.tcTypeTestErased(NicePrint.minimalStringOfType denv tgtTy, NicePrint.minimalStringOfType denv (stripTyEqnsWrtErasure EraseAll g tgtTy)), m)) else - for ety in getErasedTypes g tgtTy true do + let checkTrgtNullness = + match (srcTy,g),(tgtTy,g) with + | (NullableRefType|NullTrueValue|NullableTypar), WithoutNullRefType when g.checkNullness && isCast -> + let srcNice = NicePrint.minimalStringOfTypeWithNullness denv srcTy + let tgtNice = NicePrint.minimalStringOfTypeWithNullness denv tgtTy + warning(Error(FSComp.SR.tcDowncastFromNullableToWithoutNull(srcNice,tgtNice,tgtNice), m)) + false + | (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 + | _ -> true + for ety in getErasedTypes g tgtTy checkTrgtNullness do if isMeasureTy g ety then warning(Error(FSComp.SR.tcTypeTestLosesMeasures(NicePrint.minimalStringOfType denv ety), m)) else - warning(Error(FSComp.SR.tcTypeTestLossy(NicePrint.minimalStringOfType denv ety, NicePrint.minimalStringOfType denv (stripTyEqnsWrtErasure EraseAll g ety)), m)) + warning(Error(FSComp.SR.tcTypeTestLossy(NicePrint.minimalStringOfTypeWithNullness denv ety, NicePrint.minimalStringOfType denv (stripTyEqnsWrtErasure EraseAll g ety)), m)) /// Checks, warnings and constraint assertions for upcasts let TcStaticUpcast (cenv: cenv) denv m tgtTy srcTy = @@ -6118,7 +6127,10 @@ and TcExprDowncast (cenv: cenv) overallTy env tpenv (synExpr, synInnerExpr, m) = // TcRuntimeTypeTest ensures tgtTy is a nominal type. Hence we can insert a check here // based on the nullness semantics of the nominal type. - let expr = mkCallUnbox g m tgtTy innerExpr + let expr = + match (tgtTy,g) with + | NullTrueValue | NullableRefType | NullableTypar when g.checkNullness -> mkCallUnboxFast g m tgtTy innerExpr + | _ -> mkCallUnbox g m tgtTy innerExpr expr, tpenv and TcExprLazy (cenv: cenv) overallTy env tpenv (synInnerExpr, m) = diff --git a/src/Compiler/Checking/MethodCalls.fs b/src/Compiler/Checking/MethodCalls.fs index ac4d92141d8..3343f7dbac3 100644 --- a/src/Compiler/Checking/MethodCalls.fs +++ b/src/Compiler/Checking/MethodCalls.fs @@ -1771,7 +1771,7 @@ module ProvidedMethodCalls = | _ when typeEquiv g normTy g.float32_ty -> Const.Single(v :?> float32) | _ when typeEquiv g normTy g.float_ty -> Const.Double(v :?> float) | _ when typeEquiv g normTy g.char_ty -> Const.Char(v :?> char) - | _ when typeEquiv g normTy g.string_ty -> Const.String(v :?> string) + | _ when typeEquiv g normTy g.string_ty -> Const.String(!!v :?> string) | _ when typeEquiv g normTy g.decimal_ty -> Const.Decimal(v :?> decimal) | _ when typeEquiv g normTy g.unit_ty -> Const.Unit | _ -> fail() diff --git a/src/Compiler/Checking/NicePrint.fs b/src/Compiler/Checking/NicePrint.fs index 62262b54635..a01802b1d7c 100644 --- a/src/Compiler/Checking/NicePrint.fs +++ b/src/Compiler/Checking/NicePrint.fs @@ -2954,3 +2954,6 @@ let minimalStringOfType denv ty = let denv = suppressNullnessAnnotations denv let denvMin = { denv with showInferenceTyparAnnotations=false; showStaticallyResolvedTyparAnnotations=false } showL (PrintTypes.layoutTypeWithInfoAndPrec denvMin SimplifyTypes.typeSimplificationInfo0 2 ty) + +let minimalStringOfTypeWithNullness denv ty = + minimalStringOfType {denv with showNullnessAnnotations = Some true} ty diff --git a/src/Compiler/Checking/NicePrint.fsi b/src/Compiler/Checking/NicePrint.fsi index 84dabb2c95b..8b90e66ce92 100644 --- a/src/Compiler/Checking/NicePrint.fsi +++ b/src/Compiler/Checking/NicePrint.fsi @@ -173,3 +173,5 @@ val minimalStringsOfTwoValues: denv: DisplayEnv -> infoReader: InfoReader -> vref1: ValRef -> vref2: ValRef -> string * string val minimalStringOfType: denv: DisplayEnv -> ty: TType -> string + +val minimalStringOfTypeWithNullness: denv: DisplayEnv -> ty: TType -> string diff --git a/src/Compiler/DependencyManager/AssemblyResolveHandler.fs b/src/Compiler/DependencyManager/AssemblyResolveHandler.fs index a511f39bdb2..0c87130608e 100644 --- a/src/Compiler/DependencyManager/AssemblyResolveHandler.fs +++ b/src/Compiler/DependencyManager/AssemblyResolveHandler.fs @@ -41,7 +41,7 @@ type AssemblyResolveHandlerCoreclr(assemblyProbingPaths: AssemblyResolutionProbe member _.ResolveAssemblyNetStandard (ctxt: 'T) (assemblyName: AssemblyName) : Assembly = let loadAssembly path = - loadFromAssemblyPathMethod.Invoke(ctxt, [| path |]) :?> Assembly + !! loadFromAssemblyPathMethod.Invoke(ctxt, [| path |]) :?> Assembly let assemblyPaths = match assemblyProbingPaths with diff --git a/src/Compiler/DependencyManager/DependencyProvider.fs b/src/Compiler/DependencyManager/DependencyProvider.fs index a241880e620..cc1b47de2ea 100644 --- a/src/Compiler/DependencyManager/DependencyProvider.fs +++ b/src/Compiler/DependencyManager/DependencyProvider.fs @@ -168,7 +168,7 @@ type ReflectionDependencyManagerProvider let keyProperty (x: objnull) = x |> keyProperty.GetValue |> string let helpMessagesProperty (x: objnull) = - let toStringArray (o: objnull) = o :?> string[] + let toStringArray (o: objnull) = !!o :?> string[] match helpMessagesProperty with | Some helpMessagesProperty -> x |> helpMessagesProperty.GetValue |> toStringArray @@ -334,31 +334,31 @@ type ReflectionDependencyManagerProvider member _.StdOut = match getInstanceProperty (result.GetType()) "StdOut" with | None -> [||] - | Some p -> p.GetValue(result) :?> string[] + | Some p -> !! p.GetValue(result) :?> string[] /// The resolution error log (* process stderror *) member _.StdError = match getInstanceProperty (result.GetType()) "StdError" with | None -> [||] - | Some p -> p.GetValue(result) :?> string[] + | Some p -> !! p.GetValue(result) :?> string[] /// The resolution paths member _.Resolutions = match getInstanceProperty> (result.GetType()) "Resolutions" with | None -> Seq.empty - | Some p -> p.GetValue(result) :?> seq + | Some p -> !! p.GetValue(result) :?> seq /// The source code file paths member _.SourceFiles = match getInstanceProperty> (result.GetType()) "SourceFiles" with | None -> Seq.empty - | Some p -> p.GetValue(result) :?> seq + | Some p -> !! p.GetValue(result) :?> seq /// The roots to package directories member _.Roots = match getInstanceProperty> (result.GetType()) "Roots" with | None -> Seq.empty - | Some p -> p.GetValue(result) :?> seq + | Some p -> !! p.GetValue(result) :?> seq } static member MakeResultFromFields @@ -473,8 +473,8 @@ type ReflectionDependencyManagerProvider match tupleFields |> Array.length with | 3 -> tupleFields[0] :?> bool, - tupleFields[1] :?> string list |> List.toSeq, - tupleFields[2] :?> string list |> List.distinct |> List.toSeq + !!tupleFields[1] :?> string list |> List.toSeq, + !!tupleFields[2] :?> string list |> List.distinct |> List.toSeq | _ -> false, seqEmpty, seqEmpty ReflectionDependencyManagerProvider.MakeResultFromFields(success, [||], [||], Seq.empty, sourceFiles, packageRoots) diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 5faafe6aa25..b42500049d2 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1537,6 +1537,7 @@ tcPassingWithoutNullToNonNullAP,"You can remove this |Null|NonNull| pattern usag tcPassingWithoutNullToNonNullQuickAP,"You can remove this |NonNullQuick| pattern usage." tcPassingWithoutNullTononNullFunction,"You can remove this `nonNull` assertion." 3263,tcNullableToStringOverride,"With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function." +3264,tcDowncastFromNullableToWithoutNull,"Nullness warning: Downcasting from '%s' into '%s' can introduce unexpected null values. Cast to '%s|null' instead or handle the null before downcasting." 3268,csNullNotNullConstraintInconsistent,"The constraints 'null' and 'not null' are inconsistent" 3271,tcNullnessCheckingNotEnabled,"The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored." csTypeHasNullAsTrueValue,"The type '%s' uses 'null' as a representation value but a non-null type is expected" diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index 02dbb98e2df..b8170c14b8d 100644 --- a/src/Compiler/Interactive/fsi.fs +++ b/src/Compiler/Interactive/fsi.fs @@ -122,7 +122,7 @@ module internal Utilities = } else let specialized = typedefof>.MakeGenericType [| ty |] - Activator.CreateInstance(specialized) :?> IAnyToLayoutCall + !! Activator.CreateInstance(specialized) :?> IAnyToLayoutCall let callStaticMethod (ty: Type) name args = ty.InvokeMember( @@ -4762,7 +4762,7 @@ type FsiEvaluationSession let makeNestedException (userExn: #Exception) = // clone userExn -- make userExn the inner exception, to retain the stacktrace on raise let arguments = [| userExn.Message :> obj; userExn :> obj |] - Activator.CreateInstance(userExn.GetType(), arguments) :?> Exception + !! Activator.CreateInstance(userExn.GetType(), arguments) :?> Exception let commitResult res = match res with diff --git a/src/Compiler/TypedTree/TypeProviders.fs b/src/Compiler/TypedTree/TypeProviders.fs index 5c81312e135..be22209a27a 100644 --- a/src/Compiler/TypedTree/TypeProviders.fs +++ b/src/Compiler/TypedTree/TypeProviders.fs @@ -141,10 +141,10 @@ let CreateTypeProvider ( IsHostedExecution= isInteractive, SystemRuntimeAssemblyVersion = systemRuntimeAssemblyVersion) #endif - protect (fun () -> Activator.CreateInstance(typeProviderImplementationType, [| box e|]) :?> ITypeProvider ) + protect (fun () -> !!(Activator.CreateInstance(typeProviderImplementationType, [| box e|])) :?> ITypeProvider ) elif not(isNull(typeProviderImplementationType.GetConstructor [| |])) then - protect (fun () -> Activator.CreateInstance typeProviderImplementationType :?> ITypeProvider ) + protect (fun () -> !!(Activator.CreateInstance typeProviderImplementationType) :?> ITypeProvider ) else // No appropriate constructor found @@ -739,7 +739,7 @@ type ProvidedMethodBase (x: MethodBase, ctxt) = let paramsAsObj = try (!!meth).Invoke(provider, bindingFlags ||| BindingFlags.InvokeMethod, null, [| box x |], null) with err -> raise (StripException (StripException err)) - paramsAsObj :?> ParameterInfo[] + !!paramsAsObj :?> ParameterInfo[] staticParams |> ProvidedParameterInfo.CreateArrayNonNull ctxt diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 71f26dbf95b..aa9bbad7f8c 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9252,6 +9252,7 @@ let TypeNullNotLiked g m ty = && not (TypeNullIsTrueValue g ty) && not (TypeNullNever g ty) + let rec TypeHasDefaultValueAux isNew g m ty = let ty = stripTyEqnsAndMeasureEqns g ty (if isNew then TypeNullIsExtraValueNew g m ty else TypeNullIsExtraValue g m ty) @@ -9318,15 +9319,37 @@ let (|SpecialEquatableHeadType|_|) g ty = (|SpecialComparableHeadType|_|) g ty let (|SpecialNotEquatableHeadType|_|) g ty = if isFunTy g ty then ValueSome() else ValueNone +let (|TyparTy|NullableTypar|StructTy|NullTrueValue|NullableRefType|WithoutNullRefType|UnresolvedRefType|) (ty,g) = + let sty = ty |> stripTyEqns g + if isTyparTy g sty then + if (nullnessOfTy g sty).TryEvaluate() = ValueSome NullnessInfo.WithNull then + NullableTypar + else + TyparTy + elif isStructTy g sty then + StructTy + elif TypeNullIsTrueValue g sty then + NullTrueValue + else + match (nullnessOfTy g sty).TryEvaluate() with + | ValueSome NullnessInfo.WithNull -> NullableRefType + | ValueSome NullnessInfo.WithoutNull -> WithoutNullRefType + | _ -> UnresolvedRefType + // Can we use the fast helper for the 'LanguagePrimitives.IntrinsicFunctions.TypeTestGeneric'? let canUseTypeTestFast g ty = not (isTyparTy g ty) && not (TypeNullIsTrueValue g ty) // Can we use the fast helper for the 'LanguagePrimitives.IntrinsicFunctions.UnboxGeneric'? -let canUseUnboxFast g m ty = - not (isTyparTy g ty) && - not (TypeNullNotLiked g m ty) +let canUseUnboxFast (g:TcGlobals) m ty = + if g.checkNullness then + match (ty,g) with + | TyparTy | WithoutNullRefType | UnresolvedRefType -> false + | StructTy | NullTrueValue | NullableRefType | NullableTypar -> true + else + not (isTyparTy g ty) && + not (TypeNullNotLiked g m ty) //-------------------------------------------------------------------------- // Nullness tests and pokes diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index 85c2adaab91..4eafbf229dc 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -2608,6 +2608,9 @@ val (|SpecialEquatableHeadType|_|): TcGlobals -> TType -> TType list voption [] val (|SpecialNotEquatableHeadType|_|): TcGlobals -> TType -> unit voption +val (|TyparTy|NullableTypar|StructTy|NullTrueValue|NullableRefType|WithoutNullRefType|UnresolvedRefType|): + TType * TcGlobals -> Choice + /// Matches if the given expression is an application /// of the range or range-step operator on an integral type /// and returns the type, start, step, and finish if so. diff --git a/src/Compiler/Utilities/TaggedCollections.fs b/src/Compiler/Utilities/TaggedCollections.fs index 4676c2673a0..253b38a196d 100644 --- a/src/Compiler/Utilities/TaggedCollections.fs +++ b/src/Compiler/Utilities/TaggedCollections.fs @@ -667,8 +667,8 @@ type internal Set<'T, 'ComparerTag> when 'ComparerTag :> IComparer<'T>(comparer: interface System.IComparable with // Cast s2 to the exact same type as s1, see 4884. // It is not OK to cast s2 to seq<'T>, since different compares could permute the elements. - member s1.CompareTo(s2: obj) = - SetTree.compare s1.Comparer s1.Tree (s2 :?> Set<'T, 'ComparerTag>).Tree + member s1.CompareTo(s2: objnull) = + SetTree.compare s1.Comparer s1.Tree (!!s2 :?> Set<'T, 'ComparerTag>).Tree member this.ComputeHashCode() = let combineHash x y = (x <<< 1) + y + 631 @@ -1239,7 +1239,7 @@ type internal Map<'Key, 'T, 'ComparerTag> when 'ComparerTag :> IComparer<'Key>(c | _ -> false interface System.IComparable with - member m1.CompareTo(m2: obj) = + member m1.CompareTo(m2: objnull) = Seq.compareWith (fun (kvp1: KeyValuePair<_, _>) (kvp2: KeyValuePair<_, _>) -> let c = m1.Comparer.Compare(kvp1.Key, kvp2.Key) in @@ -1251,7 +1251,7 @@ type internal Map<'Key, 'T, 'ComparerTag> when 'ComparerTag :> IComparer<'Key>(c // Cast m2 to the exact same type as m1, see 4884. // It is not OK to cast m2 to seq>, since different compares could permute the KVPs. m1 - (m2 :?> Map<'Key, 'T, 'ComparerTag>) + (!!m2 :?> Map<'Key, 'T, 'ComparerTag>) member this.ComputeHashCode() = let combineHash x y = (x <<< 1) + y + 631 diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index ec895df3a9b..c74702e5ddc 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -1342,6 +1342,11 @@ Zkrácená syntaxe lambda je podporována pouze pro atomické výrazy, jako je metoda, vlastnost, pole nebo indexer v implicitní argumentu _. Příklad: let f = _. Length'. + + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + + An empty body may only be used if the computation expression builder defines a 'Zero' method. An empty body may only be used if the computation expression builder defines a 'Zero' method. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 6952d64f446..3e0443ea3ba 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -1342,6 +1342,11 @@ Die Lambdasyntax der Kurzform wird nur für atomische Ausdrücke wie Methode, Eigenschaft, Feld oder Indexer für das implizite Argument "_" unterstützt. Beispiel: "let f = _. Länge". + + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + + An empty body may only be used if the computation expression builder defines a 'Zero' method. An empty body may only be used if the computation expression builder defines a 'Zero' method. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 43033ad72ce..ecadc24a021 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -1342,6 +1342,11 @@ La sintaxis lambda abreviada solo se admite para expresiones atómicas, como el método, la propiedad, el campo o el indexador en el argumento '_' implícito. Por ejemplo: 'let f = _.Length'. + + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + + An empty body may only be used if the computation expression builder defines a 'Zero' method. An empty body may only be used if the computation expression builder defines a 'Zero' method. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index ad40ba90ca7..7a9769e0d22 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -1342,6 +1342,11 @@ La syntaxe lambda raccourcie est prise en charge uniquement pour les expressions atomiques, telles que la méthode, la propriété, le champ ou l’indexeur sur l’argument ’_’ implicite. Par exemple : « let f = _. Longueur ». + + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + + An empty body may only be used if the computation expression builder defines a 'Zero' method. An empty body may only be used if the computation expression builder defines a 'Zero' method. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index c9d758de8a9..1db94e74c44 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -1342,6 +1342,11 @@ La sintassi lambda a sintassi abbreviata è supportata solo per le espressioni atomiche, ad esempio metodo, proprietà, campo o indicizzatore nell'argomento '_' implicito. Ad esempio: 'let f = _. Lunghezza'. + + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + + An empty body may only be used if the computation expression builder defines a 'Zero' method. An empty body may only be used if the computation expression builder defines a 'Zero' method. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 54fdce3735a..67b3ad51f9a 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -1342,6 +1342,11 @@ 短縮ラムダ構文は、暗黙的な '_' 引数のメソッド、プロパティ、フィールド、インデクサーなどのアトミック式でのみサポートされています。例: 'let f = _.Length'。 + + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + + An empty body may only be used if the computation expression builder defines a 'Zero' method. An empty body may only be used if the computation expression builder defines a 'Zero' method. diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 9d78dfb24ef..d33ad95bff5 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -1342,6 +1342,11 @@ 줄임 람다 구문은 암시적 '_' 인수의 메서드, 속성, 필드 또는 인덱서와 같은 원자성 식에 대해서만 지원됩니다. 예: 'let f = _.Length'. + + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + + An empty body may only be used if the computation expression builder defines a 'Zero' method. An empty body may only be used if the computation expression builder defines a 'Zero' method. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 5bb68fd1d09..a992a59bb23 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -1342,6 +1342,11 @@ Składnia lambda skrótu jest obsługiwana tylko w przypadku wyrażeń niepodzielnych, takich jak metoda, właściwość, pole lub indeksator w dorozumianym argumencie „_”. Na przykład: „let f = _. Length”. + + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + + An empty body may only be used if the computation expression builder defines a 'Zero' method. An empty body may only be used if the computation expression builder defines a 'Zero' method. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 16af2317c79..d547f088b3e 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -1342,6 +1342,11 @@ A sintaxe lambda abreviada só tem suporte para expressões atômicas, como método, propriedade, campo ou indexador no argumento '_' implícito. Por exemplo: 'let f = _. Comprimento'. + + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + + An empty body may only be used if the computation expression builder defines a 'Zero' method. An empty body may only be used if the computation expression builder defines a 'Zero' method. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 1959ba35868..1da17d068ce 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -1342,6 +1342,11 @@ Сокращенный синтаксис лямбда-выражений поддерживается только для атомарных выражений, таких как метод, свойство, поле или индексатор подразумеваемого аргумента «_». Например: 'let f = _.Length'. + + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + + An empty body may only be used if the computation expression builder defines a 'Zero' method. An empty body may only be used if the computation expression builder defines a 'Zero' method. diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index a91b60277d5..fe29aebcff7 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -1342,6 +1342,11 @@ Toplu lambda söz dizimi yalnızca örtülü '_' bağımsız değişkeninde yöntem, özellik, alan veya dizin oluşturucu gibi atomik ifadeler için destekleniyor. Örnek: 'let f = _.Length'. + + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + + An empty body may only be used if the computation expression builder defines a 'Zero' method. An empty body may only be used if the computation expression builder defines a 'Zero' method. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index fd8a5fe423a..c1b2b3d4682 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -1342,6 +1342,11 @@ 仅原子表达式支持速记 lambda 语法,例如隐含的“_”参数上的方法、属性、字段或索引器。例如:“let f = _.Length”。 + + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + + An empty body may only be used if the computation expression builder defines a 'Zero' method. An empty body may only be used if the computation expression builder defines a 'Zero' method. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index d631812e7fd..7c6c76ff2f7 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -1342,6 +1342,11 @@ 只有不可部分完成運算式才支援速記 Lambda 語法,例如隱含 '_' 引數上的方法、屬性、欄位或索引子。例如: 'let f = _.Length'。 + + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + Nullness warning: Downcasting from '{0}' into '{1}' can introduce unexpected null values. Cast to '{2}|null' instead or handle the null before downcasting. + + An empty body may only be used if the computation expression builder defines a 'Zero' method. An empty body may only be used if the computation expression builder defines a 'Zero' method. diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs new file mode 100644 index 00000000000..ce89bede351 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs @@ -0,0 +1,15 @@ +module TestModule + +let objToString (o: obj) = o :?> string +let objnullToNullableString (o: objnull) = o :?> (string|null) +let objnullToOption (o:objnull) = o :?> Option +let objNullTOInt(o:objnull) = o :?> int +let castToA<'a>(o:objnull) = o :?> 'a + + +let isObjnullAString(o:objnull) = o :? string +let isObjNullOption(o:objnull) = o :? Option +let isOfType<'a>(o:objnull) = o :? 'a + +let castToNullableB<'b when 'b:not null and 'b:not struct>(a: objnull) = a :?> ('b|null) +let downcastIplicitGeneric (o:objnull) : (_|null) = downcast o \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.il.net472.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.il.net472.bsl new file mode 100644 index 00000000000..68f2e777fe6 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.il.net472.bsl @@ -0,0 +1,239 @@ + + + + + +.assembly extern runtime { } +.assembly extern FSharp.Core { } +.assembly assembly +{ + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module assembly.dll + +.imagebase {value} +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 +.corflags 0x00000001 + + + + + +.class public abstract auto ansi sealed TestModule + extends [runtime]System.Object +{ + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .method public static string objToString(object o) cil managed + { + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call !!0 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::UnboxGeneric(object) + IL_0008: ret + } + + .method public static string objnullToNullableString(object o) cil managed + { + .param [0] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any [runtime]System.String + IL_0006: ret + } + + .method public static class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1 objnullToOption(object o) cil managed + { + .param [0] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = ( 01 00 02 00 00 00 02 01 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1 + IL_0006: ret + } + + .method public static int32 objNullTOInt(object o) cil managed + { + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any [runtime]System.Int32 + IL_0006: ret + } + + .method public static !!a castToA(object o) cil managed + { + .param type a + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call !!0 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::UnboxGeneric(object) + IL_0008: ret + } + + .method public static bool isObjnullAString(object o) cil managed + { + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 3 + .locals init (object V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: isinst [runtime]System.String + IL_0008: ldnull + IL_0009: cgt.un + IL_000b: ret + } + + .method public static bool isObjNullOption(object o) cil managed + { + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::TypeTestGeneric>(object) + IL_0008: ret + } + + .method public static bool isOfType(object o) cil managed + { + .param type a + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::TypeTestGeneric(object) + IL_0008: ret + } + + .method public static !!b castToNullableB(object a) cil managed + { + .param type b + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .param [0] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any !!b + IL_0006: ret + } + + .method public static !!a downcastIplicitGeneric(object o) cil managed + { + .param type a + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .param [0] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any !!a + IL_0006: ret + } + +} + +.class private abstract auto ansi sealed ''.$TestModule + extends [runtime]System.Object +{ +} + +.class private auto ansi beforefieldinit System.Runtime.CompilerServices.NullableAttribute + extends [runtime]System.Attribute +{ + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .field public uint8[] NullableFlags + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + .method public specialname rtspecialname instance void .ctor(uint8 scalarByteValue) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [runtime]System.Attribute::.ctor() + IL_0006: ldarg.0 + IL_0007: ldc.i4.1 + IL_0008: newarr [runtime]System.Byte + IL_000d: dup + IL_000e: ldc.i4.0 + IL_000f: ldarg.1 + IL_0010: stelem.i1 + IL_0011: stfld uint8[] System.Runtime.CompilerServices.NullableAttribute::NullableFlags + IL_0016: ret + } + + .method public specialname rtspecialname instance void .ctor(uint8[] NullableFlags) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [runtime]System.Attribute::.ctor() + IL_0006: ldarg.0 + IL_0007: ldarg.1 + IL_0008: stfld uint8[] System.Runtime.CompilerServices.NullableAttribute::NullableFlags + IL_000d: ret + } + +} + +.class private auto ansi beforefieldinit System.Runtime.CompilerServices.NullableContextAttribute + extends [runtime]System.Attribute +{ + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .field public uint8 Flag + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + .method public specialname rtspecialname instance void .ctor(uint8 Flag) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [runtime]System.Attribute::.ctor() + IL_0006: ldarg.0 + IL_0007: ldarg.1 + IL_0008: stfld uint8 System.Runtime.CompilerServices.NullableContextAttribute::Flag + IL_000d: ret + } + +} + + + + + + diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.il.netcore.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.il.netcore.bsl new file mode 100644 index 00000000000..5589e54be12 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.il.netcore.bsl @@ -0,0 +1,174 @@ + + + + + +.assembly extern runtime { } +.assembly extern FSharp.Core { } +.assembly assembly +{ + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module assembly.dll + +.imagebase {value} +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 +.corflags 0x00000001 + + + + + +.class public abstract auto ansi sealed TestModule + extends [runtime]System.Object +{ + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .custom instance void [runtime]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .method public static string objToString(object o) cil managed + { + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call !!0 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::UnboxGeneric(object) + IL_0008: ret + } + + .method public static string objnullToNullableString(object o) cil managed + { + .param [0] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any [runtime]System.String + IL_0006: ret + } + + .method public static class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1 objnullToOption(object o) cil managed + { + .param [0] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = ( 01 00 02 00 00 00 02 01 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1 + IL_0006: ret + } + + .method public static int32 objNullTOInt(object o) cil managed + { + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any [runtime]System.Int32 + IL_0006: ret + } + + .method public static !!a castToA(object o) cil managed + { + .param type a + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call !!0 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::UnboxGeneric(object) + IL_0008: ret + } + + .method public static bool isObjnullAString(object o) cil managed + { + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 3 + .locals init (object V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: isinst [runtime]System.String + IL_0008: ldnull + IL_0009: cgt.un + IL_000b: ret + } + + .method public static bool isObjNullOption(object o) cil managed + { + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::TypeTestGeneric>(object) + IL_0008: ret + } + + .method public static bool isOfType(object o) cil managed + { + .param type a + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::TypeTestGeneric(object) + IL_0008: ret + } + + .method public static !!b castToNullableB(object a) cil managed + { + .param type b + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .param [0] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any !!b + IL_0006: ret + } + + .method public static !!a downcastIplicitGeneric(object o) cil managed + { + .param type a + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .param [0] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any !!a + IL_0006: ret + } + +} + +.class private abstract auto ansi sealed ''.$TestModule + extends [runtime]System.Object +{ +} + + + + + + diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.opt.il.net472.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.opt.il.net472.bsl new file mode 100644 index 00000000000..aea1ebdf966 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.opt.il.net472.bsl @@ -0,0 +1,236 @@ + + + + + +.assembly extern runtime { } +.assembly extern FSharp.Core { } +.assembly assembly +{ + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module assembly.dll + +.imagebase {value} +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 +.corflags 0x00000001 + + + + + +.class public abstract auto ansi sealed TestModule + extends [runtime]System.Object +{ + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .method public static string objToString(object o) cil managed + { + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call !!0 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::UnboxGeneric(object) + IL_0008: ret + } + + .method public static string objnullToNullableString(object o) cil managed + { + .param [0] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any [runtime]System.String + IL_0006: ret + } + + .method public static class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1 objnullToOption(object o) cil managed + { + .param [0] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = ( 01 00 02 00 00 00 02 01 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1 + IL_0006: ret + } + + .method public static int32 objNullTOInt(object o) cil managed + { + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any [runtime]System.Int32 + IL_0006: ret + } + + .method public static !!a castToA(object o) cil managed + { + .param type a + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call !!0 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::UnboxGeneric(object) + IL_0008: ret + } + + .method public static bool isObjnullAString(object o) cil managed + { + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: isinst [runtime]System.String + IL_0006: ldnull + IL_0007: cgt.un + IL_0009: ret + } + + .method public static bool isObjNullOption(object o) cil managed + { + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::TypeTestGeneric>(object) + IL_0008: ret + } + + .method public static bool isOfType(object o) cil managed + { + .param type a + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::TypeTestGeneric(object) + IL_0008: ret + } + + .method public static !!b castToNullableB(object a) cil managed + { + .param type b + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .param [0] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any !!b + IL_0006: ret + } + + .method public static !!a downcastIplicitGeneric(object o) cil managed + { + .param type a + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .param [0] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any !!a + IL_0006: ret + } + +} + +.class private abstract auto ansi sealed ''.$TestModule + extends [runtime]System.Object +{ +} + +.class private auto ansi beforefieldinit System.Runtime.CompilerServices.NullableAttribute + extends [runtime]System.Attribute +{ + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .field public uint8[] NullableFlags + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + .method public specialname rtspecialname instance void .ctor(uint8 scalarByteValue) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [runtime]System.Attribute::.ctor() + IL_0006: ldarg.0 + IL_0007: ldc.i4.1 + IL_0008: newarr [runtime]System.Byte + IL_000d: dup + IL_000e: ldc.i4.0 + IL_000f: ldarg.1 + IL_0010: stelem.i1 + IL_0011: stfld uint8[] System.Runtime.CompilerServices.NullableAttribute::NullableFlags + IL_0016: ret + } + + .method public specialname rtspecialname instance void .ctor(uint8[] NullableFlags) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [runtime]System.Attribute::.ctor() + IL_0006: ldarg.0 + IL_0007: ldarg.1 + IL_0008: stfld uint8[] System.Runtime.CompilerServices.NullableAttribute::NullableFlags + IL_000d: ret + } + +} + +.class private auto ansi beforefieldinit System.Runtime.CompilerServices.NullableContextAttribute + extends [runtime]System.Attribute +{ + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .field public uint8 Flag + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + .method public specialname rtspecialname instance void .ctor(uint8 Flag) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [runtime]System.Attribute::.ctor() + IL_0006: ldarg.0 + IL_0007: ldarg.1 + IL_0008: stfld uint8 System.Runtime.CompilerServices.NullableContextAttribute::Flag + IL_000d: ret + } + +} + + + + + + diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.opt.il.netcore.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.opt.il.netcore.bsl new file mode 100644 index 00000000000..a05e6fb269f --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.opt.il.netcore.bsl @@ -0,0 +1,171 @@ + + + + + +.assembly extern runtime { } +.assembly extern FSharp.Core { } +.assembly assembly +{ + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module assembly.dll + +.imagebase {value} +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 +.corflags 0x00000001 + + + + + +.class public abstract auto ansi sealed TestModule + extends [runtime]System.Object +{ + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .custom instance void [runtime]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .method public static string objToString(object o) cil managed + { + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call !!0 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::UnboxGeneric(object) + IL_0008: ret + } + + .method public static string objnullToNullableString(object o) cil managed + { + .param [0] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any [runtime]System.String + IL_0006: ret + } + + .method public static class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1 objnullToOption(object o) cil managed + { + .param [0] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = ( 01 00 02 00 00 00 02 01 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1 + IL_0006: ret + } + + .method public static int32 objNullTOInt(object o) cil managed + { + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any [runtime]System.Int32 + IL_0006: ret + } + + .method public static !!a castToA(object o) cil managed + { + .param type a + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call !!0 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::UnboxGeneric(object) + IL_0008: ret + } + + .method public static bool isObjnullAString(object o) cil managed + { + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: isinst [runtime]System.String + IL_0006: ldnull + IL_0007: cgt.un + IL_0009: ret + } + + .method public static bool isObjNullOption(object o) cil managed + { + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::TypeTestGeneric>(object) + IL_0008: ret + } + + .method public static bool isOfType(object o) cil managed + { + .param type a + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::TypeTestGeneric(object) + IL_0008: ret + } + + .method public static !!b castToNullableB(object a) cil managed + { + .param type b + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .param [0] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any !!b + IL_0006: ret + } + + .method public static !!a downcastIplicitGeneric(object o) cil managed + { + .param type a + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .param [0] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any !!a + IL_0006: ret + } + +} + +.class private abstract auto ansi sealed ''.$TestModule + extends [runtime]System.Object +{ +} + + + + + + diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fsopt.il.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fsopt.il.bsl new file mode 100644 index 00000000000..a05e6fb269f --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fsopt.il.bsl @@ -0,0 +1,171 @@ + + + + + +.assembly extern runtime { } +.assembly extern FSharp.Core { } +.assembly assembly +{ + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module assembly.dll + +.imagebase {value} +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 +.corflags 0x00000001 + + + + + +.class public abstract auto ansi sealed TestModule + extends [runtime]System.Object +{ + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .custom instance void [runtime]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .method public static string objToString(object o) cil managed + { + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call !!0 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::UnboxGeneric(object) + IL_0008: ret + } + + .method public static string objnullToNullableString(object o) cil managed + { + .param [0] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any [runtime]System.String + IL_0006: ret + } + + .method public static class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1 objnullToOption(object o) cil managed + { + .param [0] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = ( 01 00 02 00 00 00 02 01 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1 + IL_0006: ret + } + + .method public static int32 objNullTOInt(object o) cil managed + { + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any [runtime]System.Int32 + IL_0006: ret + } + + .method public static !!a castToA(object o) cil managed + { + .param type a + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call !!0 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::UnboxGeneric(object) + IL_0008: ret + } + + .method public static bool isObjnullAString(object o) cil managed + { + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: isinst [runtime]System.String + IL_0006: ldnull + IL_0007: cgt.un + IL_0009: ret + } + + .method public static bool isObjNullOption(object o) cil managed + { + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::TypeTestGeneric>(object) + IL_0008: ret + } + + .method public static bool isOfType(object o) cil managed + { + .param type a + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: tail. + IL_0003: call bool [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::TypeTestGeneric(object) + IL_0008: ret + } + + .method public static !!b castToNullableB(object a) cil managed + { + .param type b + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .param [0] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any !!b + IL_0006: ret + } + + .method public static !!a downcastIplicitGeneric(object o) cil managed + { + .param type a + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .param [0] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + .param [1] + .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: unbox.any !!a + IL_0006: ret + } + +} + +.class private abstract auto ansi sealed ''.$TestModule + extends [runtime]System.Object +{ +} + + + + + + diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullnessMetadata.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullnessMetadata.fs index f0ebdfa0c34..8778ce5d09d 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullnessMetadata.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullnessMetadata.fs @@ -95,6 +95,18 @@ let ``GenericCode`` compilation = |> withNoWarn 52 |> verifyCompilation DoNotOptimize +[] +let ``Downcasting and typetests`` compilation = + compilation + |> withNoWarn 52 + |> verifyCompilation DoNotOptimize + +[] +let ``Downcasting and typetests optimized`` compilation = + compilation + |> withNoWarn 52 + |> verifyCompilation Optimize + module Interop = open System.IO diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs index 2933d00e438..63b208c42e0 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs @@ -36,6 +36,35 @@ let parsedDate3 = DateTime.Parse(null) Error 3261, Line 4, Col 33, Line 4, Col 50, "Nullness warning: The types 'string' and 'string | null' do not have compatible nullability." Error 3261, Line 5, Col 19, Line 5, Col 39, "Nullness warning: The type 'string' does not support 'null'."] +[] +let ``Downcasts and typetests with nullables``() = + FSharp """module MyLib +type AB = A | B + +let warnOnCastFromNull (o: objnull) = o :?> AB +let warnOnCastFromNonNullToNull(o:obj) = o :?> (AB | null) +let warnOnTypeTestNullable(o:objnull) = o :? (AB|null) +let warnOnTypeTestRepeatedNestedNullable(o:obj) = o :? (list<((AB | null) array | null) > |null) + +let doNotWarnOnCastFromNullToOption(o:objnull) = o :?> Option +let doNotWarnOnTypeTestOption(o:objnull) = o :? Option +let doNotWarnOnGenericCastToNullableGeneric<'b when 'b:not null and 'b:not struct>(a: objnull) = a :?> ('b|null) +let doNotWarnOnDownCastNestedNullable(o:obj) = o :? list +let doNotWarnOnDowncastRepeatedNestedNullable(o:objnull) = o :? list<((AB | null) array | null) > + + """ + |> asLibrary + |> typeCheckWithStrictNullness + |> shouldFail + |> withDiagnostics + [ Error 3264, Line 4, Col 39, Line 4, Col 47, "Nullness warning: Downcasting from 'objnull' into 'AB' can introduce unexpected null values. Cast to 'AB|null' instead or handle the null before downcasting." + Error 3261, Line 5, Col 42, Line 5, Col 59, "Nullness warning: The types 'obj' and 'AB | null' do not have compatible nullability." + Error 3060, Line 5, Col 42, Line 5, Col 59, "This type test or downcast will erase the provided type 'AB | null' to the type 'AB'" + Error 3060, Line 6, Col 41, Line 6, Col 55, "This type test or downcast will erase the provided type 'AB | null' to the type 'AB'" + Error 3261, Line 7, Col 51, Line 7, Col 97, "Nullness warning: The types 'obj' and 'AB | null array | null list | null' do not have compatible nullability." + Error 3060, Line 7, Col 51, Line 7, Col 97, "This type test or downcast will erase the provided type 'List | null' to the type 'List'"] + + [] let ``Can convert generic value to objnull arg`` () = FSharp """module TestLib