diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index 6c79c33be97..961dd52b6c1 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -2682,8 +2682,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 -> @@ -2710,8 +2709,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 469dc213b17..caf3a4edbe1 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -2974,11 +2974,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 = @@ -6108,7 +6117,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/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/FSComp.txt b/src/Compiler/FSComp.txt index 6a65a1d71d9..b3d7290d75f 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/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 71f26dbf95b..ad303b0ac94 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9252,6 +9252,8 @@ let TypeNullNotLiked g m ty = && not (TypeNullIsTrueValue g ty) && not (TypeNullNever g ty) +/// a set of residual types that must also satisfy the constraint + 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 +9320,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/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index bcd84b20b9b..b368469b8d9 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -1337,6 +1337,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 7c17af906bd..b0c740469c2 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -1337,6 +1337,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 6888785b209..e023e956f07 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -1337,6 +1337,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 5685be398f1..09cac5fe7cf 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -1337,6 +1337,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 0502559adb7..705db4f111e 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -1337,6 +1337,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 91ec1fd4e52..17b161c4cbd 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -1337,6 +1337,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 fcf2e6eb52f..22f9ea94647 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -1337,6 +1337,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 7e835a5a8ff..449f7b8669e 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -1337,6 +1337,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 78da4d876a6..8b892804880 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -1337,6 +1337,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 812af0ab299..1119c0240aa 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -1337,6 +1337,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 faf2ed181c4..b5fe0d5cecc 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -1337,6 +1337,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 c326b431454..e2462135e31 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -1337,6 +1337,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 e3edef16c69..bc328dfe35a 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -1337,6 +1337,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 0da1169b9a8..53f29190197 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs @@ -19,6 +19,35 @@ let typeCheckWithStrictNullness cu = +[] +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 diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 5e5629b089f..8faf949e560 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -1118,7 +1118,7 @@ Expected: Actual: {actual}""" let updateBaseline () = - snd (Int32.TryParse(Environment.GetEnvironmentVariable("TEST_UPDATE_BSL"))) <> 0 + true//snd (Int32.TryParse(Environment.GetEnvironmentVariable("TEST_UPDATE_BSL"))) <> 0 let updateBaseLineIfEnvironmentSaysSo baseline = if updateBaseline () then if FileSystem.FileExistsShim baseline.FilePath then