From 38b4f71fcf4bbbfa5aceef964274bd9d85c1c9b4 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 30 Oct 2024 14:07:43 +0100 Subject: [PATCH 01/10] Fix error message when typecasting nullable type --- src/Compiler/Checking/ConstraintSolver.fs | 6 ++---- src/Compiler/Checking/Expressions/CheckExpressions.fs | 2 +- src/Compiler/Checking/NicePrint.fs | 3 +++ src/Compiler/Checking/NicePrint.fsi | 2 ++ 4 files changed, 8 insertions(+), 5 deletions(-) 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 9c14787d113..0bcb998549c 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -2977,7 +2977,7 @@ let TcRuntimeTypeTest isCast isOperator (cenv: cenv) denv m tgtTy srcTy = 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.minimalStringOfType denv ety, NicePrint.minimalStringOfTypeWithNullness denv (stripTyEqnsWrtErasure EraseAll g ety)), m)) /// Checks, warnings and constraint assertions for upcasts let TcStaticUpcast (cenv: cenv) denv m tgtTy srcTy = diff --git a/src/Compiler/Checking/NicePrint.fs b/src/Compiler/Checking/NicePrint.fs index 09e8708b894..ca43fbb1f23 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 From 3e97452401c459fd8698b91203f1bb596da952b0 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 1 Nov 2024 16:23:12 +0100 Subject: [PATCH 02/10] temp --- src/Compiler/TypedTree/TypedTreeOps.fs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 71f26dbf95b..13723725535 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9252,6 +9252,10 @@ 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 (|NullTrueValue|HasAllowsNullAttr|WithoutNullValueType|GenericTyparUnconstrained|GenericTyparConstrained|WithoutNullRefType|WithNullRefType|) g ty = failwith "" + let rec TypeHasDefaultValueAux isNew g m ty = let ty = stripTyEqnsAndMeasureEqns g ty (if isNew then TypeNullIsExtraValueNew g m ty else TypeNullIsExtraValue g m ty) From 7d9d1dcc5e47ee245c4af31a5a838c839b30cabc Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 5 Nov 2024 15:43:11 +0100 Subject: [PATCH 03/10] downcasts and typetests for nullables - revisit --- .../Checking/Expressions/CheckExpressions.fs | 16 +++++++++-- src/Compiler/FSComp.txt | 1 + src/Compiler/TypedTree/TypedTreeOps.fs | 27 +++++++++++++++---- src/Compiler/TypedTree/TypedTreeOps.fsi | 2 ++ src/Compiler/xlf/FSComp.txt.cs.xlf | 5 ++++ src/Compiler/xlf/FSComp.txt.de.xlf | 5 ++++ src/Compiler/xlf/FSComp.txt.es.xlf | 5 ++++ src/Compiler/xlf/FSComp.txt.fr.xlf | 5 ++++ src/Compiler/xlf/FSComp.txt.it.xlf | 5 ++++ src/Compiler/xlf/FSComp.txt.ja.xlf | 5 ++++ src/Compiler/xlf/FSComp.txt.ko.xlf | 5 ++++ src/Compiler/xlf/FSComp.txt.pl.xlf | 5 ++++ src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 5 ++++ src/Compiler/xlf/FSComp.txt.ru.xlf | 5 ++++ src/Compiler/xlf/FSComp.txt.tr.xlf | 5 ++++ src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 5 ++++ src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 5 ++++ .../EmittedIL/Nullness/NullableDowncasting.fs | 10 +++++++ .../NullableDowncasting.fs.il.net472.bsl | 0 .../NullableDowncasting.fs.il.netcore.bsl | 0 .../NullableDowncasting.fs.opt.il.net472.bsl | 0 .../NullableDowncasting.fs.opt.il.netcore.bsl | 0 .../EmittedIL/Nullness/NullnessMetadata.fs | 12 +++++++++ 23 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs create mode 100644 tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.il.net472.bsl create mode 100644 tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.il.netcore.bsl create mode 100644 tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.opt.il.net472.bsl create mode 100644 tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.opt.il.netcore.bsl diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 60708f20c29..cdae2038307 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -2974,7 +2974,16 @@ 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), WithoutNullRefType when g.checkNullness -> + let srcNice = NicePrint.minimalStringOfTypeWithNullness denv srcTy + let tgtNice = NicePrint.minimalStringOfTypeWithNullness denv tgtTy + warning(Error(FSComp.SR.tcDowncastFromNullableToWithoutNull(srcNice,tgtNice,tgtNice), m)) + false + | (NullableRefType|NullTrueValue), (NullableRefType|NullTrueValue) -> 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 @@ -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 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/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 13723725535..a179fb6b99c 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9254,8 +9254,6 @@ let TypeNullNotLiked g m ty = /// a set of residual types that must also satisfy the constraint -let (|NullTrueValue|HasAllowsNullAttr|WithoutNullValueType|GenericTyparUnconstrained|GenericTyparConstrained|WithoutNullRefType|WithNullRefType|) g ty = failwith "" - let rec TypeHasDefaultValueAux isNew g m ty = let ty = stripTyEqnsAndMeasureEqns g ty (if isNew then TypeNullIsExtraValueNew g m ty else TypeNullIsExtraValue g m ty) @@ -9322,15 +9320,34 @@ let (|SpecialEquatableHeadType|_|) g ty = (|SpecialComparableHeadType|_|) g ty let (|SpecialNotEquatableHeadType|_|) g ty = if isFunTy g ty then ValueSome() else ValueNone +let (|TyparTy|StructTy|NullTrueValue|NullableRefType|WithoutNullRefType|UnresolvedRefType|) (ty,g) = + let sty = ty |> stripTyEqns g + if isTyparTy g sty then + 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 -> 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..0a7a58dfd2e 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -2608,6 +2608,8 @@ val (|SpecialEquatableHeadType|_|): TcGlobals -> TType -> TType list voption [] val (|SpecialNotEquatableHeadType|_|): TcGlobals -> TType -> unit voption +val (|TyparTy|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..c8c193c53ce --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs @@ -0,0 +1,10 @@ +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 isObjnullAString(o:objnull) = o :? string +let isObjNullOption(o:objnull) = o :? Option +let isObjNullObj(o:objnull) = o :? obj \ 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..e69de29bb2d 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..e69de29bb2d 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..e69de29bb2d 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..e69de29bb2d diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullnessMetadata.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullnessMetadata.fs index f0ebdfa0c34..17650e989c0 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 From fae39cae73ca30d091d12560825c583b2b0dc15b Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 6 Nov 2024 13:58:45 +0100 Subject: [PATCH 04/10] Il tests, warning tests --- .../Checking/Expressions/CheckExpressions.fs | 8 +- src/Compiler/TypedTree/TypedTreeOps.fs | 9 +- src/Compiler/TypedTree/TypedTreeOps.fsi | 2 +- .../EmittedIL/Nullness/NullableDowncasting.fs | 7 +- .../NullableDowncasting.fs.il.net472.bsl | 239 ++++++++++++++++++ .../NullableDowncasting.fs.il.netcore.bsl | 174 +++++++++++++ .../NullableDowncasting.fs.opt.il.net472.bsl | 236 +++++++++++++++++ .../NullableDowncasting.fs.opt.il.netcore.bsl | 171 +++++++++++++ .../Nullness/NullableDowncasting.fsopt.il.bsl | 171 +++++++++++++ .../EmittedIL/Nullness/NullnessMetadata.fs | 2 +- .../Nullness/NullableReferenceTypesTests.fs | 29 +++ tests/FSharp.Test.Utilities/Compiler.fs | 2 +- 12 files changed, 1039 insertions(+), 11 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fsopt.il.bsl diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index cdae2038307..caf3a4edbe1 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -2976,18 +2976,18 @@ let TcRuntimeTypeTest isCast isOperator (cenv: cenv) denv m tgtTy srcTy = else let checkTrgtNullness = match (srcTy,g),(tgtTy,g) with - | (NullableRefType|NullTrueValue), WithoutNullRefType when g.checkNullness -> + | (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), (NullableRefType|NullTrueValue) -> 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 + | (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.minimalStringOfTypeWithNullness 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 = @@ -6119,7 +6119,7 @@ and TcExprDowncast (cenv: cenv) overallTy env tpenv (synExpr, synInnerExpr, m) = // based on the nullness semantics of the nominal type. let expr = match (tgtTy,g) with - | NullTrueValue | NullableRefType when g.checkNullness -> mkCallUnboxFast g m tgtTy innerExpr + | NullTrueValue | NullableRefType | NullableTypar when g.checkNullness -> mkCallUnboxFast g m tgtTy innerExpr | _ -> mkCallUnbox g m tgtTy innerExpr expr, tpenv diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index a179fb6b99c..ad303b0ac94 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9320,10 +9320,13 @@ let (|SpecialEquatableHeadType|_|) g ty = (|SpecialComparableHeadType|_|) g ty let (|SpecialNotEquatableHeadType|_|) g ty = if isFunTy g ty then ValueSome() else ValueNone -let (|TyparTy|StructTy|NullTrueValue|NullableRefType|WithoutNullRefType|UnresolvedRefType|) (ty,g) = +let (|TyparTy|NullableTypar|StructTy|NullTrueValue|NullableRefType|WithoutNullRefType|UnresolvedRefType|) (ty,g) = let sty = ty |> stripTyEqns g if isTyparTy g sty then - TyparTy + if (nullnessOfTy g sty).TryEvaluate() = ValueSome NullnessInfo.WithNull then + NullableTypar + else + TyparTy elif isStructTy g sty then StructTy elif TypeNullIsTrueValue g sty then @@ -9344,7 +9347,7 @@ let canUseUnboxFast (g:TcGlobals) m ty = if g.checkNullness then match (ty,g) with | TyparTy | WithoutNullRefType | UnresolvedRefType -> false - | StructTy | NullTrueValue | NullableRefType -> true + | StructTy | NullTrueValue | NullableRefType | NullableTypar -> true else not (isTyparTy g ty) && not (TypeNullNotLiked g m ty) diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index 0a7a58dfd2e..7677c3e905c 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -2608,7 +2608,7 @@ val (|SpecialEquatableHeadType|_|): TcGlobals -> TType -> TType list voption [] val (|SpecialNotEquatableHeadType|_|): TcGlobals -> TType -> unit voption -val (|TyparTy|StructTy|NullTrueValue|NullableRefType|WithoutNullRefType|UnresolvedRefType|): TType * TcGlobals -> Choice +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 diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs index c8c193c53ce..ce89bede351 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs @@ -4,7 +4,12 @@ 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 isObjNullObj(o:objnull) = o :? obj \ No newline at end of file +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 index e69de29bb2d..68f2e777fe6 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.il.net472.bsl +++ 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 index e69de29bb2d..5589e54be12 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullableDowncasting.fs.il.netcore.bsl +++ 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 index e69de29bb2d..aea1ebdf966 100644 --- 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 @@ -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 index e69de29bb2d..a05e6fb269f 100644 --- 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 @@ -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 17650e989c0..8778ce5d09d 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullnessMetadata.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/NullnessMetadata.fs @@ -101,7 +101,7 @@ let ``Downcasting and typetests`` compilation = |> withNoWarn 52 |> verifyCompilation DoNotOptimize -[] +[] let ``Downcasting and typetests optimized`` compilation = compilation |> withNoWarn 52 diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs index 0da1169b9a8..e40d29f88ef 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' to the type 'AB | null'" + Error 3060, Line 6, Col 41, Line 6, Col 55, "This type test or downcast will erase the provided type 'AB' to the type 'AB | null'" + 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' to the type 'List | null'"] + + [] 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 From 1cbd820ad84ae3fc4b14efae52528cbafc4a7cb4 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 6 Nov 2024 14:03:52 +0100 Subject: [PATCH 05/10] fantomas, better type test erasure messaging --- src/Compiler/TypedTree/TypedTreeOps.fsi | 3 ++- .../Language/Nullness/NullableReferenceTypesTests.fs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index 7677c3e905c..4eafbf229dc 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -2608,7 +2608,8 @@ val (|SpecialEquatableHeadType|_|): TcGlobals -> TType -> TType list voption [] val (|SpecialNotEquatableHeadType|_|): TcGlobals -> TType -> unit voption -val (|TyparTy|NullableTypar|StructTy|NullTrueValue|NullableRefType|WithoutNullRefType|UnresolvedRefType|): TType * TcGlobals -> Choice +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 diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs index e40d29f88ef..53f29190197 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs @@ -42,10 +42,10 @@ let doNotWarnOnDowncastRepeatedNestedNullable(o:objnull) = o :? list<((AB | null |> 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' to the type 'AB | null'" - Error 3060, Line 6, Col 41, Line 6, Col 55, "This type test or downcast will erase the provided type 'AB' to the type 'AB | null'" + 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' to the type 'List | null'"] + 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'"] [] From 373ece21d6eef71e642910504acbe0980f531bdd Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 6 Nov 2024 18:06:14 +0100 Subject: [PATCH 06/10] (not into main) - Apply nullable downcasting changes (#17964) --- src/Compiler/AbstractIL/il.fs | 8 ++++---- src/Compiler/AbstractIL/ilreflect.fs | 2 +- src/Compiler/Checking/MethodCalls.fs | 2 +- .../DependencyManager/AssemblyResolveHandler.fs | 2 +- .../DependencyManager/DependencyProvider.fs | 16 ++++++++-------- src/Compiler/Interactive/fsi.fs | 4 ++-- src/Compiler/TypedTree/TypeProviders.fs | 6 +++--- src/Compiler/Utilities/TaggedCollections.fs | 8 ++++---- 8 files changed, 24 insertions(+), 24 deletions(-) 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/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/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/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index 02dbb98e2df..f6f3aa627b7 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/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 From 51609c79f9b77bf9b9fd0fbad7c8783df227903a Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 6 Nov 2024 18:06:44 +0100 Subject: [PATCH 07/10] Update tests/FSharp.Test.Utilities/Compiler.fs --- tests/FSharp.Test.Utilities/Compiler.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 8faf949e560..5e5629b089f 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -1118,7 +1118,7 @@ Expected: Actual: {actual}""" let updateBaseline () = - true//snd (Int32.TryParse(Environment.GetEnvironmentVariable("TEST_UPDATE_BSL"))) <> 0 + snd (Int32.TryParse(Environment.GetEnvironmentVariable("TEST_UPDATE_BSL"))) <> 0 let updateBaseLineIfEnvironmentSaysSo baseline = if updateBaseline () then if FileSystem.FileExistsShim baseline.FilePath then From 53779ec717103904d4f25c1d64d720e980db669e Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 6 Nov 2024 18:08:00 +0100 Subject: [PATCH 08/10] Update src/Compiler/TypedTree/TypedTreeOps.fs --- src/Compiler/TypedTree/TypedTreeOps.fs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index ad303b0ac94..aa9bbad7f8c 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9252,7 +9252,6 @@ 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 From f112cca274615468e6f9af847db83762597e7ffb Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 6 Nov 2024 18:10:01 +0100 Subject: [PATCH 09/10] release notes --- docs/release-notes/.FSharp.Compiler.Service/9.0.200.md | 1 + 1 file changed, 1 insertion(+) 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 012beaecb6b..adf5dad4419 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)) ### Added From 7bda3c3d3e66ce83fe97d3337575cd998d607c7d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 18:11:52 +0000 Subject: [PATCH 10/10] Automated command ran: fantomas Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com> --- src/Compiler/Interactive/fsi.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index f6f3aa627b7..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