From 2e8ab382b61a4c91872cd5f868e2316f7d6c5be4 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 24 May 2023 15:17:10 +0100 Subject: [PATCH] squash --- .fantomasignore | 17 + FSharp.Profiles.props | 28 ++ VisualFSharp.sln | 15 + src/Compiler/AbstractIL/ilread.fs | 4 +- src/Compiler/Checking/CheckExpressions.fs | 72 ++- src/Compiler/Checking/CheckPatterns.fs | 2 + src/Compiler/Checking/ConstraintSolver.fs | 444 +++++++++++++++--- src/Compiler/Checking/ConstraintSolver.fsi | 24 + src/Compiler/Checking/InfoReader.fs | 8 +- src/Compiler/Checking/MethodCalls.fs | 10 +- src/Compiler/Checking/NameResolution.fs | 4 +- src/Compiler/Checking/NicePrint.fs | 17 +- src/Compiler/Checking/PostInferenceChecks.fs | 28 +- src/Compiler/Checking/SignatureConformance.fs | 2 +- src/Compiler/Checking/TypeHierarchy.fs | 13 +- src/Compiler/Checking/TypeRelations.fs | 4 +- src/Compiler/Checking/import.fs | 38 +- src/Compiler/Checking/infos.fs | 10 +- .../DependencyManager/DependencyProvider.fs | 2 + .../DependencyManager/DependencyProvider.fsi | 2 + src/Compiler/Driver/CompilerConfig.fs | 10 +- src/Compiler/Driver/CompilerConfig.fsi | 10 +- src/Compiler/Driver/CompilerDiagnostics.fs | 56 ++- src/Compiler/Driver/CompilerImports.fs | 323 ++++++++----- src/Compiler/Driver/CompilerImports.fsi | 9 +- src/Compiler/Driver/CompilerOptions.fs | 8 + .../GraphChecking/FileContentMapping.fs | 3 + src/Compiler/Driver/StaticLinking.fs | 6 +- src/Compiler/FSComp.txt | 16 + src/Compiler/FSStrings.resx | 18 + src/Compiler/FSharp.Compiler.Service.fsproj | 3 +- src/Compiler/Facilities/LanguageFeatures.fs | 3 + src/Compiler/Facilities/LanguageFeatures.fsi | 1 + src/Compiler/Interactive/fsi.fs | 4 +- .../Legacy/LegacyHostedCompilerForTesting.fs | 12 +- src/Compiler/Service/IncrementalBuild.fs | 4 +- src/Compiler/Service/QuickParse.fs | 2 +- .../Service/ServiceDeclarationLists.fs | 2 +- src/Compiler/Service/ServiceLexing.fs | 3 + .../Service/ServiceParamInfoLocations.fs | 1 + src/Compiler/Service/ServiceParseTreeWalk.fs | 2 + src/Compiler/Service/ServiceParsedInputOps.fs | 6 + src/Compiler/Symbols/Symbols.fs | 22 + src/Compiler/Symbols/Symbols.fsi | 6 + src/Compiler/SyntaxTree/LexFilter.fs | 4 + src/Compiler/SyntaxTree/LexHelpers.fs | 4 + src/Compiler/SyntaxTree/PrettyNaming.fs | 9 + src/Compiler/SyntaxTree/PrettyNaming.fsi | 8 + src/Compiler/SyntaxTree/SyntaxTree.fs | 9 + src/Compiler/SyntaxTree/SyntaxTree.fsi | 8 + src/Compiler/TypedTree/TcGlobals.fs | 88 ++-- src/Compiler/TypedTree/TypeProviders.fs | 367 ++++++++++----- src/Compiler/TypedTree/TypeProviders.fsi | 94 ++-- src/Compiler/TypedTree/TypedTree.fs | 118 +++-- src/Compiler/TypedTree/TypedTree.fsi | 41 +- src/Compiler/TypedTree/TypedTreeBasics.fs | 85 +++- src/Compiler/TypedTree/TypedTreeBasics.fsi | 18 +- src/Compiler/TypedTree/TypedTreeOps.fs | 195 +++++--- src/Compiler/TypedTree/TypedTreeOps.fsi | 14 +- src/Compiler/TypedTree/TypedTreePickle.fs | 236 ++++++++-- src/Compiler/TypedTree/TypedTreePickle.fsi | 3 +- src/Compiler/TypedTree/tainted.fs | 12 +- src/Compiler/TypedTree/tainted.fsi | 6 +- src/Compiler/Utilities/illib.fs | 12 +- src/Compiler/Utilities/illib.fsi | 6 + src/Compiler/Utilities/lib.fs | 8 +- src/Compiler/Utilities/lib.fsi | 14 +- src/Compiler/Utilities/sformat.fs | 36 +- src/Compiler/pars.fsy | 55 ++- src/Compiler/xlf/FSComp.txt.cs.xlf | 60 +++ src/Compiler/xlf/FSComp.txt.de.xlf | 60 +++ src/Compiler/xlf/FSComp.txt.es.xlf | 60 +++ src/Compiler/xlf/FSComp.txt.fr.xlf | 60 +++ src/Compiler/xlf/FSComp.txt.it.xlf | 60 +++ src/Compiler/xlf/FSComp.txt.ja.xlf | 60 +++ src/Compiler/xlf/FSComp.txt.ko.xlf | 60 +++ src/Compiler/xlf/FSComp.txt.pl.xlf | 60 +++ src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 60 +++ src/Compiler/xlf/FSComp.txt.ru.xlf | 60 +++ src/Compiler/xlf/FSComp.txt.tr.xlf | 60 +++ src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 60 +++ src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 60 +++ src/Compiler/xlf/FSStrings.cs.xlf | 30 ++ src/Compiler/xlf/FSStrings.de.xlf | 30 ++ src/Compiler/xlf/FSStrings.es.xlf | 30 ++ src/Compiler/xlf/FSStrings.fr.xlf | 30 ++ src/Compiler/xlf/FSStrings.it.xlf | 30 ++ src/Compiler/xlf/FSStrings.ja.xlf | 30 ++ src/Compiler/xlf/FSStrings.ko.xlf | 30 ++ src/Compiler/xlf/FSStrings.pl.xlf | 30 ++ src/Compiler/xlf/FSStrings.pt-BR.xlf | 30 ++ src/Compiler/xlf/FSStrings.ru.xlf | 30 ++ src/Compiler/xlf/FSStrings.tr.xlf | 30 ++ src/Compiler/xlf/FSStrings.zh-Hans.xlf | 30 ++ src/Compiler/xlf/FSStrings.zh-Hant.xlf | 30 ++ src/FSharp.Build/FSharp.Build.fsproj | 3 +- src/FSharp.Build/FSharpCommandLineBuilder.fs | 9 +- src/FSharp.Build/Fsc.fs | 17 +- src/FSharp.Build/SubstituteText.fs | 2 +- src/FSharp.Core/FSharp.Core.fsproj | 1 - src/FSharp.Core/array.fs | 13 + src/FSharp.Core/async.fs | 4 + src/FSharp.Core/local.fs | 4 + src/FSharp.Core/option.fs | 28 ++ src/FSharp.Core/option.fsi | 28 +- src/FSharp.Core/prim-types.fs | 72 ++- src/FSharp.Core/prim-types.fsi | 118 ++++- src/fsi/console.fs | 32 +- .../Expressions/BindingExpressions/in05.fs | 4 +- .../ErrorMessages/TypeEqualsMissingTests.fs | 24 +- ...ervice.SurfaceArea.netstandard20.debug.bsl | 33 +- .../Microsoft.FSharp.Core/BigIntType.fs | 2 +- .../Microsoft.FSharp.Core/OptionModule.fs | 4 +- .../NullableOptionalRegressionTests.fs | 3 +- tests/fsharp/tests.fs | 88 +++- ...n_return_type_and_known_type_arguments.fsx | 6 +- tests/fsharp/typecheck/sigs/neg04.bsl | 4 +- tests/fsharp/typecheck/sigs/neg20.bsl | 6 +- vsintegration/src/FSharp.VS.FSI/fsiBasis.fs | 5 +- 119 files changed, 3722 insertions(+), 682 deletions(-) diff --git a/.fantomasignore b/.fantomasignore index 45eb387fa16..ea5d5fb913c 100644 --- a/.fantomasignore +++ b/.fantomasignore @@ -90,6 +90,23 @@ src/FSharp.Core/Query.fs src/FSharp.Core/seqcore.fs +# fsharp (to investigate) + +**/TypeProviders.fsi +**/tainted.fsi + +# uses nullness features + +**/DependencyProvider.fsi +src/FSharp.Core/array.fs +src/FSharp.Core/option.fsi +src/FSharp.Core/option.fs +src/fsi/console.fs +src/FSharp.Build/FSharpCommandLineBuilder.fs +src/Compiler/Utilities/sformat.fs +src/Compiler/Utilities/illib.fsi +src/Compiler/Utilities/illib.fs + # Fantomas limitations on implementation files (to investigate) src/Compiler/AbstractIL/ilwrite.fs diff --git a/FSharp.Profiles.props b/FSharp.Profiles.props index 66e46a32b08..d477f570f34 100644 --- a/FSharp.Profiles.props +++ b/FSharp.Profiles.props @@ -1,6 +1,34 @@ + + + false + + + + + false + + + + BUILDING_WITH_LKG;NO_NULLCHECKING_LIB_SUPPORT;$(DefineConstants) + false + + + + $(OtherFlags) /langversion:preview + + + + $(OtherFlags) /checknulls + + + + + $(NoWarn);3271 + NO_CHECKNULLS;$(DefineConstants) + diff --git a/VisualFSharp.sln b/VisualFSharp.sln index b7b9e82b5dc..516513595da 100644 --- a/VisualFSharp.sln +++ b/VisualFSharp.sln @@ -195,6 +195,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Editor.Tests", "vsin EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.Editor.IntegrationTests", "vsintegration\tests\FSharp.Editor.IntegrationTests\FSharp.Editor.IntegrationTests.csproj", "{E31F9B59-FCF1-4D04-8762-C7BB60285A7B}" EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "nullness", "tests\fsharp\core\nullness\nullness.fsproj", "{6992D926-AB1C-4CD4-94D5-0319D14DB54B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1033,6 +1035,18 @@ Global {E31F9B59-FCF1-4D04-8762-C7BB60285A7B}.Release|Any CPU.Build.0 = Release|Any CPU {E31F9B59-FCF1-4D04-8762-C7BB60285A7B}.Release|x86.ActiveCfg = Release|Any CPU {E31F9B59-FCF1-4D04-8762-C7BB60285A7B}.Release|x86.Build.0 = Release|Any CPU + {6992D926-AB1C-4CD4-94D5-0319D14DB54B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6992D926-AB1C-4CD4-94D5-0319D14DB54B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6992D926-AB1C-4CD4-94D5-0319D14DB54B}.Debug|x86.ActiveCfg = Debug|Any CPU + {6992D926-AB1C-4CD4-94D5-0319D14DB54B}.Debug|x86.Build.0 = Debug|Any CPU + {6992D926-AB1C-4CD4-94D5-0319D14DB54B}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {6992D926-AB1C-4CD4-94D5-0319D14DB54B}.Proto|Any CPU.Build.0 = Debug|Any CPU + {6992D926-AB1C-4CD4-94D5-0319D14DB54B}.Proto|x86.ActiveCfg = Debug|Any CPU + {6992D926-AB1C-4CD4-94D5-0319D14DB54B}.Proto|x86.Build.0 = Debug|Any CPU + {6992D926-AB1C-4CD4-94D5-0319D14DB54B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6992D926-AB1C-4CD4-94D5-0319D14DB54B}.Release|Any CPU.Build.0 = Release|Any CPU + {6992D926-AB1C-4CD4-94D5-0319D14DB54B}.Release|x86.ActiveCfg = Release|Any CPU + {6992D926-AB1C-4CD4-94D5-0319D14DB54B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1114,6 +1128,7 @@ Global {FE23BB65-276A-4E41-8CC7-F7752241DEBA} = {39CDF34B-FB23-49AE-AB27-0975DA379BB5} {CBC96CC7-65AB-46EA-A82E-F6A788DABF80} = {F7876C9B-FB6A-4EFB-B058-D6967DB75FB2} {E31F9B59-FCF1-4D04-8762-C7BB60285A7B} = {F7876C9B-FB6A-4EFB-B058-D6967DB75FB2} + {6992D926-AB1C-4CD4-94D5-0319D14DB54B} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {48EDBBBE-C8EE-4E3C-8B19-97184A487B37} diff --git a/src/Compiler/AbstractIL/ilread.fs b/src/Compiler/AbstractIL/ilread.fs index 10189157adf..9f2f7d48f28 100644 --- a/src/Compiler/AbstractIL/ilread.fs +++ b/src/Compiler/AbstractIL/ilread.fs @@ -931,7 +931,7 @@ let mkCacheInt32 lowMem _inbase _nm _sz = if lowMem then (fun f x -> f x) else - let mutable cache = null + let mutable cache: ConcurrentDictionary MaybeNull = null // TODO NULLNESS: this explicit annotation should not be needed let mutable count = 0 #if STATISTICS addReport (fun oc -> @@ -960,7 +960,7 @@ let mkCacheGeneric lowMem _inbase _nm _sz = if lowMem then (fun f x -> f x) else - let mutable cache = null + let mutable cache: ConcurrentDictionary<_, _> MaybeNull = null // TODO NULLNESS: this explicit annotation should not be needed let mutable count = 0 #if STATISTICS addReport (fun oc -> diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 13075a5e6b9..666c8acc1a9 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -971,6 +971,43 @@ let TranslatePartialValReprInfo tps (PrelimValReprInfo (argsData, retData)) = // Members //------------------------------------------------------------------------- +let TcAddNullnessToType (warn: bool) (cenv: cenv) (env: TcEnv) nullness innerTyC m = + let g = cenv.g + if g.langFeatureNullness then + if TypeNullNever g innerTyC then + let tyString = NicePrint.minimalStringOfType env.DisplayEnv innerTyC + errorR(Error(FSComp.SR.tcTypeDoesNotHaveAnyNull(tyString), m)) + + match tryAddNullnessToTy nullness innerTyC with + + | None -> + let tyString = NicePrint.minimalStringOfType env.DisplayEnv innerTyC + errorR(Error(FSComp.SR.tcTypeDoesNotHaveAnyNull(tyString), m)) + innerTyC + + | Some innerTyCWithNull -> + // The inner type is not allowed to support null or use null as a representation value. + // For example "int option?" is not allowed, nor "string??". + // + // For variable types in FSharp.Core we make an exception because we must allow + // val toObj: value: 'T option -> 'T __withnull when 'T : not struct (* and 'T : __notnull *) + // wihout implying 'T is not null. This is because it is legitimate to use this + // function to "collapse" null and obj-null-coming-from-option using such a function. + + if not g.compilingFSharpCore || not (isTyparTy g innerTyC) then + AddCxTypeDefnNotSupportsNull env.DisplayEnv cenv.css m NoTrace innerTyC + + innerTyCWithNull + + else + if warn then + warning(Error(FSComp.SR.tcNullnessCheckingNotEnabled(), m)) + innerTyC + +//------------------------------------------------------------------------- +// Members +//------------------------------------------------------------------------- + let ComputeLogicalName (id: Ident) (memberFlags: SynMemberFlags) = match memberFlags.MemberKind with | SynMemberKind.ClassConstructor -> ".cctor" @@ -2085,7 +2122,7 @@ module GeneralizationHelpers = match tp.Constraints |> List.partition (function TyparConstraint.CoercesTo _ -> true | _ -> false) with | [TyparConstraint.CoercesTo(tgtTy, _)], others -> // Throw away null constraints if they are implied - if others |> List.exists (function TyparConstraint.SupportsNull _ -> not (TypeSatisfiesNullConstraint g m tgtTy) | _ -> true) + if others |> List.exists (function TyparConstraint.SupportsNull _ -> not (TypeNullIsExtraValue g m tgtTy) | _ -> true) then None else Some tgtTy | _ -> None @@ -3963,7 +4000,14 @@ let rec TcTyparConstraint ridx (cenv: cenv) newOk checkConstraints occ (env: TcE tpenv | SynTypeConstraint.WhereTyparSupportsNull(tp, m) -> - TcSimpleTyparConstraint cenv env newOk tpenv tp m AddCxTypeUseSupportsNull + TcSimpleTyparConstraint cenv env newOk tpenv tp m AddCxTypeDefnSupportsNull + + | SynTypeConstraint.WhereTyparNotSupportsNull(tp, m) -> + if g.langFeatureNullness then + TcSimpleTyparConstraint cenv env newOk tpenv tp m AddCxTypeDefnNotSupportsNull + else + warning(Error(FSComp.SR.tcNullnessCheckingNotEnabled(), m)) + tpenv | SynTypeConstraint.WhereTyparIsComparable(tp, m) -> TcSimpleTyparConstraint cenv env newOk tpenv tp m AddCxTypeMustSupportComparison @@ -4377,11 +4421,18 @@ and TcTypeOrMeasure kindOpt (cenv: cenv) newOk checkConstraints occ (iwsam: Warn | SynType.StaticConstant (synConst, m) -> TcTypeStaticConstant kindOpt tpenv synConst m + | SynType.StaticConstantNull m | SynType.StaticConstantNamed (_, _, m) | SynType.StaticConstantExpr (_, m) -> errorR(Error(FSComp.SR.parsInvalidLiteralInType(), m)) NewErrorType (), tpenv + | SynType.WithNull(innerTy, ambivalent, m) -> + let innerTyC, tpenv = TcTypeAndRecover cenv newOk checkConstraints occ WarnOnIWSAM.Yes env tpenv innerTy + let nullness = if ambivalent then KnownAmbivalentToNull else KnownWithNull + let tyWithNull = TcAddNullnessToType false cenv env nullness innerTyC m + tyWithNull, tpenv + | SynType.MeasurePower(ty, exponent, m) -> TcTypeMeasurePower kindOpt cenv newOk checkConstraints occ env tpenv ty exponent m @@ -4531,7 +4582,7 @@ and TcFunctionType (cenv: cenv) newOk checkConstraints occ env tpenv domainTy re and TcArrayType (cenv: cenv) newOk checkConstraints occ env tpenv rank elemTy m = let g = cenv.g let elemTy, tpenv = TcTypeAndRecover cenv newOk checkConstraints occ WarnOnIWSAM.Yes env tpenv elemTy - let tyR = mkArrayTy g rank elemTy m + let tyR = mkArrayTy g rank g.knownWithoutNull elemTy m tyR, tpenv and TcTypeParameter kindOpt (cenv: cenv) env newOk tpenv tp = @@ -4556,8 +4607,9 @@ and TcTypeWithConstraints (cenv: cenv) env newOk checkConstraints occ tpenv synT and TcTypeHashConstraint (cenv: cenv) env newOk checkConstraints occ tpenv synTy m = let tp = TcAnonTypeOrMeasure (Some TyparKind.Type) cenv TyparRigidity.WarnIfNotRigid TyparDynamicReq.Yes newOk m let ty, tpenv = TcTypeAndRecover cenv newOk checkConstraints occ WarnOnIWSAM.No env tpenv synTy - AddCxTypeMustSubsumeType ContextInfo.NoContext env.DisplayEnv cenv.css m NoTrace ty (mkTyparTy tp) - tp.AsType, tpenv + let tpTy = mkTyparTy tp + AddCxTypeMustSubsumeType ContextInfo.NoContext env.DisplayEnv cenv.css m NoTrace ty tpTy + tpTy, tpenv and TcTypeStaticConstant kindOpt tpenv c m = match c, kindOpt with @@ -4711,7 +4763,7 @@ and TcStaticConstantParameter (cenv: cenv) (env: TcEnv) tpenv kind (StripParenTy | SynConst.Double n when typeEquiv g g.float_ty kind -> record(g.float_ty); box (n: double) | SynConst.Char n when typeEquiv g g.char_ty kind -> record(g.char_ty); box (n: char) | SynConst.String (s, _, _) - | SynConst.SourceIdentifier (_, s, _) when s <> null && typeEquiv g g.string_ty kind -> record(g.string_ty); box (s: string) + | SynConst.SourceIdentifier (_, s, _) when typeEquiv g g.string_ty kind -> record(g.string_ty); box (s: string) | SynConst.Bool b when typeEquiv g g.bool_ty kind -> record(g.bool_ty); box (b: bool) | _ -> fail() v, tpenv @@ -4740,7 +4792,6 @@ and TcStaticConstantParameter (cenv: cenv) (env: TcEnv) tpenv kind (StripParenTy | Const.Single n -> record(g.float32_ty); box (n: single) | Const.Double n -> record(g.float_ty); box (n: double) | Const.Char n -> record(g.char_ty); box (n: char) - | Const.String null -> fail() | Const.String s -> record(g.string_ty); box (s: string) | Const.Bool b -> record(g.bool_ty); box (b: bool) | _ -> fail() @@ -4909,7 +4960,7 @@ and TcTypeApp (cenv: cenv) newOk checkConstraints occ env tpenv m tcref pathType List.iter2 (UnifyTypes cenv env m) tinst actualArgTys // Try to decode System.Tuple --> F# tuple types etc. - let ty = g.decompileType tcref actualArgTys + let ty = g.decompileType tcref actualArgTys g.knownWithoutNull ty, tpenv @@ -5564,8 +5615,11 @@ and TcExprUndelayed (cenv: cenv) (overallTy: OverallTy) env tpenv (synExpr: SynE | SynExpr.Null m -> TcNonControlFlowExpr env <| fun env -> + // Which? AddCxTypeUseSupportsNull env.DisplayEnv cenv.css m NoTrace overallTy.Commit - mkNull m overallTy.Commit, tpenv + //AddCxTypeDefnSupportsNull env.DisplayEnv cenv.css m NoTrace overallTy.Commit + let tyWithNull = addNullnessToTy KnownWithNull overallTy.Commit + mkNull m tyWithNull, tpenv | SynExpr.Lazy (synInnerExpr, m) -> TcNonControlFlowExpr env <| fun env -> diff --git a/src/Compiler/Checking/CheckPatterns.fs b/src/Compiler/Checking/CheckPatterns.fs index 5ce9fa02e69..2c28dfb5110 100644 --- a/src/Compiler/Checking/CheckPatterns.fs +++ b/src/Compiler/Checking/CheckPatterns.fs @@ -461,6 +461,8 @@ and TcRecordPat warnOnUpper cenv env vFlags patEnv ty fieldPats m = and TcNullPat cenv env patEnv ty m = try AddCxTypeUseSupportsNull env.DisplayEnv cenv.css m NoTrace ty + // Which? + //AddCxTypeDefnSupportsNull env.DisplayEnv cenv.css m NoTrace ty with exn -> errorRecovery exn m (fun _ -> TPat_null m), patEnv diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index 22ebc3ca4df..2cd52880800 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -69,6 +69,10 @@ open FSharp.Compiler.TypedTreeOps open FSharp.Compiler.TypeHierarchy open FSharp.Compiler.TypeRelations +#if !NO_TYPEPROVIDERS +open FSharp.Compiler.TypeProviders +#endif + //------------------------------------------------------------------------- // Generate type variables and record them in within the scope of the // compilation environment, which currently corresponds to the scope @@ -98,9 +102,10 @@ let NewErrorMeasureVar () = NewCompGenTypar (TyparKind.Measure, TyparRigidity.Flexible, TyparStaticReq.None, TyparDynamicReq.No, true) let NewInferenceType (g: TcGlobals) = - ignore g // included for future, minimizing code diffs, see https://github.com/dotnet/fsharp/pull/6804 - mkTyparTy (Construct.NewTypar (TyparKind.Type, TyparRigidity.Flexible, SynTypar(compgenId, TyparStaticReq.None, true), false, TyparDynamicReq.No, [], false, false)) - + let tp = Construct.NewTypar (TyparKind.Type, TyparRigidity.Flexible, SynTypar(compgenId, TyparStaticReq.None, true), false, TyparDynamicReq.No, [], false, false) + let nullness = if g.langFeatureNullness then NewNullnessVar() else KnownAmbivalentToNull + TType_var (tp, nullness) + let NewErrorType () = mkTyparTy (NewErrorTypar ()) @@ -240,7 +245,15 @@ exception ConstraintSolverTypesNotInEqualityRelation of displayEnv: DisplayEnv * exception ConstraintSolverTypesNotInSubsumptionRelation of displayEnv: DisplayEnv * argTy: TType * paramTy: TType * callRange: range * parameterRange: range -exception ConstraintSolverMissingConstraint of displayEnv: DisplayEnv * Typar * TyparConstraint * range * range +exception ConstraintSolverMissingConstraint of displayEnv: DisplayEnv * Typar * TyparConstraint * range * range + +exception ConstraintSolverNullnessWarningEquivWithTypes of DisplayEnv * TType * TType * NullnessInfo * NullnessInfo * range * range + +exception ConstraintSolverNullnessWarningWithTypes of DisplayEnv * TType * TType * NullnessInfo * NullnessInfo * range * range + +exception ConstraintSolverNullnessWarningWithType of DisplayEnv * TType * NullnessInfo * range * range + +exception ConstraintSolverNonNullnessWarningWithType of DisplayEnv * TType * NullnessInfo * range * range exception ConstraintSolverError of string * range * range @@ -1043,7 +1056,8 @@ and SolveTypMeetsTyparConstraints (csenv: ConstraintSolverEnv) ndeep m2 trace ty | ValueSome destTypar -> AddConstraint csenv ndeep m2 trace destTypar (TyparConstraint.DefaultsTo(priority, dty, m)) - | TyparConstraint.SupportsNull m2 -> SolveTypeUseSupportsNull csenv ndeep m2 trace ty + | TyparConstraint.NotSupportsNull m2 -> SolveTypeDefnNotSupportsNull csenv ndeep m2 trace ty + | TyparConstraint.SupportsNull m2 -> SolveTypeDefnSupportsNull csenv ndeep m2 trace ty | TyparConstraint.IsEnum(underlyingTy, m2) -> SolveTypeIsEnum csenv ndeep m2 trace ty underlyingTy | TyparConstraint.SupportsComparison(m2) -> SolveTypeSupportsComparison csenv ndeep m2 trace ty | TyparConstraint.SupportsEquality(m2) -> SolveTypeSupportsEquality csenv ndeep m2 trace ty @@ -1058,6 +1072,73 @@ and SolveTypMeetsTyparConstraints (csenv: ConstraintSolverEnv) ndeep m2 trace ty SolveMemberConstraint csenv false PermitWeakResolution.No ndeep m2 trace traitInfo |> OperationResult.ignore } +// nullness1: actual +// nullness2: expected +and SolveNullnessEquiv (csenv:ConstraintSolverEnv) m2 (trace: OptionalTrace) ty1 ty2 nullness1 nullness2 = + match nullness1, nullness2 with + | Nullness.Variable nv1, Nullness.Variable nv2 when nv1 === nv2 -> + CompleteD + | Nullness.Variable nv1, _ when nv1.IsSolved -> + SolveNullnessEquiv csenv m2 trace ty1 ty2 nv1.Solution nullness2 + | _, Nullness.Variable nv2 when nv2.IsSolved -> + SolveNullnessEquiv csenv m2 trace ty1 ty2 nullness1 nv2.Solution + | Nullness.Variable nv1, _ -> + trace.Exec (fun () -> nv1.Set nullness2) (fun () -> nv1.Unset()) + CompleteD + | _, Nullness.Variable nv2 -> + trace.Exec (fun () -> nv2.Set nullness1) (fun () -> nv2.Unset()) + CompleteD + | Nullness.Known n1, Nullness.Known n2 -> + match n1, n2 with + | NullnessInfo.AmbivalentToNull, _ -> CompleteD + | _, NullnessInfo.AmbivalentToNull -> CompleteD + | NullnessInfo.WithNull, NullnessInfo.WithNull -> CompleteD + | NullnessInfo.WithoutNull, NullnessInfo.WithoutNull -> CompleteD + // Allow expected of WithNull and actual of WithoutNull + // TODO NULLNESS: this is not sound in contravariant cases etc. + | NullnessInfo.WithNull, NullnessInfo.WithoutNull -> CompleteD + | _ -> + // NOTE: we never give nullness warnings for the 'obj' type + if csenv.g.checkNullness then + if not (isObjTy csenv.g ty1) && not (isObjTy csenv.g ty2) then + WarnD(ConstraintSolverNullnessWarningEquivWithTypes(csenv.DisplayEnv, ty1, ty2, n1, n2, csenv.m, m2)) + else + CompleteD + else + CompleteD + +// nullness1: target +// nullness2: source +and SolveNullnessSubsumesNullness (csenv:ConstraintSolverEnv) m2 (trace: OptionalTrace) ty1 ty2 nullness1 nullness2 = + match nullness1, nullness2 with + | Nullness.Variable nv1, Nullness.Variable nv2 when nv1 === nv2 -> + CompleteD + | Nullness.Variable nv1, _ when nv1.IsSolved -> + SolveNullnessSubsumesNullness csenv m2 trace ty1 ty2 nv1.Solution nullness2 + | _, Nullness.Variable nv2 when nv2.IsSolved -> + SolveNullnessSubsumesNullness csenv m2 trace ty1 ty2 nullness1 nv2.Solution + | Nullness.Variable nv1, _ -> + trace.Exec (fun () -> nv1.Set nullness2) (fun () -> nv1.Unset()) + CompleteD + | _, Nullness.Variable nv2 -> + trace.Exec (fun () -> nv2.Set nullness1) (fun () -> nv2.Unset()) + CompleteD + | Nullness.Known n1, Nullness.Known n2 -> + match n1, n2 with + | NullnessInfo.AmbivalentToNull, _ -> CompleteD + | _, NullnessInfo.AmbivalentToNull -> CompleteD + | NullnessInfo.WithNull, NullnessInfo.WithNull -> CompleteD + | NullnessInfo.WithoutNull, NullnessInfo.WithoutNull -> CompleteD + // Allow target of WithNull and actual of WithoutNull + | NullnessInfo.WithNull, NullnessInfo.WithoutNull -> CompleteD + | NullnessInfo.WithoutNull, NullnessInfo.WithNull -> + if csenv.g.checkNullness then + if not (isObjTy csenv.g ty1) && not (isObjTy csenv.g ty2) then + WarnD(ConstraintSolverNullnessWarningWithTypes(csenv.DisplayEnv, ty1, ty2, n1, n2, csenv.m, m2)) + else + CompleteD + else + CompleteD and SolveTyparEqualsType (csenv: ConstraintSolverEnv) ndeep m2 (trace: OptionalTrace) ty1 ty = trackErrors { let m = csenv.m @@ -1079,7 +1160,6 @@ and SolveTyparsEqualTypes (csenv: ConstraintSolverEnv) ndeep m2 (trace: Optional SolveTyparEqualsTypePart1 csenv m2 trace tpTy r ty | _ -> failwith "SolveTyparsEqualTypes") - do! (tpTys, tys) ||> Iterate2D (fun tpTy ty -> match tpTy with | TType_var (r, _) @@ -1128,7 +1208,7 @@ and SolveAnonInfoEqualsAnonInfo (csenv: ConstraintSolverEnv) m2 (anonInfo1: Anon /// Add the constraint "ty1 = ty2" to the constraint problem. /// Propagate all effects of adding this constraint, e.g. to solve type variables -and SolveTypeEqualsType (csenv: ConstraintSolverEnv) ndeep m2 (trace: OptionalTrace) (cxsln:(TraitConstraintInfo * TraitConstraintSln) option) ty1 ty2 = +and SolveTypeEqualsType (csenv:ConstraintSolverEnv) ndeep m2 (trace: OptionalTrace) (cxsln:(TraitConstraintInfo * TraitConstraintSln) option) ty1 ty2 = let ndeep = ndeep + 1 let aenv = csenv.EquivEnv let g = csenv.g @@ -1152,46 +1232,97 @@ and SolveTypeEqualsType (csenv: ConstraintSolverEnv) ndeep m2 (trace: OptionalTr match sty1, sty2 with // type vars inside forall-types may be alpha-equivalent - | TType_var (tp1, _), TType_var (tp2, _) when typarEq tp1 tp2 || (match aenv.EquivTypars.TryFind tp1 with | Some tpTy1 when typeEquiv g tpTy1 ty2 -> true | _ -> false) -> - CompleteD - - // 'v1 = 'v2 - | TType_var (tp1, _), TType_var (tp2, _) when PreferUnifyTypar tp1 tp2 -> - SolveTyparEqualsType csenv ndeep m2 trace sty1 ty2 - - // 'v1 = 'v2 - | TType_var (tp1, _), TType_var (tp2, _) when not csenv.MatchingOnly && PreferUnifyTypar tp2 tp1 -> - SolveTyparEqualsType csenv ndeep m2 trace sty2 ty1 - - | TType_var (r, _), _ when not (IsRigid csenv r) -> - SolveTyparEqualsType csenv ndeep m2 trace sty1 ty2 - - | _, TType_var (r, _) when not csenv.MatchingOnly && not (IsRigid csenv r) -> - SolveTyparEqualsType csenv ndeep m2 trace sty2 ty1 + | TType_var (tp1, nullness1), TType_var (tp2, nullness2) when typarEq tp1 tp2 || (match aenv.EquivTypars.TryFind tp1 with | Some tpTy1 when typeEquiv g tpTy1 ty2 -> true | _ -> false) -> + SolveNullnessEquiv csenv m2 trace ty1 ty2 nullness1 nullness2 + + | TType_var (tp1, nullness1), TType_var (tp2, nullness2) when PreferUnifyTypar tp1 tp2 -> + match nullness1.TryEvaluate(), nullness2.TryEvaluate() with + // Unifying 'T1? and 'T2? + | ValueSome NullnessInfo.WithNull, ValueSome NullnessInfo.WithNull -> + SolveTyparEqualsType csenv ndeep m2 trace sty1 (TType_var (tp2, g.knownWithoutNull)) + //// Unifying 'T1 % and 'T2 % + //| ValueSome NullnessInfo.AmbivalentToNull, ValueSome NullnessInfo.AmbivalentToNull -> + // SolveTyparEqualsType csenv ndeep m2 trace sty1 (TType_var (tp2, g.knownWithoutNull)) + | _ -> + SolveTyparEqualsType csenv ndeep m2 trace sty1 ty2 ++ (fun () -> + let nullnessAfterSolution1 = combineNullness (nullnessOfTy g sty1) nullness1 + SolveNullnessEquiv csenv m2 trace ty1 ty2 nullnessAfterSolution1 nullness2 + ) + + | TType_var (tp1, nullness1), TType_var (tp2, nullness2) when not csenv.MatchingOnly && PreferUnifyTypar tp2 tp1 -> + match nullness1.TryEvaluate(), nullness2.TryEvaluate() with + // Unifying 'T1? and 'T2? + | ValueSome NullnessInfo.WithNull, ValueSome NullnessInfo.WithNull -> + SolveTyparEqualsType csenv ndeep m2 trace sty2 (TType_var (tp1, g.knownWithoutNull)) + //// Unifying 'T1 % and 'T2 % + //| ValueSome NullnessInfo.AmbivalentToNull, ValueSome NullnessInfo.AmbivalentToNull -> + // SolveTyparEqualsType csenv ndeep m2 trace sty2 (TType_var (tp1, g.knownWithoutNull)) + | _ -> + // Unifying 'T1 ? and 'T2 % + // Unifying 'T1 % and 'T2 ? + SolveTyparEqualsType csenv ndeep m2 trace sty2 ty1 ++ (fun () -> + let nullnessAfterSolution2 = combineNullness (nullnessOfTy g sty2) nullness2 + SolveNullnessEquiv csenv m2 trace ty1 ty2 nullness1 nullnessAfterSolution2 + ) + + | TType_var (tp1, nullness1), _ when not (IsRigid csenv tp1) -> + match nullness1.TryEvaluate(), (nullnessOfTy g sty2).TryEvaluate() with + // Unifying 'T1? and 'T2? + | ValueSome NullnessInfo.WithNull, ValueSome NullnessInfo.WithNull -> + SolveTyparEqualsType csenv ndeep m2 trace sty1 (replaceNullnessOfTy g.knownWithoutNull sty2) + // Unifying 'T1 % and 'T2 % + //| ValueSome NullnessInfo.AmbivalentToNull, ValueSome NullnessInfo.AmbivalentToNull -> + // SolveTyparEqualsType csenv ndeep m2 trace sty1 (replaceNullnessOfTy g.knownWithoutNull sty2) + | _ -> + SolveTyparEqualsType csenv ndeep m2 trace sty1 ty2 ++ (fun () -> + let nullnessAfterSolution1 = combineNullness (nullnessOfTy g sty1) nullness1 + SolveNullnessEquiv csenv m2 trace ty1 ty2 nullnessAfterSolution1 (nullnessOfTy g sty2) + ) + + | _, TType_var (tp2, nullness2) when not csenv.MatchingOnly && not (IsRigid csenv tp2) -> + match (nullnessOfTy g sty1).TryEvaluate(), nullness2.TryEvaluate() with + // Unifying 'T1? and 'T2? + | ValueSome NullnessInfo.WithNull, ValueSome NullnessInfo.WithNull -> + SolveTyparEqualsType csenv ndeep m2 trace sty2 (replaceNullnessOfTy g.knownWithoutNull sty1) + // Unifying 'T1 % and 'T2 % + //| ValueSome NullnessInfo.AmbivalentToNull, ValueSome NullnessInfo.AmbivalentToNull -> + // SolveTyparEqualsType csenv ndeep m2 trace sty2 (replaceNullnessOfTy g.knownWithoutNull sty1) + | _ -> + SolveTyparEqualsType csenv ndeep m2 trace sty2 ty1 ++ (fun () -> + let nullnessAfterSolution2 = combineNullness (nullnessOfTy g sty2) nullness2 + SolveNullnessEquiv csenv m2 trace ty1 ty2 (nullnessOfTy g sty1) nullnessAfterSolution2 + ) // Catch float<_>=float<1>, float32<_>=float32<1> and decimal<_>=decimal<1> - | _, TType_app (tc2, [ms], _) when (tc2.IsMeasureableReprTycon && typeEquiv csenv.g sty1 (reduceTyconRefMeasureableOrProvided csenv.g tc2 [ms])) -> - SolveTypeEqualsType csenv ndeep m2 trace None ms (TType_measure Measure.One) + | (_, TType_app (tc2, [ms2], nullness2)) when (tc2.IsMeasureableReprTycon && typeEquiv csenv.g sty1 (reduceTyconRefMeasureableOrProvided csenv.g tc2 [ms2])) -> + SolveTypeEqualsType csenv ndeep m2 trace None (TType_measure Measure.One) ms2 ++ (fun () -> + SolveNullnessEquiv csenv m2 trace ty1 ty2 (nullnessOfTy g sty1) nullness2 + ) - | TType_app (tc2, [ms], _), _ when (tc2.IsMeasureableReprTycon && typeEquiv csenv.g sty2 (reduceTyconRefMeasureableOrProvided csenv.g tc2 [ms])) -> - SolveTypeEqualsType csenv ndeep m2 trace None ms (TType_measure Measure.One) + | (TType_app (tc1, [ms1], nullness1), _) when (tc1.IsMeasureableReprTycon && typeEquiv csenv.g sty2 (reduceTyconRefMeasureableOrProvided csenv.g tc1 [ms1])) -> + SolveTypeEqualsType csenv ndeep m2 trace None ms1 (TType_measure Measure.One) ++ (fun () -> + SolveNullnessEquiv csenv m2 trace ty1 ty2 nullness1 (nullnessOfTy g sty2) + ) - | TType_app (tc1, l1, _), TType_app (tc2, l2, _) when tyconRefEq g tc1 tc2 -> - SolveTypeEqualsTypeEqns csenv ndeep m2 trace None l1 l2 + | TType_app (tc1, l1, nullness1), TType_app (tc2, l2, nullness2) when tyconRefEq g tc1 tc2 -> + SolveTypeEqualsTypeEqns csenv ndeep m2 trace None l1 l2 ++ (fun () -> + SolveNullnessEquiv csenv m2 trace ty1 ty2 nullness1 nullness2 + ) - | TType_app _, TType_app _ -> - localAbortD + | TType_app _, TType_app _ -> localAbortD | TType_tuple (tupInfo1, l1), TType_tuple (tupInfo2, l2) -> if evalTupInfoIsStruct tupInfo1 <> evalTupInfoIsStruct tupInfo2 then ErrorD (ConstraintSolverError(FSComp.SR.tcTupleStructMismatch(), csenv.m, m2)) else SolveTypeEqualsTypeEqns csenv ndeep m2 trace None l1 l2 - | TType_anon (anonInfo1, l1),TType_anon (anonInfo2, l2) -> - SolveAnonInfoEqualsAnonInfo csenv m2 anonInfo1 anonInfo2 ++ (fun () -> + | TType_anon (anonInfo1, l1),TType_anon (anonInfo2, l2) -> + SolveAnonInfoEqualsAnonInfo csenv m2 anonInfo1 anonInfo2 ++ (fun () -> SolveTypeEqualsTypeEqns csenv ndeep m2 trace None l1 l2) - | TType_fun (domainTy1, rangeTy1, _), TType_fun (domainTy2, rangeTy2, _) -> - SolveFunTypeEqn csenv ndeep m2 trace None domainTy1 domainTy2 rangeTy1 rangeTy2 + | TType_fun (domainTy1, rangeTy1, nullness1), TType_fun (domainTy2, rangeTy2, nullness2) -> + SolveFunTypeEqn csenv ndeep m2 trace None domainTy1 domainTy2 rangeTy1 rangeTy2 ++ (fun () -> + SolveNullnessEquiv csenv m2 trace ty1 ty2 nullness1 nullness2 + ) | TType_measure ms1, TType_measure ms2 -> UnifyMeasures csenv trace ms1 ms2 @@ -1234,7 +1365,9 @@ and SolveTypeEqualsTypeEqns csenv ndeep m2 trace cxsln origl1 origl2 = loop origl1 origl2 and SolveFunTypeEqn csenv ndeep m2 trace cxsln domainTy1 domainTy2 rangeTy1 rangeTy2 = trackErrors { - do! SolveTypeEqualsTypeKeepAbbrevsWithCxsln csenv ndeep m2 trace cxsln domainTy1 domainTy2 + // TODO NULLNESS: consider whether flipping the actual and expected in argument position + // causes other problems, e.g. better/worse diagnostics + do! SolveTypeEqualsTypeKeepAbbrevsWithCxsln csenv ndeep m2 trace cxsln domainTy2 domainTy1 return! SolveTypeEqualsTypeKeepAbbrevsWithCxsln csenv ndeep m2 trace cxsln rangeTy1 rangeTy2 } @@ -1257,41 +1390,56 @@ and SolveTypeSubsumesType (csenv: ConstraintSolverEnv) ndeep m2 (trace: Optional let denv = csenv.DisplayEnv match sty1, sty2 with - | TType_var (tp1, _), _ -> + | TType_var (tp1, nullness1) , _ -> match aenv.EquivTypars.TryFind tp1 with | Some tpTy1 -> SolveTypeSubsumesType csenv ndeep m2 trace cxsln tpTy1 ty2 | _ -> match sty2 with - | TType_var (r2, _) when typarEq tp1 r2 -> CompleteD - | TType_var (r, _) when not csenv.MatchingOnly -> SolveTyparSubtypeOfType csenv ndeep m2 trace r ty1 + | TType_var (r2, nullness2) when typarEq tp1 r2 -> + SolveNullnessEquiv csenv m2 trace ty1 ty2 nullness1 nullness2 + | TType_var (r2, nullness2) when not csenv.MatchingOnly -> + SolveTyparSubtypeOfType csenv ndeep m2 trace r2 ty1 ++ (fun () -> + let nullnessAfterSolution2 = combineNullness (nullnessOfTy g sty2) nullness2 + SolveNullnessSubsumesNullness csenv m2 trace ty1 ty2 nullness1 nullnessAfterSolution2 + ) | _ -> SolveTypeEqualsTypeKeepAbbrevsWithCxsln csenv ndeep m2 trace cxsln ty1 ty2 - | _, TType_var (r, _) when not csenv.MatchingOnly -> - SolveTyparSubtypeOfType csenv ndeep m2 trace r ty1 + | _, TType_var (r2, nullness2) when not csenv.MatchingOnly -> + SolveTyparSubtypeOfType csenv ndeep m2 trace r2 ty1 ++ (fun () -> + let nullnessAfterSolution2 = combineNullness (nullnessOfTy g sty2) nullness2 + SolveNullnessSubsumesNullness csenv m2 trace ty1 ty2 (nullnessOfTy g sty1) nullnessAfterSolution2 + ) | TType_tuple (tupInfo1, l1), TType_tuple (tupInfo2, l2) -> if evalTupInfoIsStruct tupInfo1 <> evalTupInfoIsStruct tupInfo2 then ErrorD (ConstraintSolverError(FSComp.SR.tcTupleStructMismatch(), csenv.m, m2)) else SolveTypeEqualsTypeEqns csenv ndeep m2 trace cxsln l1 l2 (* nb. can unify since no variance *) + | TType_fun (domainTy1, rangeTy1, nullness1), TType_fun (domainTy2, rangeTy2, nullness2) -> + // nb. can unify since no variance + SolveFunTypeEqn csenv ndeep m2 trace cxsln domainTy1 domainTy2 rangeTy1 rangeTy2 ++ (fun () -> + SolveNullnessSubsumesNullness csenv m2 trace ty1 ty2 nullness1 nullness2 + ) + | TType_anon (anonInfo1, l1), TType_anon (anonInfo2, l2) -> SolveAnonInfoEqualsAnonInfo csenv m2 anonInfo1 anonInfo2 ++ (fun () -> SolveTypeEqualsTypeEqns csenv ndeep m2 trace cxsln l1 l2) (* nb. can unify since no variance *) - | TType_fun (domainTy1, rangeTy1, _), TType_fun (domainTy2, rangeTy2, _) -> - SolveFunTypeEqn csenv ndeep m2 trace cxsln domainTy1 domainTy2 rangeTy1 rangeTy2 (* nb. can unify since no variance *) - | TType_measure ms1, TType_measure ms2 -> UnifyMeasures csenv trace ms1 ms2 // Enforce the identities float=float<1>, float32=float32<1> and decimal=decimal<1> - | _, TType_app (tc2, [ms], _) when (tc2.IsMeasureableReprTycon && typeEquiv csenv.g sty1 (reduceTyconRefMeasureableOrProvided csenv.g tc2 [ms])) -> - SolveTypeEqualsTypeKeepAbbrevsWithCxsln csenv ndeep m2 trace cxsln ms (TType_measure Measure.One) + | _, TType_app (tc2, [ms2], nullness2) when tc2.IsMeasureableReprTycon && typeEquiv csenv.g sty1 (reduceTyconRefMeasureableOrProvided csenv.g tc2 [ms2]) -> + SolveTypeEqualsTypeKeepAbbrevsWithCxsln csenv ndeep m2 trace cxsln ms2 (TType_measure Measure.One) ++ (fun () -> + SolveNullnessSubsumesNullness csenv m2 trace ty1 ty2 (nullnessOfTy g sty1) nullness2 + ) - | TType_app (tc2, [ms], _), _ when (tc2.IsMeasureableReprTycon && typeEquiv csenv.g sty2 (reduceTyconRefMeasureableOrProvided csenv.g tc2 [ms])) -> - SolveTypeEqualsTypeKeepAbbrevsWithCxsln csenv ndeep m2 trace cxsln ms (TType_measure Measure.One) + | TType_app (tc1, [ms1], nullness1), _ when tc1.IsMeasureableReprTycon && typeEquiv csenv.g sty2 (reduceTyconRefMeasureableOrProvided csenv.g tc1 [ms1]) -> + SolveTypeEqualsTypeKeepAbbrevsWithCxsln csenv ndeep m2 trace cxsln ms1 (TType_measure Measure.One) ++ (fun () -> + SolveNullnessSubsumesNullness csenv m2 trace ty1 ty2 nullness1 (nullnessOfTy g sty2) + ) // Special subsumption rule for byref tags - | TType_app (tc1, l1, _), TType_app (tc2, l2, _) when tyconRefEq g tc1 tc2 && g.byref2_tcr.CanDeref && tyconRefEq g g.byref2_tcr tc1 -> + | TType_app (tc1, l1, _) , TType_app (tc2, l2, _) when tyconRefEq g tc1 tc2 && g.byref2_tcr.CanDeref && tyconRefEq g g.byref2_tcr tc1 -> match l1, l2 with | [ h1; tag1 ], [ h2; tag2 ] -> trackErrors { do! SolveTypeEqualsType csenv ndeep m2 trace None h1 h2 @@ -1303,8 +1451,10 @@ and SolveTypeSubsumesType (csenv: ConstraintSolverEnv) ndeep m2 (trace: Optional } | _ -> SolveTypeEqualsTypeEqns csenv ndeep m2 trace cxsln l1 l2 - | TType_app (tc1, l1, _), TType_app (tc2, l2, _) when tyconRefEq g tc1 tc2 -> - SolveTypeEqualsTypeEqns csenv ndeep m2 trace cxsln l1 l2 + | TType_app (tc1, l1, nullness1) , TType_app (tc2, l2, nullness2) when tyconRefEq g tc1 tc2 -> + SolveTypeEqualsTypeEqns csenv ndeep m2 trace cxsln l1 l2 ++ (fun () -> + SolveNullnessSubsumesNullness csenv m2 trace ty1 ty2 nullness1 nullness2 + ) | TType_ucase (uc1, l1), TType_ucase (uc2, l2) when g.unionCaseRefEq uc1 uc2 -> SolveTypeEqualsTypeEqns csenv ndeep m2 trace cxsln l1 l2 @@ -1901,7 +2051,7 @@ and MemberConstraintSolutionOfMethInfo css m minfo minst staticTyOpt = match callMethInfoOpt, callExpr with | Some methInfo, Expr.Op (TOp.ILCall (_, _, _, _, NormalValUse, _, _, ilMethRef, _, methInst, _), [], args, m) when (args, (objArgVars@allArgVars)) ||> List.lengthsEqAndForall2 (fun a b -> match a with Expr.Val (v, _, _) -> valEq v.Deref b | _ -> false) -> - let declaringTy = ImportProvidedType amap m (methInfo.PApply((fun x -> x.DeclaringType), m)) + let declaringTy = ImportProvidedType amap m (methInfo.PApply((fun x -> nonNull x.DeclaringType), m)) if isILAppTy g declaringTy then let extOpt = None // EXTENSION METHODS FROM TYPE PROVIDERS: for extension methods coming from the type providers we would have something here. ILMethSln(declaringTy, extOpt, ilMethRef, methInst, staticTyOpt) @@ -2160,15 +2310,26 @@ and EnforceConstraintConsistency (csenv: ConstraintSolverEnv) ndeep m2 trace ret do! SolveTypeEqualsTypeKeepAbbrevs csenv ndeep m2 trace argsTy1 argsTy2 return! SolveTypeEqualsTypeKeepAbbrevs csenv ndeep m2 trace retTy1 retTy2 - | TyparConstraint.SupportsComparison _, TyparConstraint.IsDelegate _ - | TyparConstraint.IsDelegate _, TyparConstraint.SupportsComparison _ - | TyparConstraint.IsNonNullableStruct _, TyparConstraint.IsReferenceType _ + | TyparConstraint.SupportsComparison _, TyparConstraint.IsDelegate _ + | TyparConstraint.IsDelegate _ , TyparConstraint.SupportsComparison _ -> + return! ErrorD (Error(FSComp.SR.csDelegateComparisonConstraintInconsistent(), m)) + + | TyparConstraint.NotSupportsNull _, TyparConstraint.SupportsNull _ + | TyparConstraint.SupportsNull _, TyparConstraint.NotSupportsNull _ -> + return! ErrorD (Error(FSComp.SR.csNullNotNullConstraintInconsistent(), m)) + + | TyparConstraint.SupportsNull _, TyparConstraint.IsNonNullableStruct _ + | TyparConstraint.IsNonNullableStruct _, TyparConstraint.SupportsNull _ -> + return! ErrorD (Error(FSComp.SR.csStructNullConstraintInconsistent(), m)) + + | TyparConstraint.IsNonNullableStruct _, TyparConstraint.IsReferenceType _ | TyparConstraint.IsReferenceType _, TyparConstraint.IsNonNullableStruct _ -> return! ErrorD (Error(FSComp.SR.csStructConstraintInconsistent(), m)) | TyparConstraint.SupportsComparison _, TyparConstraint.SupportsComparison _ | TyparConstraint.SupportsEquality _, TyparConstraint.SupportsEquality _ | TyparConstraint.SupportsNull _, TyparConstraint.SupportsNull _ + | TyparConstraint.NotSupportsNull _, TyparConstraint.NotSupportsNull _ | TyparConstraint.IsNonNullableStruct _, TyparConstraint.IsNonNullableStruct _ | TyparConstraint.IsUnmanaged _, TyparConstraint.IsUnmanaged _ | TyparConstraint.IsReferenceType _, TyparConstraint.IsReferenceType _ @@ -2205,6 +2366,7 @@ and CheckConstraintImplication (csenv: ConstraintSolverEnv) tpc1 tpc2 = // comparison implies equality | TyparConstraint.SupportsComparison _, TyparConstraint.SupportsEquality _ | TyparConstraint.SupportsNull _, TyparConstraint.SupportsNull _ + | TyparConstraint.NotSupportsNull _, TyparConstraint.NotSupportsNull _ | TyparConstraint.IsNonNullableStruct _, TyparConstraint.IsNonNullableStruct _ | TyparConstraint.IsUnmanaged _, TyparConstraint.IsUnmanaged _ | TyparConstraint.IsReferenceType _, TyparConstraint.IsReferenceType _ @@ -2295,20 +2457,156 @@ and AddConstraint (csenv: ConstraintSolverEnv) ndeep m2 trace tp newConstraint () } -and SolveTypeUseSupportsNull (csenv: ConstraintSolverEnv) ndeep m2 trace ty = +and SolveNullnessSupportsNull (csenv:ConstraintSolverEnv) ndeep m2 (trace: OptionalTrace) ty nullness = trackErrors { let g = csenv.g let m = csenv.m let denv = csenv.DisplayEnv - match tryDestTyparTy g ty with - | ValueSome destTypar -> - AddConstraint csenv ndeep m2 trace destTypar (TyparConstraint.SupportsNull m) - | ValueNone -> - if TypeSatisfiesNullConstraint g m ty then CompleteD else + match nullness with + | Nullness.Variable nv -> + if nv.IsSolved then + do! SolveNullnessSupportsNull csenv ndeep m2 trace ty nv.Solution + else + trace.Exec (fun () -> nv.Set KnownWithNull) (fun () -> nv.Unset()) + | Nullness.Known n1 -> + match n1 with + | NullnessInfo.AmbivalentToNull -> () + | NullnessInfo.WithNull -> () + | NullnessInfo.WithoutNull -> + if g.checkNullness then + if not (isObjTy g ty) then + return! WarnD(ConstraintSolverNullnessWarningWithType(denv, ty, n1, m, m2)) + } + +and SolveTypeSupportsNullCore (csenv:ConstraintSolverEnv) ndeep m2 trace ty = trackErrors { + let g = csenv.g + let m = csenv.m + let denv = csenv.DisplayEnv + if TypeNullIsExtraValueNew g m ty then + () + else match ty with | NullableTy g _ -> - ErrorD (ConstraintSolverError(FSComp.SR.csNullableTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2)) + return! ErrorD (ConstraintSolverError(FSComp.SR.csNullableTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2)) | _ -> - ErrorD (ConstraintSolverError(FSComp.SR.csTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2)) + // If langFeatureNullness is on then solve, maybe give warnings + if g.langFeatureNullness then + let nullness = nullnessOfTy g ty + do! SolveNullnessSupportsNull csenv ndeep m2 trace ty nullness + + // If langFeatureNullness or checkNullness are off give the same errors as F# 4.5 + if not g.langFeatureNullness || not g.checkNullness then + if not (TypeNullIsExtraValue g m ty) then + return! ErrorD (ConstraintSolverError(FSComp.SR.csTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2)) + } + +// This version prefers to constrain a type parameter definiton +and SolveTypeDefnSupportsNull (csenv:ConstraintSolverEnv) ndeep m2 trace ty = + let g = csenv.g + let m = csenv.m + let denv = csenv.DisplayEnv + if g.langFeatureNullness then + match stripTyparEqns ty with + // If you set a type variable constrained with a T: null to U? then you don't induce an inference constraint + // of U: null. + // TODO: what about Obsolete? + | TType_var(_, nullness) when nullness.Evaluate() = NullnessInfo.WithNull -> CompleteD + | _ -> + match tryDestTyparTy g ty with + | ValueSome tp -> + AddConstraint csenv ndeep m2 trace tp (TyparConstraint.SupportsNull m) + | ValueNone -> + SolveTypeSupportsNullCore csenv ndeep m2 trace ty + else + match tryDestTyparTy g ty with + | ValueSome destTypar -> + AddConstraint csenv ndeep m2 trace destTypar (TyparConstraint.SupportsNull m) + | ValueNone -> + if TypeNullIsExtraValue g m ty then CompleteD else + match ty with + | NullableTy g _ -> + ErrorD (ConstraintSolverError(FSComp.SR.csNullableTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2)) + | _ -> + ErrorD (ConstraintSolverError(FSComp.SR.csTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2)) + +// This version prefers to constrain the nullness annotation for a type annotation usage +and SolveTypeUseSupportsNull (csenv:ConstraintSolverEnv) ndeep m2 trace ty = + let g = csenv.g + let m = csenv.m + let denv = csenv.DisplayEnv + if g.langFeatureNullness then + match stripTyparEqns ty with + | TType_var(tp, nullness) -> + AddConstraint csenv ndeep m2 trace tp (TyparConstraint.IsReferenceType m) ++ (fun () -> + SolveNullnessSupportsNull csenv ndeep m2 trace ty nullness) + | _ -> + SolveTypeSupportsNullCore csenv ndeep m2 trace ty + else + match tryDestTyparTy g ty with + | ValueSome destTypar -> + AddConstraint csenv ndeep m2 trace destTypar (TyparConstraint.SupportsNull m) + | ValueNone -> + if TypeNullIsExtraValue g m ty then CompleteD else + match ty with + | NullableTy g _ -> + ErrorD (ConstraintSolverError(FSComp.SR.csNullableTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2)) + | _ -> + ErrorD (ConstraintSolverError(FSComp.SR.csTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2)) + +and SolveNullnessNotSupportsNull (csenv:ConstraintSolverEnv) ndeep m2 (trace: OptionalTrace) ty nullness = trackErrors { + let g = csenv.g + let m = csenv.m + let denv = csenv.DisplayEnv + match nullness with + | Nullness.Variable nv -> + if nv.IsSolved then + do! SolveNullnessNotSupportsNull csenv ndeep m2 trace ty nv.Solution + else + trace.Exec (fun () -> nv.Set KnownWithoutNull) (fun () -> nv.Unset()) + | Nullness.Known n1 -> + match n1 with + | NullnessInfo.AmbivalentToNull -> () + | NullnessInfo.WithoutNull -> () + | NullnessInfo.WithNull -> + if g.checkNullness then + if not (isObjTy g ty) then + return! WarnD(ConstraintSolverNonNullnessWarningWithType(denv, ty, n1, m, m2)) + else + if TypeNullIsExtraValue g m ty then + return! ErrorD (ConstraintSolverError(FSComp.SR.csTypeHasNullAsExtraValue(NicePrint.minimalStringOfType denv ty), m, m2)) + } + +and SolveTypeNotSupportsNullCore (csenv:ConstraintSolverEnv) ndeep m2 trace ty = trackErrors { + let g = csenv.g + let m = csenv.m + let denv = csenv.DisplayEnv + if TypeNullIsTrueValue g ty then + // We can only give warnings here as F# 5.0 introduces these constraints into existing + // code via Option.ofObj and Option.toObj + do! WarnD (ConstraintSolverError(FSComp.SR.csTypeHasNullAsTrueValue(NicePrint.minimalStringOfType denv ty), m, m2)) + elif TypeNullIsExtraValueNew g m ty then + if g.checkNullness || TypeNullIsExtraValue g m ty then + do! WarnD (ConstraintSolverError(FSComp.SR.csTypeHasNullAsExtraValue(NicePrint.minimalStringOfType denv ty), m, m2)) + else + if g.checkNullness then + let nullness = nullnessOfTy g ty + do! SolveNullnessNotSupportsNull csenv ndeep m2 trace ty nullness + } + +// This version prefers to constrain a type parameter definiton +and SolveTypeDefnNotSupportsNull (csenv:ConstraintSolverEnv) ndeep m2 trace ty = + let g = csenv.g + let m = csenv.m + //match stripTyparEqns ty with + //// If you set a type variable constrained with a T: __notnull to U then you don't induce an inference constraint + //// of U: __notnull. + //// TODO: what about Obsolete? + //| TType_var(_, nullness) when nullness.TryEvaluate() = Some NullnessInfo.WithoutNull || nullness.TryEvaluate() = Some NullnessInfo.AmbivalentToNull -> CompleteD + //| _ -> + match tryDestTyparTy g ty with + | ValueSome tp -> + AddConstraint csenv ndeep m2 trace tp (TyparConstraint.NotSupportsNull m) + | ValueNone -> + SolveTypeNotSupportsNullCore csenv ndeep m2 trace ty and SolveTypeSupportsComparison (csenv: ConstraintSolverEnv) ndeep m2 trace ty = let g = csenv.g @@ -2500,7 +2798,7 @@ and SolveTypeRequiresDefaultConstructor (csenv: ConstraintSolverEnv) ndeep m2 tr | ValueSome tp -> AddConstraint csenv ndeep m2 trace tp (TyparConstraint.RequiresDefaultConstructor m) | _ -> - if isStructTy g ty then + if isStructTy g ty && (isStructTupleTy g ty || isStructAnonRecdTy g ty || TypeHasDefaultValue g m ty) then if isStructTupleTy g ty then destStructTupleTy g ty |> IterateD (SolveTypeRequiresDefaultValue csenv ndeep m trace) elif isStructAnonRecdTy g ty then @@ -2603,7 +2901,9 @@ and CanMemberSigsMatchUpToCheck else return! ErrorD(Error (FSComp.SR.csMemberIsNotInstance(minfo.LogicalName), m)) else - return! MapCombineTDC2D subsumeTypes calledObjArgTys callerObjArgTys + // The object types must be non-null + let nonNullCalledObjArgTys = calledObjArgTys |> List.map (replaceNullnessOfTy g.knownWithoutNull) + return! MapCombineTDC2D subsumeTypes nonNullCalledObjArgTys callerObjArgTys } let! usesTDC3 = @@ -3544,6 +3844,20 @@ let AddCxMethodConstraint denv css m trace traitInfo = (fun res -> ErrorD (ErrorFromAddingConstraint(denv, res, m))) |> RaiseOperationResult +let AddCxTypeDefnSupportsNull denv css m trace ty = + let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m denv + PostponeOnFailedMemberConstraintResolution csenv trace + (fun csenv -> SolveTypeDefnSupportsNull csenv 0 m trace ty) + (fun res -> ErrorD (ErrorFromAddingConstraint(denv, res, m))) + |> RaiseOperationResult + +let AddCxTypeDefnNotSupportsNull denv css m trace ty = + let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m denv + PostponeOnFailedMemberConstraintResolution csenv trace + (fun csenv -> SolveTypeDefnNotSupportsNull csenv 0 m trace ty) + (fun res -> ErrorD (ErrorFromAddingConstraint(denv, res, m))) + |> RaiseOperationResult + let AddCxTypeUseSupportsNull denv css m trace ty = let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m denv PostponeOnFailedMemberConstraintResolution csenv trace diff --git a/src/Compiler/Checking/ConstraintSolver.fsi b/src/Compiler/Checking/ConstraintSolver.fsi index d6cc309c6b5..a0943c752bf 100644 --- a/src/Compiler/Checking/ConstraintSolver.fsi +++ b/src/Compiler/Checking/ConstraintSolver.fsi @@ -174,6 +174,26 @@ exception ConstraintSolverTypesNotInSubsumptionRelation of exception ConstraintSolverMissingConstraint of displayEnv: DisplayEnv * Typar * TyparConstraint * range * range +exception ConstraintSolverNullnessWarningEquivWithTypes of + DisplayEnv * + TType * + TType * + NullnessInfo * + NullnessInfo * + range * + range + +exception ConstraintSolverNullnessWarningWithTypes of + DisplayEnv * + TType * + TType * + NullnessInfo * + NullnessInfo * + range * + range + +exception ConstraintSolverNullnessWarningWithType of DisplayEnv * TType * NullnessInfo * range * range +exception ConstraintSolverNonNullnessWarningWithType of DisplayEnv * TType * NullnessInfo * range * range exception ConstraintSolverError of string * range * range exception ErrorFromApplyingDefault of @@ -296,6 +316,10 @@ val AddCxTypeMustSubsumeTypeMatchingOnlyUndoIfFailed: val AddCxMethodConstraint: DisplayEnv -> ConstraintSolverState -> range -> OptionalTrace -> TraitConstraintInfo -> unit +val AddCxTypeDefnNotSupportsNull: DisplayEnv -> ConstraintSolverState -> range -> OptionalTrace -> TType -> unit + +val AddCxTypeDefnSupportsNull: DisplayEnv -> ConstraintSolverState -> range -> OptionalTrace -> TType -> unit + val AddCxTypeUseSupportsNull: DisplayEnv -> ConstraintSolverState -> range -> OptionalTrace -> TType -> unit val AddCxTypeMustSupportComparison: DisplayEnv -> ConstraintSolverState -> range -> OptionalTrace -> TType -> unit diff --git a/src/Compiler/Checking/InfoReader.fs b/src/Compiler/Checking/InfoReader.fs index 5a6b63722cc..769e38d3227 100644 --- a/src/Compiler/Checking/InfoReader.fs +++ b/src/Compiler/Checking/InfoReader.fs @@ -683,7 +683,7 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this = // a decent hash function for these. canMemoize=(fun (_flags, _: range, ty) -> match stripTyEqns g ty with - | TType_app(tcref, [], _) -> tcref.TypeContents.tcaug_closed + | TType_app(tcref, [], _) -> tcref.TypeContents.tcaug_closed // TODO NULLNESS: consider whether ignoring _nullness is valid here | _ -> false), keyComparer= @@ -692,13 +692,13 @@ type InfoReader(g: TcGlobals, amap: Import.ImportMap) as this = // Ignoring the ranges - that's OK. flagsEq.Equals(flags1, flags2) && match stripTyEqns g ty1, stripTyEqns g ty2 with - | TType_app(tcref1, [], _), TType_app(tcref2, [], _) -> tyconRefEq g tcref1 tcref2 + | TType_app(tcref1, [], _),TType_app(tcref2, [], _) -> tyconRefEq g tcref1 tcref2 // TODO NULLNESS: consider whether ignoring _nullness is valid here | _ -> false member _.GetHashCode((flags, _, ty)) = // Ignoring the ranges - that's OK. flagsEq.GetHashCode flags + (match stripTyEqns g ty with - | TType_app(tcref, [], _) -> hash tcref.LogicalName + | TType_app(tcref, [], _) -> hash tcref.LogicalName // TODO NULLNESS: consider whether ignoring _nullness is valid here | _ -> 0) }) let FindImplicitConversionsUncached (ad, m, ty) = @@ -1071,7 +1071,7 @@ let TryFindMetadataInfoOfExternalEntityRef (infoReader: InfoReader) m eref = // Generalize to get a formal signature let formalTypars = eref.Typars m let formalTypeInst = generalizeTypars formalTypars - let ty = TType_app(eref, formalTypeInst, 0uy) + let ty = TType_app(eref, formalTypeInst, KnownAmbivalentToNull) if isILAppTy g ty then let formalTypeInfo = ILTypeInfo.FromType g ty Some(nlref.Ccu.FileName, formalTypars, formalTypeInfo) diff --git a/src/Compiler/Checking/MethodCalls.fs b/src/Compiler/Checking/MethodCalls.fs index ceb13545da8..e8d6850bc77 100644 --- a/src/Compiler/Checking/MethodCalls.fs +++ b/src/Compiler/Checking/MethodCalls.fs @@ -1183,8 +1183,14 @@ let BuildMethodCall tcVal g amap isMutable m isProp minfo valUseFlags minst objA // Build a 'call' to a struct default constructor | DefaultStructCtor (g, ty) -> - if not (TypeHasDefaultValue g m ty) then - errorR(Error(FSComp.SR.tcDefaultStructConstructorCall(), m)) + if g.langFeatureNullness then + if not (TypeHasDefaultValueNew g m ty) && not (TypeHasDefaultValue g m ty) then + errorR(Error(FSComp.SR.tcDefaultStructConstructorCall(), m)) + if g.checkNullness && not (TypeHasDefaultValueNew g m ty) then + warning(Error(FSComp.SR.tcDefaultStructConstructorCallNulls(), m)) + else + if not (TypeHasDefaultValue g m ty) then + errorR(Error(FSComp.SR.tcDefaultStructConstructorCall(), m)) mkDefault (m, ty), ty) let ILFieldStaticChecks g amap infoReader ad m (finfo : ILFieldInfo) = diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index 6fa70c26fc7..0fde8caa167 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -1568,7 +1568,7 @@ let AddDeclaredTyparsToNameEnv check nenv typars = /// a fresh set of inference type variables for the type parameters. let FreshenTycon (ncenv: NameResolver) m (tcref: TyconRef) = let tinst = ncenv.InstantiationGenerator m (tcref.Typars m) - let improvedTy = ncenv.g.decompileType tcref tinst + let improvedTy = ncenv.g.decompileType tcref tinst ncenv.g.knownWithoutNull improvedTy /// Convert a reference to a named type into a type that includes @@ -1576,7 +1576,7 @@ let FreshenTycon (ncenv: NameResolver) m (tcref: TyconRef) = let FreshenTyconWithEnclosingTypeInst (ncenv: NameResolver) m (tinstEnclosing: TypeInst) (tcref: TyconRef) = let tps = ncenv.InstantiationGenerator m (tcref.Typars m) let tinst = List.skip tinstEnclosing.Length tps - let improvedTy = ncenv.g.decompileType tcref (tinstEnclosing @ tinst) + let improvedTy = ncenv.g.decompileType tcref (tinstEnclosing @ tinst) ncenv.g.knownWithoutNull improvedTy /// Convert a reference to a union case into a UnionCaseInfo that includes diff --git a/src/Compiler/Checking/NicePrint.fs b/src/Compiler/Checking/NicePrint.fs index 2ab993e369a..21196099f96 100644 --- a/src/Compiler/Checking/NicePrint.fs +++ b/src/Compiler/Checking/NicePrint.fs @@ -769,6 +769,9 @@ module PrintTypes = | TyparConstraint.SupportsNull _ -> [wordL (tagKeyword "null") |> longConstraintPrefix] + | TyparConstraint.NotSupportsNull _ -> + [(wordL (tagKeyword "__notnull") (* ^^ wordL(tagKeyword "null") *) ) |> longConstraintPrefix] + | TyparConstraint.IsNonNullableStruct _ -> if denv.shortConstraints then [wordL (tagText "value type")] @@ -874,6 +877,12 @@ module PrintTypes = | [] -> tcL | [arg] -> layoutTypeWithInfoAndPrec denv env 2 arg ^^ tcL | args -> bracketIfL (prec <= 1) (bracketL (layoutTypesWithInfoAndPrec denv env 2 (sepL (tagPunctuation ",")) args) --- tcL) + + and layoutNullness part2 (nullness: Nullness) = + match nullness.Evaluate() with + | NullnessInfo.WithNull -> part2 ^^ wordL (tagText "__withnull") + | NullnessInfo.WithoutNull -> part2 + | NullnessInfo.AmbivalentToNull -> part2 ^^ wordL (tagText "__maybenull") // TODO NULLNESS: emit this optionally ^^ wordL (tagText "%") /// Layout a type, taking precedence into account to insert brackets where needed and layoutTypeWithInfoAndPrec denv env prec ty = @@ -934,8 +943,10 @@ module PrintTypes = bracketIfL (prec <= 4) funcTyL // Layout a type variable . - | TType_var (r, _) -> - layoutTyparRefWithInfo denv env r + | TType_var (r, nullness) -> + let part1 = layoutTyparRefWithInfo denv env r + let part2 = layoutNullness part1 nullness + part2 | TType_measure unt -> layoutMeasure denv unt @@ -2684,7 +2695,7 @@ let minimalStringsOfTwoTypes denv ty1 ty2 = let denv = denv.SetOpenPaths [] let denv = { denv with includeStaticParametersInTypeNames=true } let makeName t = - let assemblyName = PrintTypes.layoutAssemblyName denv t |> function | null | "" -> "" | name -> sprintf " (%s)" name + let assemblyName = PrintTypes.layoutAssemblyName denv t |> function "" -> "" | name -> sprintf " (%s)" name sprintf "%s%s" (stringOfTy denv t) assemblyName (makeName ty1, makeName ty2, stringOfTyparConstraints denv tpcs) diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 145c7e0799c..cf855cd1ea7 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -428,6 +428,7 @@ and CheckTypeConstraintDeep cenv f g env x = | TyparConstraint.SupportsComparison _ | TyparConstraint.SupportsEquality _ | TyparConstraint.SupportsNull _ + | TyparConstraint.NotSupportsNull _ | TyparConstraint.IsNonNullableStruct _ | TyparConstraint.IsUnmanaged _ | TyparConstraint.IsReferenceType _ @@ -2532,13 +2533,34 @@ let CheckEntityDefn cenv env (tycon: Entity) = // Check fields. We check these late because we have to have first checked that the structs are // free of cycles - if tycon.IsStructOrEnumTycon then + if g.langFeatureNullness then for f in tycon.AllInstanceFieldsAsList do + let m = f.Range // Check if it's marked unsafe let zeroInitUnsafe = TryFindFSharpBoolAttribute g g.attrib_DefaultValueAttribute f.FieldAttribs if zeroInitUnsafe = Some true then - if not (TypeHasDefaultValue g m ty) then - errorR(Error(FSComp.SR.chkValueWithDefaultValueMustHaveDefaultValue(), m)) + let ty = f.FormalType + if not (TypeHasDefaultValueNew g m ty) && not (TypeHasDefaultValue g m ty) then + if tycon.IsStructOrEnumTycon then + // Under F# 4.5 we gave a hard error for this case so we can give it now + errorR(Error(FSComp.SR.chkValueWithDefaultValueMustHaveDefaultValue(), m)) + else + if g.checkNullness then + // Under F# 5.0 rules with checkNullness we can now give a warning for this case + warning(Error(FSComp.SR.chkValueWithDefaultValueMustHaveDefaultValue(), m)) + + elif g.checkNullness && not (TypeHasDefaultValueNew g m ty) then + // Under F# 5.0 rules with checkNullness we can now give a warning for this case + warning(Error(FSComp.SR.chkValueWithDefaultValueMustHaveDefaultValueNulls(), m)) + // These are the F# 4.5 rules, mistakenly only applied to structs + elif tycon.IsStructOrEnumTycon then + for f in tycon.AllInstanceFieldsAsList do + let m = f.Range + // Check if it's marked unsafe + let zeroInitUnsafe = TryFindFSharpBoolAttribute g g.attrib_DefaultValueAttribute f.FieldAttribs + if zeroInitUnsafe = Some true then + if not (TypeHasDefaultValue g m ty) then + errorR(Error(FSComp.SR.chkValueWithDefaultValueMustHaveDefaultValue(), m)) // Check type abbreviations match tycon.TypeAbbrev with diff --git a/src/Compiler/Checking/SignatureConformance.fs b/src/Compiler/Checking/SignatureConformance.fs index 2aa8bf924db..921b6b0fc64 100644 --- a/src/Compiler/Checking/SignatureConformance.fs +++ b/src/Compiler/Checking/SignatureConformance.fs @@ -230,7 +230,7 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) = else let aNull2 = TypeNullIsExtraValue g m (generalizedTyconRef g (mkLocalTyconRef implTycon)) - let fNull2 = TypeNullIsExtraValue g m (generalizedTyconRef g (mkLocalTyconRef implTycon)) + let fNull2 = TypeNullIsExtraValue g m (generalizedTyconRef g (mkLocalTyconRef implTycon)) // TODO: should be sigTycon, raises extra errors if aNull2 && not fNull2 then errorR(Error(FSComp.SR.DefinitionsInSigAndImplNotCompatibleImplementationSaysNull2(implTycon.TypeOrMeasureKind.ToString(), implTycon.DisplayName), m)) false diff --git a/src/Compiler/Checking/TypeHierarchy.fs b/src/Compiler/Checking/TypeHierarchy.fs index f6949c707d8..43302b24b46 100644 --- a/src/Compiler/Checking/TypeHierarchy.fs +++ b/src/Compiler/Checking/TypeHierarchy.fs @@ -46,7 +46,7 @@ let GetSuperTypeOfType g amap m ty = #if !NO_TYPEPROVIDERS | ProvidedTypeMetadata info -> let st = info.ProvidedType - let superOpt = st.PApplyOption((fun st -> match st.BaseType with null -> None | t -> Some t), m) + let superOpt = st.PApplyOption((fun st -> match st.BaseType with null -> None | t -> Some (nonNull t)), m) match superOpt with | None -> None | Some super -> Some(ImportProvidedType amap m super) @@ -81,7 +81,13 @@ let GetSuperTypeOfType g amap m ty = else None - resBeforeNull + match resBeforeNull with + | Some superTy -> + let nullness = nullnessOfTy g ty + let superTyWithNull = addNullnessToTy nullness superTy + Some superTyWithNull + | None -> + None /// Make a type for System.Collections.Generic.IList let mkSystemCollectionsGenericIListTy (g: TcGlobals) ty = @@ -264,6 +270,7 @@ let FoldHierarchyOfTypeAux followInterfaces allowMultiIntfInst skipUnref visitor | TyparConstraint.IsEnum _ | TyparConstraint.IsDelegate _ | TyparConstraint.SupportsNull _ + | TyparConstraint.NotSupportsNull _ | TyparConstraint.IsNonNullableStruct _ | TyparConstraint.IsUnmanaged _ | TyparConstraint.IsReferenceType _ @@ -395,6 +402,8 @@ let CopyTyparConstraints m tprefInst (tporig: Typar) = TyparConstraint.IsEnum (instType tprefInst underlyingTy, m) | TyparConstraint.SupportsComparison _ -> TyparConstraint.SupportsComparison m + | TyparConstraint.NotSupportsNull _ -> + TyparConstraint.NotSupportsNull m | TyparConstraint.SupportsEquality _ -> TyparConstraint.SupportsEquality m | TyparConstraint.IsDelegate(argTys, retTy, _) -> diff --git a/src/Compiler/Checking/TypeRelations.fs b/src/Compiler/Checking/TypeRelations.fs index 90d5bed1fcb..c12e6e7e8a4 100644 --- a/src/Compiler/Checking/TypeRelations.fs +++ b/src/Compiler/Checking/TypeRelations.fs @@ -156,7 +156,9 @@ let ChooseTyparSolutionAndRange (g: TcGlobals) amap (tp:Typar) = errorR(Error(FSComp.SR.typrelCannotResolveAmbiguityInPrintf(), m)) maxTy, m | TyparConstraint.SupportsNull m -> - maxTy, m + addNullnessToTy KnownWithNull maxTy, m + | TyparConstraint.NotSupportsNull m -> + maxTy, m // NOTE: this doesn't "force" non-nullness, since it is the default choice in 'obj' or 'int' | TyparConstraint.SupportsComparison m -> join m g.mk_IComparable_ty, m | TyparConstraint.SupportsEquality m -> diff --git a/src/Compiler/Checking/import.fs b/src/Compiler/Checking/import.fs index bf6c9e73006..a299ec893fe 100644 --- a/src/Compiler/Checking/import.fs +++ b/src/Compiler/Checking/import.fs @@ -163,8 +163,24 @@ let CanImportILTypeRef (env: ImportMap) m (tref: ILTypeRef) = /// /// Prefer the F# abbreviation for some built-in types, e.g. 'string' rather than /// 'System.String', since we prefer the F# abbreviation to the .NET equivalents. -let ImportTyconRefApp (env: ImportMap) tcref tyargs = - env.g.improveType tcref tyargs +let ImportTyconRefApp (env: ImportMap) tcref tyargs nullness = + env.g.improveType tcref tyargs nullness + +let ImportNullness (g: TcGlobals) = + ignore g + // if g.langFeatureNullness && g.assumeNullOnImport then + // KnownWithNull + // else + // TODO NULLNESS + KnownAmbivalentToNull + +let ImportNullnessForTyconRef (g: TcGlobals) (m: range) (tcref: TyconRef) = + ignore (g, tcref, m) + // if g.langFeatureNullness && g.assumeNullOnImport && TyconRefNullIsExtraValue g m tcref then + // KnownWithNull + // else + // TODO NULLNESS + KnownAmbivalentToNull /// Import an IL type as an F# type. let rec ImportILType (env: ImportMap) m tinst ty = @@ -175,12 +191,14 @@ let rec ImportILType (env: ImportMap) m tinst ty = | ILType.Array(bounds, ty) -> let n = bounds.Rank let elemTy = ImportILType env m tinst ty - mkArrayTy env.g n elemTy m + let nullness = ImportNullness env.g + mkArrayTy env.g n nullness elemTy m | ILType.Boxed tspec | ILType.Value tspec -> let tcref = ImportILTypeRef env m tspec.TypeRef let inst = tspec.GenericArgs |> List.map (ImportILType env m tinst) - ImportTyconRefApp env tcref inst + let nullness = ImportNullnessForTyconRef env.g m tcref + ImportTyconRefApp env tcref inst nullness | ILType.Byref ty -> mkByrefTy env.g (ImportILType env m tinst ty) @@ -192,6 +210,7 @@ let rec ImportILType (env: ImportMap) m tinst ty = | ILType.Modified(_, _, ty) -> // All custom modifiers are ignored + // NULLNESS TODO: pick up the optional attributes at this point and fold the array of nullness information into the conversion ImportILType env m tinst ty | ILType.TypeVar u16 -> @@ -200,7 +219,9 @@ let rec ImportILType (env: ImportMap) m tinst ty = List.item (int u16) tinst with _ -> error(Error(FSComp.SR.impNotEnoughTypeParamsInScopeWhileImporting(), m)) - ty + let nullness = ImportNullness env.g + let tyWithNullness = addNullnessToTy nullness ty + tyWithNullness /// Determines if an IL type can be imported as an F# type let rec CanImportILType (env: ImportMap) m ty = @@ -286,7 +307,8 @@ let rec ImportProvidedType (env: ImportMap) (m: range) (* (tinst: TypeInst) *) ( let g = env.g if st.PUntaint((fun st -> st.IsArray), m) then let elemTy = ImportProvidedType env m (* tinst *) (st.PApply((fun st -> st.GetElementType()), m)) - mkArrayTy g (st.PUntaint((fun st -> st.GetArrayRank()), m)) elemTy m + let nullness = ImportNullness env.g + mkArrayTy g (st.PUntaint((fun st -> st.GetArrayRank()), m)) nullness elemTy m elif st.PUntaint((fun st -> st.IsByRef), m) then let elemTy = ImportProvidedType env m (* tinst *) (st.PApply((fun st -> st.GetElementType()), m)) mkByrefTy g elemTy @@ -358,7 +380,9 @@ let rec ImportProvidedType (env: ImportMap) (m: range) (* (tinst: TypeInst) *) ( else genericArg) - ImportTyconRefApp env tcref genericArgs + let nullness = ImportNullnessForTyconRef env.g m tcref + + ImportTyconRefApp env tcref genericArgs nullness /// Import a provided method reference as an Abstract IL method reference let ImportProvidedMethodBaseAsILMethodRef (env: ImportMap) (m: range) (mbase: Tainted) = diff --git a/src/Compiler/Checking/infos.fs b/src/Compiler/Checking/infos.fs index 2fe6031aeae..e34858a4b73 100644 --- a/src/Compiler/Checking/infos.fs +++ b/src/Compiler/Checking/infos.fs @@ -395,7 +395,7 @@ let OptionalArgInfoOfProvidedParameter (amap: ImportMap) m (provParam : Tainted< let GetAndSanityCheckProviderMethod m (mi: Tainted<'T :> ProvidedMemberInfo>) (get : 'T -> ProvidedMethodInfo MaybeNull) err = match mi.PApply((fun mi -> (get mi :> ProvidedMethodBase MaybeNull)),m) with | Tainted.Null -> error(Error(err(mi.PUntaint((fun mi -> mi.Name),m),mi.PUntaint((fun mi -> (nonNull mi.DeclaringType).Name), m)), m)) // TODO NULLNESS: type isntantiation should not be needed - | meth -> meth + | Tainted.NonNull meth -> meth /// Try to get an arbitrary ProvidedMethodInfo associated with a property. let ArbitraryMethodInfoOfPropertyInfo (pi: Tainted) m = @@ -1262,7 +1262,7 @@ type MethInfo = // For non-generic type providers there is no difference let formalParams = [ [ for p in mi.PApplyArray((fun mi -> mi.GetParameters()), "GetParameters", m) do - let paramName = p.PUntaint((fun p -> match p.Name with null -> None | s -> Some s), m) + let paramName = p.PUntaint((fun p -> match p.Name with "" -> None | s -> Some s), m) let paramTy = ImportProvidedType amap m (p.PApply((fun p -> p.ParameterType), m)) let isIn, isOut, isOptional = p.PUntaint((fun p -> p.IsIn, p.IsOut, p.IsOptional), m) yield TSlotParam(paramName, paramTy, isIn, isOut, isOptional, []) ] ] @@ -1291,7 +1291,7 @@ type MethInfo = [ [for p in mi.PApplyArray((fun mi -> mi.GetParameters()), "GetParameters", m) do let paramName = match p.PUntaint((fun p -> p.Name), m) with - | null -> None + | "" -> None | name -> Some (mkSynId m name) let paramTy = match p.PApply((fun p -> p.ParameterType), m) with @@ -1509,7 +1509,7 @@ type RecdFieldInfo = member x.FieldType = actualTyOfRecdFieldRef x.RecdFieldRef x.TypeInst /// Get the enclosing (declaring) type of the field in an F#-declared record, class or struct type - member x.DeclaringType = TType_app (x.RecdFieldRef.TyconRef, x.TypeInst, 0uy) + member x.DeclaringType = TType_app (x.RecdFieldRef.TyconRef, x.TypeInst, KnownWithoutNull) // TODO NULLNESS - qualify this override x.ToString() = x.TyconRef.ToString() + "::" + x.LogicalName @@ -1938,7 +1938,7 @@ type PropInfo = #if !NO_TYPEPROVIDERS | ProvidedProp (_, pi, m) -> [ for p in pi.PApplyArray((fun pi -> pi.GetIndexParameters()), "GetIndexParameters", m) do - let paramName = p.PUntaint((fun p -> match p.Name with null -> None | s -> Some (mkSynId m s)), m) + let paramName = p.PUntaint((fun p -> match p.Name with "" -> None | s -> Some (mkSynId m s)), m) let paramTy = ImportProvidedType amap m (p.PApply((fun p -> p.ParameterType), m)) yield ParamNameAndType(paramName, paramTy) ] #endif diff --git a/src/Compiler/DependencyManager/DependencyProvider.fs b/src/Compiler/DependencyManager/DependencyProvider.fs index f80969b3880..b2ba19f5615 100644 --- a/src/Compiler/DependencyManager/DependencyProvider.fs +++ b/src/Compiler/DependencyManager/DependencyProvider.fs @@ -124,7 +124,9 @@ type IResolveDependenciesResult = /// #I @"c:\somepath\to\packages\1.1.1\ResolvedPackage" abstract Roots: seq +#if NO_CHECKNULLS [] +#endif type IDependencyManagerProvider = abstract Name: string abstract Key: string diff --git a/src/Compiler/DependencyManager/DependencyProvider.fsi b/src/Compiler/DependencyManager/DependencyProvider.fsi index a4c76e67b92..7618f353141 100644 --- a/src/Compiler/DependencyManager/DependencyProvider.fsi +++ b/src/Compiler/DependencyManager/DependencyProvider.fsi @@ -39,7 +39,9 @@ type IResolveDependenciesResult = abstract Roots: seq /// Wraps access to a DependencyManager implementation +#if NO_CHECKNULLS [] +#endif type IDependencyManagerProvider = /// Name of the dependency manager diff --git a/src/Compiler/Driver/CompilerConfig.fs b/src/Compiler/Driver/CompilerConfig.fs index 117aa66391c..687d776d090 100644 --- a/src/Compiler/Driver/CompilerConfig.fs +++ b/src/Compiler/Driver/CompilerConfig.fs @@ -194,11 +194,14 @@ type IRawFSharpAssemblyData = abstract TryGetILModuleDef: unit -> ILModuleDef option /// The raw F# signature data in the assembly, if any - abstract GetRawFSharpSignatureData: range * ilShortAssemName: string * fileName: string -> (string * (unit -> ReadOnlyByteMemory)) list + abstract GetRawFSharpSignatureData: + range * ilShortAssemName: string * fileName: string -> + (string * ((unit -> ReadOnlyByteMemory) * (unit -> ReadOnlyByteMemory) option)) list /// The raw F# optimization data in the assembly, if any abstract GetRawFSharpOptimizationData: - range * ilShortAssemName: string * fileName: string -> (string * (unit -> ReadOnlyByteMemory)) list + range * ilShortAssemName: string * fileName: string -> + (string * ((unit -> ReadOnlyByteMemory) * (unit -> ReadOnlyByteMemory) option)) list /// The table of type forwarders in the assembly abstract GetRawTypeForwarders: unit -> ILExportedTypesAndForwarders @@ -439,6 +442,7 @@ type TcConfigBuilder = mutable embedResources: string list mutable diagnosticsOptions: FSharpDiagnosticOptions mutable mlCompatibility: bool + mutable checkNullness: bool mutable checkOverflow: bool mutable showReferenceResolutions: bool mutable outputDir: string option @@ -673,6 +677,7 @@ type TcConfigBuilder = subsystemVersion = 4, 0 // per spec for 357994 useHighEntropyVA = false mlCompatibility = false + checkNullness = false checkOverflow = false showReferenceResolutions = false outputDir = None @@ -1246,6 +1251,7 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) = member _.embedResources = data.embedResources member _.diagnosticsOptions = data.diagnosticsOptions member _.mlCompatibility = data.mlCompatibility + member _.checkNullness = data.checkNullness member _.checkOverflow = data.checkOverflow member _.showReferenceResolutions = data.showReferenceResolutions member _.outputDir = data.outputDir diff --git a/src/Compiler/Driver/CompilerConfig.fsi b/src/Compiler/Driver/CompilerConfig.fsi index 7f29c346eb8..f97ba86d7d6 100644 --- a/src/Compiler/Driver/CompilerConfig.fsi +++ b/src/Compiler/Driver/CompilerConfig.fsi @@ -46,11 +46,13 @@ type IRawFSharpAssemblyData = /// Get the raw F# signature data in the assembly, if any abstract GetRawFSharpSignatureData: - m: range * ilShortAssemName: string * fileName: string -> (string * (unit -> ReadOnlyByteMemory)) list + m: range * ilShortAssemName: string * fileName: string -> + (string * ((unit -> ReadOnlyByteMemory) * (unit -> ReadOnlyByteMemory) option)) list /// Get the raw F# optimization data in the assembly, if any abstract GetRawFSharpOptimizationData: - m: range * ilShortAssemName: string * fileName: string -> (string * (unit -> ReadOnlyByteMemory)) list + m: range * ilShortAssemName: string * fileName: string -> + (string * ((unit -> ReadOnlyByteMemory) * (unit -> ReadOnlyByteMemory) option)) list /// Get the table of type forwarders in the assembly abstract GetRawTypeForwarders: unit -> ILExportedTypesAndForwarders @@ -286,6 +288,8 @@ type TcConfigBuilder = mutable mlCompatibility: bool + mutable checkNullness: bool + mutable checkOverflow: bool mutable showReferenceResolutions: bool @@ -620,6 +624,8 @@ type TcConfig = member mlCompatibility: bool + member checkNullness: bool + member checkOverflow: bool member showReferenceResolutions: bool diff --git a/src/Compiler/Driver/CompilerDiagnostics.fs b/src/Compiler/Driver/CompilerDiagnostics.fs index d0c83a20a5b..d3e03d95735 100644 --- a/src/Compiler/Driver/CompilerDiagnostics.fs +++ b/src/Compiler/Driver/CompilerDiagnostics.fs @@ -174,6 +174,10 @@ type Exception with | ConstraintSolverTupleDiffLengths (_, _, _, _, m, _) | ConstraintSolverInfiniteTypes (_, _, _, _, m, _) | ConstraintSolverMissingConstraint (_, _, _, m, _) + | ConstraintSolverNullnessWarningEquivWithTypes (_, _, _, _, _, m, _) + | ConstraintSolverNullnessWarningWithTypes (_, _, _, _, _, m, _) + | ConstraintSolverNullnessWarningWithType (_, _, _, m, _) + | ConstraintSolverNonNullnessWarningWithType (_, _, _, m, _) | ConstraintSolverTypesNotInEqualityRelation (_, _, _, m, _, _) | ConstraintSolverError (_, m, _) | ConstraintSolverTypesNotInSubsumptionRelation (_, _, _, m, _) @@ -339,6 +343,10 @@ type Exception with #endif | ErrorsFromAddingSubsumptionConstraint (_, _, _, _, _, ContextInfo.DowncastUsedInsteadOfUpcast _, _) -> fst (FSComp.SR.considerUpcast ("", "")) + | ConstraintSolverNullnessWarningEquivWithTypes _ -> 3261 + | ConstraintSolverNullnessWarningWithTypes _ -> 3262 + | ConstraintSolverNullnessWarningWithType _ -> 3263 + | ConstraintSolverNonNullnessWarningWithType _ -> 3264 | _ -> 193 type PhasedDiagnostic with @@ -442,6 +450,10 @@ module OldStyleMessages = let ConstraintSolverTupleDiffLengthsE () = Message("ConstraintSolverTupleDiffLengths", "%d%d") let ConstraintSolverInfiniteTypesE () = Message("ConstraintSolverInfiniteTypes", "%s%s") let ConstraintSolverMissingConstraintE () = Message("ConstraintSolverMissingConstraint", "%s") + let ConstraintSolverNullnessWarningEquivWithTypesE () = Message("ConstraintSolverNullnessWarningEquivWithTypes", "%s%s%s%s") + let ConstraintSolverNullnessWarningWithTypesE () = Message("ConstraintSolverNullnessWarningWithTypes", "%s%s%s%s") + let ConstraintSolverNullnessWarningWithTypeE () = Message("ConstraintSolverNullnessWarningWithType", "%s") + let ConstraintSolverNonNullnessWarningWithTypeE () = Message("ConstraintSolverNonNullnessWarningWithType", "%s") let ConstraintSolverTypesNotInEqualityRelation1E () = Message("ConstraintSolverTypesNotInEqualityRelation1", "%s%s") let ConstraintSolverTypesNotInEqualityRelation2E () = Message("ConstraintSolverTypesNotInEqualityRelation2", "%s%s") let ConstraintSolverTypesNotInSubsumptionRelationE () = Message("ConstraintSolverTypesNotInSubsumptionRelation", "%s%s%s") @@ -664,6 +676,42 @@ type Exception with if m.StartLine <> m2.StartLine then os.AppendString(SeeAlsoE().Format(stringOfRange m)) + | ConstraintSolverNullnessWarningEquivWithTypes (denv, ty1, ty2, nullness1, nullness2, m, m2) -> + + let t1, t2, _cxs = NicePrint.minimalStringsOfTwoTypes denv ty1 ty2 + + os.Append(ConstraintSolverNullnessWarningEquivWithTypesE().Format t1 t2 (nullness1.ToString()) (nullness2.ToString())) + |> ignore + + if m.StartLine <> m2.StartLine then + os.Append(SeeAlsoE().Format(stringOfRange m)) |> ignore + + | ConstraintSolverNullnessWarningWithTypes (denv, ty1, ty2, nullness1, nullness2, m, m2) -> + + let t1, t2, _cxs = NicePrint.minimalStringsOfTwoTypes denv ty1 ty2 + + os.Append(ConstraintSolverNullnessWarningWithTypesE().Format t1 t2 (nullness1.ToString()) (nullness2.ToString())) + |> ignore + + if m.StartLine <> m2.StartLine then + os.Append(SeeAlsoE().Format(stringOfRange m)) |> ignore + + | ConstraintSolverNullnessWarningWithType (denv, ty, _, m, m2) -> + + let t = NicePrint.minimalStringOfType denv ty + os.Append(ConstraintSolverNullnessWarningWithTypeE().Format(t)) |> ignore + + if m.StartLine <> m2.StartLine then + os.Append(SeeAlsoE().Format(stringOfRange m)) |> ignore + + | ConstraintSolverNonNullnessWarningWithType (denv, ty, _, m, m2) -> + + let t = NicePrint.minimalStringOfType denv ty + os.Append(ConstraintSolverNonNullnessWarningWithTypeE().Format(t)) |> ignore + + if m.StartLine <> m2.StartLine then + os.Append(SeeAlsoE().Format(stringOfRange m)) |> ignore + | ConstraintSolverMissingConstraint (denv, tpr, tpc, m, m2) -> os.AppendString( ConstraintSolverMissingConstraintE() @@ -816,7 +864,8 @@ type Exception with let argsMessage, returnType, genericParametersMessage = let retTy = - knownReturnType |> Option.defaultValue (TType_var(Typar.NewUnlinked(), 0uy)) + knownReturnType + |> Option.defaultValue (TType.TType_var(Typar.NewUnlinked(), KnownAmbivalentToNull)) let argRepr = callerArgs.ArgumentNamesAndTypes @@ -1220,9 +1269,10 @@ type Exception with | Parser.TOKEN_INTERP_STRING_BEGIN_PART -> SR.GetString("Parser.TOKEN.INTERP.STRING.BEGIN.PART") | Parser.TOKEN_INTERP_STRING_PART -> SR.GetString("Parser.TOKEN.INTERP.STRING.PART") | Parser.TOKEN_INTERP_STRING_END -> SR.GetString("Parser.TOKEN.INTERP.STRING.END") + | Parser.TOKEN_WITHNULL__ -> SR.GetString("Parser.TOKEN.WITHNULL__") + | Parser.TOKEN_NOTNULL__ -> SR.GetString("Parser.TOKEN.NOTNULL__") | unknown -> - Debug.Assert(false, "unknown token tag") - let result = sprintf "%+A" unknown + let result = sprintf "unknown token tag %+A" unknown Debug.Assert(false, result) result diff --git a/src/Compiler/Driver/CompilerImports.fs b/src/Compiler/Driver/CompilerImports.fs index d182a8d8ebf..61eb721aea4 100644 --- a/src/Compiler/Driver/CompilerImports.fs +++ b/src/Compiler/Driver/CompilerImports.fs @@ -58,11 +58,19 @@ let IsSignatureDataResource (r: ILResource) = || r.Name.StartsWithOrdinal FSharpSignatureCompressedDataResourceName || r.Name.StartsWithOrdinal FSharpSignatureDataResourceName2 +let IsSignatureDataResourceB (r: ILResource) = + r.Name.StartsWithOrdinal FSharpSignatureDataResourceNameB + || r.Name.StartsWithOrdinal FSharpSignatureCompressedDataResourceNameB + let IsOptimizationDataResource (r: ILResource) = r.Name.StartsWithOrdinal FSharpOptimizationDataResourceName || r.Name.StartsWithOrdinal FSharpOptimizationCompressedDataResourceName || r.Name.StartsWithOrdinal FSharpOptimizationDataResourceName2 +let IsOptimizationDataResourceB (r: ILResource) = + r.Name.StartsWithOrdinal FSharpOptimizationDataResourceNameB + || r.Name.StartsWithOrdinal FSharpOptimizationCompressedDataResourceNameB + let decompressResource (r: ILResource) = use raw = r.GetBytes().AsStream() use decompressed = new MemoryStream() @@ -71,71 +79,130 @@ let decompressResource (r: ILResource) = deflator.Close() ByteStorage.FromByteArray(decompressed.ToArray()).GetByteMemory() -let GetResourceNameAndSignatureDataFunc (r: ILResource) = - let resourceType, ccuName = - if r.Name.StartsWithOrdinal FSharpSignatureDataResourceName then - FSharpSignatureDataResourceName, String.dropPrefix r.Name FSharpSignatureDataResourceName - elif r.Name.StartsWithOrdinal FSharpSignatureCompressedDataResourceName then - FSharpSignatureCompressedDataResourceName, String.dropPrefix r.Name FSharpSignatureCompressedDataResourceName - elif r.Name.StartsWithOrdinal FSharpSignatureDataResourceName2 then - FSharpSignatureDataResourceName2, String.dropPrefix r.Name FSharpSignatureDataResourceName2 - else - failwith "GetSignatureDataResourceName" - - if resourceType = FSharpSignatureCompressedDataResourceName then - ccuName, (fun () -> decompressResource (r)) +let GetSignatureDataResourceName (r: ILResource) = + if r.Name.StartsWithOrdinal FSharpSignatureDataResourceName then + (fun () -> r.GetBytes()), String.dropPrefix r.Name FSharpSignatureDataResourceName + elif r.Name.StartsWithOrdinal FSharpSignatureCompressedDataResourceName then + (fun () -> decompressResource r), String.dropPrefix r.Name FSharpSignatureCompressedDataResourceName + elif r.Name.StartsWithOrdinal FSharpSignatureDataResourceNameB then + (fun () -> r.GetBytes()), String.dropPrefix r.Name FSharpSignatureDataResourceNameB + elif r.Name.StartsWithOrdinal FSharpSignatureCompressedDataResourceNameB then + (fun () -> decompressResource r), String.dropPrefix r.Name FSharpSignatureCompressedDataResourceNameB + elif r.Name.StartsWithOrdinal FSharpSignatureDataResourceName2 then + (fun () -> r.GetBytes()), String.dropPrefix r.Name FSharpSignatureDataResourceName2 else - ccuName, (fun () -> r.GetBytes()) - -let GetResourceNameAndOptimizationDataFunc (r: ILResource) = - let resourceType, ccuName = - if r.Name.StartsWithOrdinal FSharpOptimizationDataResourceName then - FSharpOptimizationDataResourceName, String.dropPrefix r.Name FSharpOptimizationDataResourceName - elif r.Name.StartsWithOrdinal FSharpOptimizationCompressedDataResourceName then - FSharpOptimizationCompressedDataResourceName, String.dropPrefix r.Name FSharpOptimizationCompressedDataResourceName - elif r.Name.StartsWithOrdinal FSharpOptimizationDataResourceName2 then - FSharpOptimizationDataResourceName2, String.dropPrefix r.Name FSharpOptimizationDataResourceName2 - else - failwith "GetOptimizationDataResourceName" - - if resourceType = FSharpOptimizationCompressedDataResourceName then - ccuName, (fun () -> decompressResource (r)) + failwith "unreachable" + +let GetResourceNameAndSignatureDataFuncs (resources: ILResource list) = + [ for r in resources do + if IsSignatureDataResource r then + let readerA, ccuName = GetSignatureDataResourceName r + + let readerB = + resources |> List.tryPick (fun rB -> + if IsSignatureDataResourceB rB then + let readerB, ccuNameB = GetSignatureDataResourceName rB + if ccuName = ccuNameB then + Some readerB + else None + else None) + + ccuName, (readerA, readerB) ] + +let GetOptimizationDataResourceName (r: ILResource) = + if r.Name.StartsWithOrdinal FSharpOptimizationDataResourceName then + (fun () -> r.GetBytes()), String.dropPrefix r.Name FSharpOptimizationDataResourceName + elif r.Name.StartsWithOrdinal FSharpOptimizationCompressedDataResourceName then + (fun () -> decompressResource r), String.dropPrefix r.Name FSharpOptimizationCompressedDataResourceName + elif r.Name.StartsWithOrdinal FSharpOptimizationDataResourceNameB then + (fun () -> r.GetBytes()), String.dropPrefix r.Name FSharpOptimizationDataResourceNameB + elif r.Name.StartsWithOrdinal FSharpOptimizationCompressedDataResourceNameB then + (fun () -> decompressResource r), String.dropPrefix r.Name FSharpOptimizationCompressedDataResourceNameB + elif r.Name.StartsWithOrdinal FSharpOptimizationDataResourceName2 then + (fun () -> r.GetBytes()), String.dropPrefix r.Name FSharpOptimizationDataResourceName2 else - ccuName, (fun () -> r.GetBytes()) + failwith $"GetOptimizationDataResourceName - {r.Name}" + +let GetResourceNameAndOptimizationDataFuncs (resources: ILResource list) = + [ for r in resources do + if IsOptimizationDataResource r then + let readerA, ccuName = GetOptimizationDataResourceName r + + let readerB = + resources |> List.tryPick (fun rB -> + if IsOptimizationDataResourceB rB then + let readerB, ccuNameB = GetOptimizationDataResourceName rB + if ccuName = ccuNameB then + Some readerB + else None + else None) + ccuName, (readerA, readerB) ] let IsReflectedDefinitionsResource (r: ILResource) = r.Name.StartsWithOrdinal(QuotationPickler.SerializedReflectedDefinitionsResourceNameBase) -let PickleToResource inMem file (g: TcGlobals) compress scope rName p x = +let ByteBufferToBytes compress (bytes: ByteBuffer) = + if compress then + let raw = new MemoryStream(bytes.AsMemory().ToArray()) + let compressed = new MemoryStream() + use deflator = new DeflateStream(compressed, CompressionLevel.Optimal) + raw.CopyTo deflator + deflator.Close() + compressed.ToArray() + else + bytes.AsMemory().ToArray() + +let PickleToResource inMem file (g: TcGlobals) compress scope rName rNameB p x = let file = PathMap.apply g.pathMap file - let bytes = - use bytes = pickleObjWithDanglingCcus inMem file g scope p x + let bytes, bytesB = pickleObjWithDanglingCcus inMem file g scope p x + use bytes = bytes + use bytesB = bytesB + let bytes = ByteBufferToBytes compress bytes + let bytesB = ByteBufferToBytes compress bytesB + let byteStorage = ByteStorage.FromByteArray(bytes) - if compress then - let raw = new MemoryStream(bytes.AsMemory().ToArray()) - let compressed = new MemoryStream() - use deflator = new DeflateStream(compressed, CompressionLevel.Optimal) - raw.CopyTo deflator - deflator.Close() - compressed.ToArray() + let byteStorageB = + if inMem then + ByteStorage.FromMemoryAndCopy(bytesB.AsMemory(), useBackingMemoryMappedFile = true) else - bytes.AsMemory().ToArray() + ByteStorage.FromByteArray(bytesB.AsMemory().ToArray()) - let byteStorage = ByteStorage.FromByteArray(bytes) + let resource = + { + Name = rName + Location = ILResourceLocation.Local(byteStorage) + Access = ILResourceAccess.Public + CustomAttrsStored = storeILCustomAttrs emptyILCustomAttrs + MetadataIndex = NoMetadataIdx + } - { - Name = rName - Location = ILResourceLocation.Local(byteStorage) - Access = ILResourceAccess.Public - CustomAttrsStored = storeILCustomAttrs emptyILCustomAttrs - MetadataIndex = NoMetadataIdx - } + let resourceB = + if bytesB.AsMemory().Length > 0 then + Some + { + Name = rNameB + Location = ILResourceLocation.Local(byteStorageB) + Access = ILResourceAccess.Public + CustomAttrsStored = storeILCustomAttrs emptyILCustomAttrs + MetadataIndex = NoMetadataIdx + } + else + None + + resource, resourceB -let GetSignatureData (file, ilScopeRef, ilModule, byteReader) : PickledDataWithReferences = - unpickleObjWithDanglingCcus file ilScopeRef ilModule unpickleCcuInfo (byteReader ()) +let GetSignatureData (file, ilScopeRef, ilModule, byteReaderA, byteReaderB) : PickledDataWithReferences = + let memA = byteReaderA () -let WriteSignatureData (tcConfig: TcConfig, tcGlobals, exportRemapping, ccu: CcuThunk, fileName, inMem) : ILResource = + let memB = + (match byteReaderB with + | None -> ByteMemory.Empty.AsReadOnly() + | Some br -> br ()) + + unpickleObjWithDanglingCcus file ilScopeRef ilModule unpickleCcuInfo memA memB + +let WriteSignatureData (tcConfig: TcConfig, tcGlobals, exportRemapping, ccu: CcuThunk, fileName, inMem) = let mspec = ApplyExportRemappingToEntity tcGlobals exportRemapping ccu.Contents if tcConfig.dumpSignatureData then @@ -150,13 +217,19 @@ let WriteSignatureData (tcConfig: TcConfig, tcGlobals, exportRemapping, ccu: Ccu // For historical reasons, we use a different resource name for FSharp.Core, so older F# compilers // don't complain when they see the resource. - let rName, compress = + let rName = if tcConfig.compressMetadata then - FSharpSignatureCompressedDataResourceName, true + FSharpSignatureCompressedDataResourceName elif ccu.AssemblyName = getFSharpCoreLibraryName then - FSharpSignatureDataResourceName2, false + FSharpSignatureDataResourceName2 + else + FSharpSignatureDataResourceName + + let rNameB = + if tcConfig.compressMetadata then + FSharpSignatureCompressedDataResourceNameB else - FSharpSignatureDataResourceName, false + FSharpSignatureDataResourceNameB let includeDir = if String.IsNullOrEmpty tcConfig.implicitIncludeDir then @@ -170,9 +243,10 @@ let WriteSignatureData (tcConfig: TcConfig, tcGlobals, exportRemapping, ccu: Ccu inMem fileName tcGlobals - compress + tcConfig.compressMetadata ccu (rName + ccu.AssemblyName) + (rNameB + ccu.AssemblyName) pickleCcuInfo { mspec = mspec @@ -180,28 +254,56 @@ let WriteSignatureData (tcConfig: TcConfig, tcGlobals, exportRemapping, ccu: Ccu usesQuotations = ccu.UsesFSharp20PlusQuotations } -let GetOptimizationData (file, ilScopeRef, ilModule, byteReader) = - unpickleObjWithDanglingCcus file ilScopeRef ilModule Optimizer.u_CcuOptimizationInfo (byteReader ()) +let GetOptimizationData (file, ilScopeRef, ilModule, byteReaderA, byteReaderB) = + let memA = byteReaderA () + + let memB = + (match byteReaderB with + | None -> ByteMemory.Empty.AsReadOnly() + | Some br -> br ()) + + unpickleObjWithDanglingCcus file ilScopeRef ilModule Optimizer.u_CcuOptimizationInfo memA memB let WriteOptimizationData (tcConfig: TcConfig, tcGlobals, fileName, inMem, ccu: CcuThunk, modulInfo) = // For historical reasons, we use a different resource name for FSharp.Core, so older F# compilers // don't complain when they see the resource. - let rName, compress = + let rName = if tcConfig.compressMetadata then - FSharpOptimizationCompressedDataResourceName, true + FSharpOptimizationCompressedDataResourceName elif ccu.AssemblyName = getFSharpCoreLibraryName then - FSharpOptimizationDataResourceName2, false + FSharpOptimizationDataResourceName2 + else + FSharpOptimizationDataResourceName + + let rNameB = + if tcConfig.compressMetadata then + FSharpOptimizationCompressedDataResourceNameB else - FSharpOptimizationDataResourceName, false + FSharpOptimizationDataResourceNameB - PickleToResource inMem fileName tcGlobals compress ccu (rName + ccu.AssemblyName) Optimizer.p_CcuOptimizationInfo modulInfo + PickleToResource + inMem + fileName + tcGlobals + tcConfig.compressMetadata + ccu + (rName + ccu.AssemblyName) + (rNameB + ccu.AssemblyName) + Optimizer.p_CcuOptimizationInfo + modulInfo let EncodeSignatureData (tcConfig: TcConfig, tcGlobals, exportRemapping, generatedCcu, outfile, isIncrementalBuild) = if tcConfig.GenerateSignatureData then - let resource = + let resource1, resource2 = WriteSignatureData(tcConfig, tcGlobals, exportRemapping, generatedCcu, outfile, isIncrementalBuild) - let resources = [ resource ] + let resources = + [ + resource1 + match resource2 with + | None -> () + | Some r -> r + ] let sigAttr = mkSignatureDataVersionAttr tcGlobals (parseILVersion FSharpBinaryMetadataFormatRevision) @@ -220,9 +322,18 @@ let EncodeOptimizationData (tcGlobals, tcConfig: TcConfig, outfile, exportRemapp else data - [ + let r1, r2 = WriteOptimizationData(tcConfig, tcGlobals, outfile, isIncrementalBuild, ccu, optData) - ] + + let resources = + [ + r1 + match r2 with + | None -> () + | Some r -> r + ] + + resources else [] @@ -854,12 +965,7 @@ type RawFSharpAssemblyDataBackedByFileOnDisk(ilModule: ILModuleDef, ilAssemblyRe member _.GetRawFSharpSignatureData(m, ilShortAssemName, fileName) = let resources = ilModule.Resources.AsList() - let sigDataReaders = - [ - for r in resources do - if IsSignatureDataResource r then - GetResourceNameAndSignatureDataFunc r - ] + let sigDataReaders = GetResourceNameAndSignatureDataFuncs resources let sigDataReaders = if sigDataReaders.IsEmpty && List.contains ilShortAssemName externalSigAndOptData then @@ -868,27 +974,22 @@ type RawFSharpAssemblyDataBackedByFileOnDisk(ilModule: ILModuleDef, ilAssemblyRe if not (FileSystem.FileExistsShim sigFileName) then error (Error(FSComp.SR.buildExpectedSigdataFile (FileSystem.GetFullPathShim sigFileName), m)) - [ - (ilShortAssemName, - fun () -> - FileSystem - .OpenFileForReadShim(sigFileName, useMemoryMappedFile = true, shouldShadowCopy = true) - .AsByteMemory() - .AsReadOnly()) - ] + let readerA () = + FileSystem + .OpenFileForReadShim(sigFileName, useMemoryMappedFile = true, shouldShadowCopy = true) + .AsByteMemory() + .AsReadOnly() + + [ (ilShortAssemName, (readerA, None)) ] else sigDataReaders sigDataReaders member _.GetRawFSharpOptimizationData(m, ilShortAssemName, fileName) = - let optDataReaders = - ilModule.Resources.AsList() - |> List.choose (fun r -> - if IsOptimizationDataResource r then - Some(GetResourceNameAndOptimizationDataFunc r) - else - None) + let resources = ilModule.Resources.AsList() + + let optDataReaders = GetResourceNameAndOptimizationDataFuncs resources // Look for optimization data in a file let optDataReaders = @@ -896,17 +997,17 @@ type RawFSharpAssemblyDataBackedByFileOnDisk(ilModule: ILModuleDef, ilAssemblyRe let optDataFile = Path.ChangeExtension(fileName, "optdata") if not (FileSystem.FileExistsShim optDataFile) then - let fullPath = FileSystem.GetFullPathShim optDataFile - error (Error(FSComp.SR.buildExpectedFileAlongSideFSharpCore (optDataFile, fullPath), m)) - - [ - (ilShortAssemName, - (fun () -> - FileSystem - .OpenFileForReadShim(optDataFile, useMemoryMappedFile = true, shouldShadowCopy = true) - .AsByteMemory() - .AsReadOnly())) - ] + error ( + Error(FSComp.SR.buildExpectedFileAlongSideFSharpCore (optDataFile, FileSystem.GetFullPathShim optDataFile), m) + ) + + let readerA () = + FileSystem + .OpenFileForReadShim(optDataFile, useMemoryMappedFile = true, shouldShadowCopy = true) + .AsByteMemory() + .AsReadOnly() + + [ (ilShortAssemName, (readerA, None)) ] else optDataReaders @@ -945,20 +1046,11 @@ type RawFSharpAssemblyData(ilModule: ILModuleDef, ilAssemblyRefs) = member _.GetRawFSharpSignatureData(_, _, _) = let resources = ilModule.Resources.AsList() - - [ - for r in resources do - if IsSignatureDataResource r then - GetResourceNameAndSignatureDataFunc r - ] + GetResourceNameAndSignatureDataFuncs resources member _.GetRawFSharpOptimizationData(_, _, _) = - ilModule.Resources.AsList() - |> List.choose (fun r -> - if IsOptimizationDataResource r then - Some(GetResourceNameAndOptimizationDataFunc r) - else - None) + let resources = ilModule.Resources.AsList() + GetResourceNameAndOptimizationDataFuncs resources member _.GetRawTypeForwarders() = match ilModule.Manifest with @@ -2001,9 +2093,9 @@ and [] TcImports let ccuRawDataAndInfos = ilModule.GetRawFSharpSignatureData(m, ilShortAssemName, fileName) - |> List.map (fun (ccuName, sigDataReader) -> + |> List.map (fun (ccuName, (sigDataReader, sigDataReaderB)) -> let data = - GetSignatureData(fileName, ilScopeRef, ilModule.TryGetILModuleDef(), sigDataReader) + GetSignatureData(fileName, ilScopeRef, ilModule.TryGetILModuleDef(), sigDataReader, sigDataReaderB) let optDatas = Map.ofList optDataReaders @@ -2051,9 +2143,9 @@ and [] TcImports lazy (match Map.tryFind ccuName optDatas with | None -> None - | Some info -> + | Some (readerA, readerB) -> let data = - GetOptimizationData(fileName, ilScopeRef, ilModule.TryGetILModuleDef(), info) + GetOptimizationData(fileName, ilScopeRef, ilModule.TryGetILModuleDef(), readerA, readerB) let fixupThunk () = data.OptionalFixup(fun nm -> availableToOptionalCcu (tcImports.FindCcu(ctok, m, nm, lookupOnly = false))) @@ -2494,6 +2586,7 @@ and [] TcImports tcConfig.implicitIncludeDir, tcConfig.mlCompatibility, tcConfig.isInteractive, + tcConfig.checkNullness, tcConfig.useReflectionFreeCodeGen, tryFindSysTypeCcu, tcConfig.emitDebugInfoInQuotations, diff --git a/src/Compiler/Driver/CompilerImports.fsi b/src/Compiler/Driver/CompilerImports.fsi index f9fa17487ae..70f551c0f49 100644 --- a/src/Compiler/Driver/CompilerImports.fsi +++ b/src/Compiler/Driver/CompilerImports.fsi @@ -37,13 +37,20 @@ exception MSBuildReferenceResolutionError of message: string * warningCode: stri /// Determine if an IL resource attached to an F# assembly is an F# signature data resource val IsSignatureDataResource: ILResource -> bool +/// Determine if an IL resource attached to an F# assembly is an F# signature data resource (data stream B) +val IsSignatureDataResourceB: ILResource -> bool + /// Determine if an IL resource attached to an F# assembly is an F# optimization data resource val IsOptimizationDataResource: ILResource -> bool +/// Determine if an IL resource attached to an F# assembly is an F# optimization data resource (data sream B) +val IsOptimizationDataResourceB: ILResource -> bool + /// Determine if an IL resource attached to an F# assembly is an F# quotation data resource for reflected definitions val IsReflectedDefinitionsResource: ILResource -> bool -val GetResourceNameAndSignatureDataFunc: ILResource -> string * (unit -> ReadOnlyByteMemory) +val GetResourceNameAndSignatureDataFuncs: + ILResource list -> (string * ((unit -> ReadOnlyByteMemory) * (unit -> ReadOnlyByteMemory) option)) list /// Encode the F# interface data into a set of IL attributes and resources val EncodeSignatureData: diff --git a/src/Compiler/Driver/CompilerOptions.fs b/src/Compiler/Driver/CompilerOptions.fs index 9592a660491..501a45b595f 100644 --- a/src/Compiler/Driver/CompilerOptions.fs +++ b/src/Compiler/Driver/CompilerOptions.fs @@ -859,6 +859,14 @@ let errorsAndWarningsFlags (tcConfigB: TcConfigBuilder) = Some(FSComp.SR.optsWarnOn ()) ) + CompilerOption( + "checknulls", + tagNone, + OptionSwitch(fun switch -> tcConfigB.checkNullness <- switch = OptionSwitch.On), + None, + Some(FSComp.SR.optsCheckNulls ()) + ) + CompilerOption( "consolecolors", tagNone, diff --git a/src/Compiler/Driver/GraphChecking/FileContentMapping.fs b/src/Compiler/Driver/GraphChecking/FileContentMapping.fs index b0b44e941fb..b42310bb6a1 100644 --- a/src/Compiler/Driver/GraphChecking/FileContentMapping.fs +++ b/src/Compiler/Driver/GraphChecking/FileContentMapping.fs @@ -240,6 +240,7 @@ let visitSynType (t: SynType) : FileContentEntry list = let continuations = List.map (snd >> visit) fields Continuation.concatenate continuations continuation | SynType.Array (elementType = elementType) -> visit elementType continuation + | SynType.WithNull (innerType = innerType) -> visit innerType continuation | SynType.Fun (argType, returnType, _, _) -> let continuations = List.map visit [ argType; returnType ] Continuation.concatenate continuations continuation @@ -250,6 +251,7 @@ let visitSynType (t: SynType) : FileContentEntry list = | SynType.HashConstraint (innerType, _) -> visit innerType continuation | SynType.MeasurePower (baseMeasure = baseMeasure) -> visit baseMeasure continuation | SynType.StaticConstant _ -> continuation [] + | SynType.StaticConstantNull _ -> continuation [] | SynType.StaticConstantExpr (expr, _) -> continuation (visitSynExpr expr) | SynType.StaticConstantNamed (ident, value, _) -> let continuations = List.map visit [ ident; value ] @@ -287,6 +289,7 @@ let visitSynTypeConstraint (tc: SynTypeConstraint) : FileContentEntry list = | SynTypeConstraint.WhereTyparIsReferenceType _ | SynTypeConstraint.WhereTyparIsUnmanaged _ | SynTypeConstraint.WhereTyparSupportsNull _ + | SynTypeConstraint.WhereTyparNotSupportsNull _ | SynTypeConstraint.WhereTyparIsComparable _ | SynTypeConstraint.WhereTyparIsEquatable _ -> () | SynTypeConstraint.WhereTyparDefaultsToType (typeName = typeName) -> yield! visitSynType typeName diff --git a/src/Compiler/Driver/StaticLinking.fs b/src/Compiler/Driver/StaticLinking.fs index 54a47568f40..361c48b5cf1 100644 --- a/src/Compiler/Driver/StaticLinking.fs +++ b/src/Compiler/Driver/StaticLinking.fs @@ -184,7 +184,8 @@ let StaticLinkILModules // Save only the interface/optimization attributes of generated data let intfDataResources, others = - allResources |> List.partition (snd >> IsSignatureDataResource) + allResources + |> List.partition (fun (_, r) -> IsSignatureDataResource r || IsSignatureDataResourceB r) let intfDataResources = [ @@ -194,7 +195,8 @@ let StaticLinkILModules ] let optDataResources, others = - others |> List.partition (snd >> IsOptimizationDataResource) + others + |> List.partition (fun (_, r) -> IsOptimizationDataResource r || IsOptimizationDataResourceB r) let optDataResources = [ diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 0da140f884d..fce80a8d230 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1514,6 +1514,19 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3250,expressionHasNoName,"Expression does not have a name." 3251,chkNoFirstClassNameOf,"Using the 'nameof' operator as a first-class function value is not permitted." 3252,tcIllegalByrefsInOpenTypeDeclaration,"Byref types are not allowed in an open type declaration." +3260,tcTypeDoesNotHaveAnyNull,"The type '%s' does not support a nullness qualitification." +#3261 reserved for ConstraintSolverNullnessWarningEquivWithTypes +#3262 reserved for ConstraintSolverNullnessWarningWithTypes +#3263 reserved for ConstraintSolverNullnessWarningWithType +#3264 reserved for ConstraintSolverNonNullnessWarningWithType +3266,tcDefaultStructConstructorCallNulls,"Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable." +3267,chkValueWithDefaultValueMustHaveDefaultValueNulls,"Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable." +3268,csNullNotNullConstraintInconsistent,"The constraints 'null' and 'not null' are inconsistent" +3269,csStructNullConstraintInconsistent,"The constraints 'struct' and 'null' are inconsistent" +3270,csDelegateComparisonConstraintInconsistent,"The constraints 'delegate' and 'comparison' 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' has 'null' as a true representation value but a constraint does not permit this" +csTypeHasNullAsExtraValue,"The type '%s' has 'null' as an extra value but a constraint does not permit this" 3300,chkInvalidFunctionParameterType,"The parameter '%s' has an invalid type '%s'. This is not permitted by the rules of Common IL." 3301,chkInvalidFunctionReturnType,"The function or method has an invalid return type '%s'. This is not permitted by the rules of Common IL." 3302,packageManagementRequiresVFive,"The 'package management' feature requires language version 5.0 or above" @@ -1528,6 +1541,8 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3353,chkFeatureNotSupportedInLibrary,"Feature '%s' requires the F# library for language version %s or greater." 3360,parsEqualsMissingInTypeDefinition,"Unexpected token in type definition. Expected '=' after the type '%s'." useSdkRefs,"Use reference assemblies for .NET framework references when available (Enabled by default)." +optsCheckNulls,"Enable nullness declarations and checks" +fSharpBannerVersion,"%s for F# %s" optsGetLangVersions,"Display the allowed values for language version." optsSetLangVersion,"Specify language version such as 'latest' or 'preview'." optsSupportedLangVersions,"Supported language versions:" @@ -1545,6 +1560,7 @@ featurePackageManagement,"package management" featureFromEndSlicing,"from-end slicing" featureFixedIndexSlice3d4d,"fixed-index slice 3d/4d" featureAndBang,"applicative computation expressions" +featureNullnessChecking,"nullness checking" featureResumableStateMachines,"resumable state machines" featureNullableOptionalInterop,"nullable optional interop" featureDefaultInterfaceMemberConsumption,"default interface member consumption" diff --git a/src/Compiler/FSStrings.resx b/src/Compiler/FSStrings.resx index 92013c7bba2..417b7b77fe0 100644 --- a/src/Compiler/FSStrings.resx +++ b/src/Compiler/FSStrings.resx @@ -129,6 +129,18 @@ A type parameter is missing a constraint '{0}' + + Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'. + + + Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'. + + + Nullness warning: The type '{0}' does not support nullness. + + + Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null. + The unit of measure '{0}' does not match the unit of measure '{1}' @@ -441,6 +453,12 @@ keyword 'fixed' + + keyword '__withnull' + + + keyword '__notnull' + interpolated string diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 54f68ef21d1..632cab7f8bc 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -1,4 +1,4 @@ - + @@ -13,6 +13,7 @@ FSharp.Compiler.Service true $(DefineConstants);COMPILER + true $(DefineConstants);FSHARPCORE_USE_PACKAGE $(OtherFlags) --extraoptimizationloops:1 diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index f46b0dbeee1..cf5a3680b7c 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -37,6 +37,7 @@ type LanguageFeature = | StringInterpolation | OverloadsForCustomOperations | ExpandedMeasurables + | NullnessChecking | StructActivePattern | PrintfBinaryFormat | IndexerNotationWithoutDot @@ -144,6 +145,7 @@ type LanguageVersion(versionText) = // F# preview LanguageFeature.FromEndSlicing, previewVersion + LanguageFeature.NullnessChecking, previewVersion LanguageFeature.MatchNotAllowedForUnionCaseWithNoData, previewVersion LanguageFeature.CSharpExtensionAttributeNotRequired, previewVersion LanguageFeature.ErrorForNonVirtualMembersOverrides, previewVersion @@ -238,6 +240,7 @@ type LanguageVersion(versionText) = | LanguageFeature.FromEndSlicing -> FSComp.SR.featureFromEndSlicing () | LanguageFeature.FixedIndexSlice3d4d -> FSComp.SR.featureFixedIndexSlice3d4d () | LanguageFeature.AndBang -> FSComp.SR.featureAndBang () + | LanguageFeature.NullnessChecking -> FSComp.SR.featureNullnessChecking () | LanguageFeature.ResumableStateMachines -> FSComp.SR.featureResumableStateMachines () | LanguageFeature.NullableOptionalInterop -> FSComp.SR.featureNullableOptionalInterop () | LanguageFeature.DefaultInterfaceMemberConsumption -> FSComp.SR.featureDefaultInterfaceMemberConsumption () diff --git a/src/Compiler/Facilities/LanguageFeatures.fsi b/src/Compiler/Facilities/LanguageFeatures.fsi index d12c77fdcbe..22abb8e4bbb 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fsi +++ b/src/Compiler/Facilities/LanguageFeatures.fsi @@ -27,6 +27,7 @@ type LanguageFeature = | StringInterpolation | OverloadsForCustomOperations | ExpandedMeasurables + | NullnessChecking | StructActivePattern | PrintfBinaryFormat | IndexerNotationWithoutDot diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index 1c7e7bcd2ac..97c3cc08d84 100644 --- a/src/Compiler/Interactive/fsi.fs +++ b/src/Compiler/Interactive/fsi.fs @@ -1570,7 +1570,7 @@ let rec ConvReflectionTypeToILType (reflectionTy: Type) = if ctors.Length = 1 - && ctors[ 0 ].GetCustomAttribute() <> null + && not (isNull (box (ctors[ 0 ].GetCustomAttribute()))) && not ctors[0].IsPublic && IsCompilerGeneratedName reflectionTy.Name then @@ -3283,7 +3283,7 @@ type internal MagicAssemblyResolution() = fsiDynamicCompiler: FsiDynamicCompiler, fsiConsoleOutput: FsiConsoleOutput, fullAssemName: string - ) = + ) : Assembly MaybeNull = try // Grab the name of the assembly diff --git a/src/Compiler/Legacy/LegacyHostedCompilerForTesting.fs b/src/Compiler/Legacy/LegacyHostedCompilerForTesting.fs index 87ec46483fe..5982b97df29 100644 --- a/src/Compiler/Legacy/LegacyHostedCompilerForTesting.fs +++ b/src/Compiler/Legacy/LegacyHostedCompilerForTesting.fs @@ -208,11 +208,13 @@ type internal FscCompiler(legacyReferenceResolver) = // args.[0] is later discarded, assuming it is just the path to fsc. // compensate for this in case caller didn't know let args = - match args with - | [||] - | null -> [| "fsc" |] - | a when not <| fscExeArg a[0] -> Array.append [| "fsc" |] a - | _ -> args + match box args with + | Null -> [| "fsc" |] + | _ -> + match args with + | [||] -> [| "fsc" |] + | a when not <| fscExeArg a[0] -> Array.append [| "fsc" |] a + | _ -> args let errorRanges = args |> Seq.exists errorRangesArg let vsErrors = args |> Seq.exists vsErrorsArg diff --git a/src/Compiler/Service/IncrementalBuild.fs b/src/Compiler/Service/IncrementalBuild.fs index dc27983338e..443deca396a 100644 --- a/src/Compiler/Service/IncrementalBuild.fs +++ b/src/Compiler/Service/IncrementalBuild.fs @@ -584,9 +584,7 @@ type RawFSharpAssemblyDataBackedByLanguageService (tcConfig, tcGlobals, generate let sigData = let _sigDataAttributes, sigDataResources = EncodeSignatureData(tcConfig, tcGlobals, exportRemapping, generatedCcu, outfile, true) - [ for r in sigDataResources do - GetResourceNameAndSignatureDataFunc r - ] + GetResourceNameAndSignatureDataFuncs sigDataResources let autoOpenAttrs = topAttrs.assemblyAttrs |> List.choose (List.singleton >> TryFindFSharpStringAttribute tcGlobals tcGlobals.attrib_AutoOpenAttribute) diff --git a/src/Compiler/Service/QuickParse.fs b/src/Compiler/Service/QuickParse.fs index 381ba6a865d..dbfcbcb2931 100644 --- a/src/Compiler/Service/QuickParse.fs +++ b/src/Compiler/Service/QuickParse.fs @@ -79,7 +79,7 @@ module QuickParse = | _ -> false let GetCompleteIdentifierIslandImplAux (lineStr: string) (index: int) : (string * int * bool) option = - if index < 0 || isNull lineStr || index >= lineStr.Length then + if index < 0 || index >= lineStr.Length then None else let fixup = diff --git a/src/Compiler/Service/ServiceDeclarationLists.fs b/src/Compiler/Service/ServiceDeclarationLists.fs index a0da4745902..bd1d343c0f1 100644 --- a/src/Compiler/Service/ServiceDeclarationLists.fs +++ b/src/Compiler/Service/ServiceDeclarationLists.fs @@ -680,7 +680,7 @@ module internal DescriptionListsImpl = |> Array.map (fun sp -> let ty = Import.ImportProvidedType amap m (sp.PApply((fun x -> x.ParameterType), m)) let spKind = NicePrint.prettyLayoutOfType denv ty - let spName = sp.PUntaint((fun sp -> sp.Name), m) + let spName = sp.PUntaint((fun sp -> nonNull sp.Name), m) let spOpt = sp.PUntaint((fun sp -> sp.IsOptional), m) let display = (if spOpt then SepL.questionMark else emptyL) ^^ wordL (tagParameter spName) ^^ RightL.colon ^^ spKind let display = toArray display diff --git a/src/Compiler/Service/ServiceLexing.fs b/src/Compiler/Service/ServiceLexing.fs index 09f3d6b8016..85910118c7c 100644 --- a/src/Compiler/Service/ServiceLexing.fs +++ b/src/Compiler/Service/ServiceLexing.fs @@ -395,6 +395,9 @@ module internal TokenClassifications = | HIGH_PRECEDENCE_PAREN_APP | FIXED | HIGH_PRECEDENCE_BRACK_APP + | MAYBENULL__ + | NOTNULL__ + | WITHNULL__ | TYPE_COMING_SOON | TYPE_IS_HERE | MODULE_COMING_SOON diff --git a/src/Compiler/Service/ServiceParamInfoLocations.fs b/src/Compiler/Service/ServiceParamInfoLocations.fs index b757120b628..b9f4ddc412f 100755 --- a/src/Compiler/Service/ServiceParamInfoLocations.fs +++ b/src/Compiler/Service/ServiceParamInfoLocations.fs @@ -62,6 +62,7 @@ module internal ParameterLocationsImpl = let isStaticArg (StripParenTypes synType) = match synType with | SynType.StaticConstant _ + | SynType.StaticConstantNull _ | SynType.StaticConstantExpr _ | SynType.StaticConstantNamed _ -> true | SynType.LongIdent _ -> true // NOTE: this is not a static constant, but it is a prefix of incomplete code, e.g. "TP<42, Arg3" is a prefix of "TP<42, Arg3=6>" and Arg3 shows up as a LongId diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index b6f95d66a30..354fbf48716 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -836,6 +836,7 @@ module SyntaxTraversal = | SynType.Fun (argType = ty1; returnType = ty2) -> [ ty1; ty2 ] |> List.tryPick (traverseSynType path) | SynType.MeasurePower (ty, _, _) | SynType.HashConstraint (ty, _) + | SynType.WithNull (ty, _, _) | SynType.WithGlobalConstraints (ty, _, _) | SynType.Array (_, ty, _) -> traverseSynType path ty | SynType.StaticConstantNamed (ty1, ty2, _) @@ -844,6 +845,7 @@ module SyntaxTraversal = | SynType.StaticConstantExpr (expr, _) -> traverseSynExpr [] expr | SynType.Paren (innerType = t) | SynType.SignatureParameter (usedType = t) -> traverseSynType path t + | SynType.StaticConstantNull _ | SynType.Anon _ | SynType.AnonRecd _ | SynType.LongIdent _ diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index 91c48c42bcb..36343d26f97 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -611,6 +611,7 @@ module ParsedInput = | SynTypeConstraint.WhereTyparIsReferenceType (t, _) -> walkTypar t | SynTypeConstraint.WhereTyparIsUnmanaged (t, _) -> walkTypar t | SynTypeConstraint.WhereTyparSupportsNull (t, _) -> walkTypar t + | SynTypeConstraint.WhereTyparNotSupportsNull (t, _) -> walkTypar t | SynTypeConstraint.WhereTyparIsComparable (t, _) -> walkTypar t | SynTypeConstraint.WhereTyparIsEquatable (t, _) -> walkTypar t | SynTypeConstraint.WhereTyparSubtypeOfType (t, ty, _) -> walkTypar t |> Option.orElseWith (fun () -> walkType ty) @@ -674,6 +675,7 @@ module ParsedInput = | SynType.Array (_, t, _) -> walkType t | SynType.Fun (argType = t1; returnType = t2) -> walkType t1 |> Option.orElseWith (fun () -> walkType t2) | SynType.WithGlobalConstraints (t, _, _) -> walkType t + | SynType.WithNull (t, _, _) | SynType.HashConstraint (t, _) -> walkType t | SynType.Or (t1, t2, _, _) -> walkType t1 |> Option.orElseWith (fun () -> walkType t2) | SynType.MeasurePower (t, _, _) -> walkType t @@ -681,6 +683,7 @@ module ParsedInput = | SynType.SignatureParameter (usedType = t) -> walkType t | SynType.StaticConstantExpr (e, _) -> walkExpr e | SynType.StaticConstantNamed (ident, value, _) -> List.tryPick walkType [ ident; value ] + | SynType.StaticConstantNull _ | SynType.Anon _ | SynType.AnonRecd _ | SynType.LongIdent _ @@ -1619,6 +1622,7 @@ module ParsedInput = | SynTypeConstraint.WhereTyparIsReferenceType (t, _) | SynTypeConstraint.WhereTyparIsUnmanaged (t, _) | SynTypeConstraint.WhereTyparSupportsNull (t, _) + | SynTypeConstraint.WhereTyparNotSupportsNull (t, _) | SynTypeConstraint.WhereTyparIsComparable (t, _) | SynTypeConstraint.WhereTyparIsEquatable (t, _) -> walkTypar t | SynTypeConstraint.WhereTyparDefaultsToType (t, ty, _) @@ -1680,6 +1684,7 @@ module ParsedInput = | SynType.Array (_, t, _) | SynType.HashConstraint (t, _) | SynType.MeasurePower (t, _, _) + | SynType.WithNull (t, _, _) | SynType.Paren (t, _) | SynType.SignatureParameter (usedType = t) -> walkType t | SynType.Fun (argType = t1; returnType = t2) @@ -1699,6 +1704,7 @@ module ParsedInput = | SynType.StaticConstantNamed (ident, value, _) -> walkType ident walkType value + | SynType.StaticConstantNull _ | SynType.Anon _ | SynType.AnonRecd _ | SynType.Var _ diff --git a/src/Compiler/Symbols/Symbols.fs b/src/Compiler/Symbols/Symbols.fs index 8595d61bf1b..16823f15c14 100644 --- a/src/Compiler/Symbols/Symbols.fs +++ b/src/Compiler/Symbols/Symbols.fs @@ -1559,6 +1559,11 @@ type FSharpGenericParameterConstraint(cenv, cx: TyparConstraint) = | TyparConstraint.SupportsComparison _ -> true | _ -> false + member _.IsNotSupportsNullConstraint = + match cx with + | TyparConstraint.NotSupportsNull _ -> true + | _ -> false + member _.IsEqualityConstraint = match cx with | TyparConstraint.SupportsEquality _ -> true @@ -2459,6 +2464,23 @@ type FSharpType(cenv, ty:TType) = | TType_measure (Measure.Inv _) -> FSharpEntity(cenv, cenv.g.measureinverse_tcr) | _ -> invalidOp "not a named type" + member _.HasNullAnnotation = + protect <| fun () -> + match stripTyparEqns ty with + | TType_var (_, nullness) + | TType_app (_, _, nullness) + | TType_fun(_, _, nullness) -> match nullness.Evaluate() with NullnessInfo.WithNull -> true | _ -> false + | TType_tuple (_, _) -> false + | _ -> false + + member _.IsNullAmbivalent = + protect <| fun () -> + match stripTyparEqns ty with + | TType_app (_, _, nullness) + | TType_fun(_, _, nullness) -> match nullness.Evaluate() with NullnessInfo.AmbivalentToNull -> true | _ -> false + | TType_tuple (_, _) -> false + | _ -> false + member _.GenericArguments = protect <| fun () -> match stripTyparEqns ty with diff --git a/src/Compiler/Symbols/Symbols.fsi b/src/Compiler/Symbols/Symbols.fsi index 44e215e5214..beaa3617435 100644 --- a/src/Compiler/Symbols/Symbols.fsi +++ b/src/Compiler/Symbols/Symbols.fsi @@ -1073,6 +1073,12 @@ type FSharpType = /// Get the type definition for a type member TypeDefinition: FSharpEntity + /// Indicates this type is known to have a null annotation + member HasNullAnnotation: bool + + /// Indicates this type is assumed to support the null value + member IsNullAmbivalent: bool + /// Get the generic arguments for a tuple type, a function type or a type constructed using a named entity member GenericArguments: IList diff --git a/src/Compiler/SyntaxTree/LexFilter.fs b/src/Compiler/SyntaxTree/LexFilter.fs index e163646f688..4cdf7deda24 100644 --- a/src/Compiler/SyntaxTree/LexFilter.fs +++ b/src/Compiler/SyntaxTree/LexFilter.fs @@ -1084,7 +1084,11 @@ type LexFilterImpl ( // f<{| C : int |}>x // fx // fx + // fx + // fx + // fx | DEFAULT | COLON | COLON_GREATER | STRUCT | NULL | DELEGATE | AND | WHEN + | NOTNULL__ | MAYBENULL__ | WITHNULL__ | DOT_DOT | NEW | LBRACE_BAR diff --git a/src/Compiler/SyntaxTree/LexHelpers.fs b/src/Compiler/SyntaxTree/LexHelpers.fs index f1de8c6b814..a8723d765e7 100644 --- a/src/Compiler/SyntaxTree/LexHelpers.fs +++ b/src/Compiler/SyntaxTree/LexHelpers.fs @@ -397,6 +397,10 @@ module Keywords = FSHARP, "__token_ODO", ODO FSHARP, "__token_OLET", OLET(true) FSHARP, "__token_constraint", CONSTRAINT + (*------- for prototyping *) + FSHARP, "__maybenull", MAYBENULL__ + FSHARP, "__notnull", NOTNULL__ + FSHARP, "__withnull", WITHNULL__ ] (*------- reserved keywords which are ml-compatibility ids *) @ List.map diff --git a/src/Compiler/SyntaxTree/PrettyNaming.fs b/src/Compiler/SyntaxTree/PrettyNaming.fs index f78f742a03f..0e6d0d42102 100755 --- a/src/Compiler/SyntaxTree/PrettyNaming.fs +++ b/src/Compiler/SyntaxTree/PrettyNaming.fs @@ -1096,12 +1096,21 @@ let FSharpOptimizationDataResourceName = "FSharpOptimizationData." let FSharpSignatureDataResourceName = "FSharpSignatureData." +let FSharpOptimizationDataResourceNameB = "FSharpOptimizationDataB." + +let FSharpSignatureDataResourceNameB = "FSharpSignatureDataB." + // Compressed OptimizationData/SignatureData name for embedded resource let FSharpOptimizationCompressedDataResourceName = "FSharpOptimizationCompressedData." let FSharpSignatureCompressedDataResourceName = "FSharpSignatureCompressedData." +let FSharpOptimizationCompressedDataResourceNameB = + "FSharpOptimizationCompressedDataB." + +let FSharpSignatureCompressedDataResourceNameB = "FSharpSignatureCompressedDataB." + // For historical reasons, we use a different resource name for FSharp.Core, so older F# compilers // don't complain when they see the resource. The prefix of these names must not be 'FSharpOptimizationData' // or 'FSharpSignatureData' diff --git a/src/Compiler/SyntaxTree/PrettyNaming.fsi b/src/Compiler/SyntaxTree/PrettyNaming.fsi index 05478070e1a..27f6f2e0ea3 100644 --- a/src/Compiler/SyntaxTree/PrettyNaming.fsi +++ b/src/Compiler/SyntaxTree/PrettyNaming.fsi @@ -267,10 +267,18 @@ val internal FSharpOptimizationDataResourceName: string val internal FSharpSignatureDataResourceName: string +val internal FSharpOptimizationDataResourceNameB: string + +val internal FSharpSignatureDataResourceNameB: string + val internal FSharpOptimizationCompressedDataResourceName: string val internal FSharpSignatureCompressedDataResourceName: string +val internal FSharpOptimizationCompressedDataResourceNameB: string + +val internal FSharpSignatureCompressedDataResourceNameB: string + val internal FSharpOptimizationDataResourceName2: string val internal FSharpSignatureDataResourceName2: string diff --git a/src/Compiler/SyntaxTree/SyntaxTree.fs b/src/Compiler/SyntaxTree/SyntaxTree.fs index eb8c30d2c03..a7360d0165e 100644 --- a/src/Compiler/SyntaxTree/SyntaxTree.fs +++ b/src/Compiler/SyntaxTree/SyntaxTree.fs @@ -322,6 +322,8 @@ type SynTypeConstraint = | WhereTyparSupportsNull of typar: SynTypar * range: range + | WhereTyparNotSupportsNull of genericName: SynTypar * range: range + | WhereTyparIsComparable of typar: SynTypar * range: range | WhereTyparIsEquatable of typar: SynTypar * range: range @@ -344,6 +346,7 @@ type SynTypeConstraint = | WhereTyparIsReferenceType (range = range) | WhereTyparIsUnmanaged (range = range) | WhereTyparSupportsNull (range = range) + | WhereTyparNotSupportsNull (range = range) | WhereTyparIsComparable (range = range) | WhereTyparIsEquatable (range = range) | WhereTyparDefaultsToType (range = range) @@ -431,10 +434,14 @@ type SynType = | StaticConstant of constant: SynConst * range: range + | StaticConstantNull of range: range + | StaticConstantExpr of expr: SynExpr * range: range | StaticConstantNamed of ident: SynType * value: SynType * range: range + | WithNull of innerType: SynType * ambivalent: bool * range: range + | Paren of innerType: SynType * range: range | SignatureParameter of attributes: SynAttributes * optional: bool * id: Ident option * usedType: SynType * range: range @@ -455,9 +462,11 @@ type SynType = | SynType.Anon (range = m) | SynType.WithGlobalConstraints (range = m) | SynType.StaticConstant (range = m) + | SynType.StaticConstantNull (range = m) | SynType.StaticConstantExpr (range = m) | SynType.StaticConstantNamed (range = m) | SynType.HashConstraint (range = m) + | SynType.WithNull (range = m) | SynType.MeasurePower (range = m) | SynType.Paren (range = m) | SynType.SignatureParameter (range = m) diff --git a/src/Compiler/SyntaxTree/SyntaxTree.fsi b/src/Compiler/SyntaxTree/SyntaxTree.fsi index 5c35a2e2c82..9ba3e480d81 100644 --- a/src/Compiler/SyntaxTree/SyntaxTree.fsi +++ b/src/Compiler/SyntaxTree/SyntaxTree.fsi @@ -392,6 +392,9 @@ type SynTypeConstraint = /// F# syntax is 'typar: null | WhereTyparSupportsNull of typar: SynTypar * range: range + /// F# syntax is 'typar : null + | WhereTyparNotSupportsNull of genericName: SynTypar * range: range + /// F# syntax is 'typar: comparison | WhereTyparIsComparable of typar: SynTypar * range: range @@ -499,12 +502,17 @@ type SynType = /// For the dimensionless units i.e. 1, and static parameters to provided types | StaticConstant of constant: SynConst * range: range + /// F# syntax: null, used in parameters to type providers + | StaticConstantNull of range: range + /// F# syntax: const expr, used in static parameters to type providers | StaticConstantExpr of expr: SynExpr * range: range /// F# syntax: ident=1 etc., used in static parameters to type providers | StaticConstantNamed of ident: SynType * value: SynType * range: range + | WithNull of innerType: SynType * ambivalent: bool * range: range + | Paren of innerType: SynType * range: range /// F# syntax: a: b, used in signatures and type annotations diff --git a/src/Compiler/TypedTree/TcGlobals.fs b/src/Compiler/TypedTree/TcGlobals.fs index 4174d567a6e..3f92f88a3e6 100755 --- a/src/Compiler/TypedTree/TcGlobals.fs +++ b/src/Compiler/TypedTree/TcGlobals.fs @@ -185,6 +185,7 @@ type TcGlobals( directoryToResolveRelativePaths, mlCompatibility: bool, isInteractive: bool, + checkNullness: bool, useReflectionFreeCodeGen: bool, // The helper to find system types amongst referenced DLLs tryFindSysTypeCcuHelper, @@ -193,11 +194,15 @@ type TcGlobals( pathMap: PathMap, langVersion: LanguageVersion) = - // empty flags - let v_knownWithoutNull = 0uy + let v_langFeatureNullness = langVersion.SupportsFeature LanguageFeature.NullnessChecking + + let v_knownWithoutNull = + if v_langFeatureNullness then KnownWithoutNull else KnownAmbivalentToNull let mkNonGenericTy tcref = TType_app(tcref, [], v_knownWithoutNull) + let mkNonGenericTyWithNullness tcref nullness = TType_app(tcref, [], nullness) + let mkNonLocalTyconRef2 ccu path n = mkNonLocalTyconRef (mkNonLocalEntityRef ccu path) n let mk_MFCore_tcref ccu n = mkNonLocalTyconRef2 ccu CorePathArray n @@ -642,16 +647,18 @@ type TcGlobals( | [_] -> None | _ -> TType_tuple (tupInfo, l) |> Some - - let decodeTupleTy tupInfo l = - match tryDecodeTupleTy tupInfo l with + let decodeTupleTyAndNullness tupInfo tinst _nullness = // TODO nullness + match tryDecodeTupleTy tupInfo tinst with | Some ty -> ty | None -> failwith "couldn't decode tuple ty" - let decodeTupleTyIfPossible tcref tupInfo l = - match tryDecodeTupleTy tupInfo l with + let decodeTupleTyAndNullnessIfPossible tcref tupInfo tinst nullness = // TODO nullness + match tryDecodeTupleTy tupInfo tinst with | Some ty -> ty - | None -> TType_app(tcref, l, v_knownWithoutNull) + | None -> TType_app(tcref, tinst, nullness) + + let decodeTupleTy tupInfo tinst = + decodeTupleTyAndNullness tupInfo tinst v_knownWithoutNull let mk_MFCore_attrib nm : BuiltinAttribInfo = AttribInfo(mkILTyRef(ilg.fsharpCoreAssemblyScopeRef, Core + "." + nm), mk_MFCore_tcref fslibCcu nm) @@ -948,25 +955,29 @@ type TcGlobals( "Single" , v_float32_tcr |] |> Array.map (fun (nm, tcr) -> let ty = mkNonGenericTy tcr - nm, findSysTyconRef sys nm, (fun _ -> ty)) + nm, findSysTyconRef sys nm, (fun _ nullness -> + match nullness with + | Nullness.Known NullnessInfo.WithoutNull -> ty + | _ -> mkNonGenericTyWithNullness tcr nullness)) let decompileTyconEntries = [| - "FSharpFunc`2" , v_fastFunc_tcr , (fun tinst -> mkFunTy (List.item 0 tinst) (List.item 1 tinst)) - "Tuple`2" , v_ref_tuple2_tcr , decodeTupleTy tupInfoRef - "Tuple`3" , v_ref_tuple3_tcr , decodeTupleTy tupInfoRef - "Tuple`4" , v_ref_tuple4_tcr , decodeTupleTy tupInfoRef - "Tuple`5" , v_ref_tuple5_tcr , decodeTupleTy tupInfoRef - "Tuple`6" , v_ref_tuple6_tcr , decodeTupleTy tupInfoRef - "Tuple`7" , v_ref_tuple7_tcr , decodeTupleTy tupInfoRef - "Tuple`8" , v_ref_tuple8_tcr , decodeTupleTyIfPossible v_ref_tuple8_tcr tupInfoRef - "ValueTuple`2" , v_struct_tuple2_tcr , decodeTupleTy tupInfoStruct - "ValueTuple`3" , v_struct_tuple3_tcr , decodeTupleTy tupInfoStruct - "ValueTuple`4" , v_struct_tuple4_tcr , decodeTupleTy tupInfoStruct - "ValueTuple`5" , v_struct_tuple5_tcr , decodeTupleTy tupInfoStruct - "ValueTuple`6" , v_struct_tuple6_tcr , decodeTupleTy tupInfoStruct - "ValueTuple`7" , v_struct_tuple7_tcr , decodeTupleTy tupInfoStruct - "ValueTuple`8" , v_struct_tuple8_tcr , decodeTupleTyIfPossible v_struct_tuple8_tcr tupInfoStruct |] + // TODO: nullness here + "FSharpFunc`2" , v_fastFunc_tcr , (fun tinst _nullness -> mkFunTy (List.item 0 tinst) (List.item 1 tinst)) + "Tuple`2" , v_ref_tuple2_tcr , decodeTupleTyAndNullness tupInfoRef + "Tuple`3" , v_ref_tuple3_tcr , decodeTupleTyAndNullness tupInfoRef + "Tuple`4" , v_ref_tuple4_tcr , decodeTupleTyAndNullness tupInfoRef + "Tuple`5" , v_ref_tuple5_tcr , decodeTupleTyAndNullness tupInfoRef + "Tuple`6" , v_ref_tuple6_tcr , decodeTupleTyAndNullness tupInfoRef + "Tuple`7" , v_ref_tuple7_tcr , decodeTupleTyAndNullness tupInfoRef + "Tuple`8" , v_ref_tuple8_tcr , decodeTupleTyAndNullnessIfPossible v_ref_tuple8_tcr tupInfoRef + "ValueTuple`2" , v_struct_tuple2_tcr , decodeTupleTyAndNullness tupInfoStruct + "ValueTuple`3" , v_struct_tuple3_tcr , decodeTupleTyAndNullness tupInfoStruct + "ValueTuple`4" , v_struct_tuple4_tcr , decodeTupleTyAndNullness tupInfoStruct + "ValueTuple`5" , v_struct_tuple5_tcr , decodeTupleTyAndNullness tupInfoStruct + "ValueTuple`6" , v_struct_tuple6_tcr , decodeTupleTyAndNullness tupInfoStruct + "ValueTuple`7" , v_struct_tuple7_tcr , decodeTupleTyAndNullness tupInfoStruct + "ValueTuple`8" , v_struct_tuple8_tcr , decodeTupleTyAndNullnessIfPossible v_struct_tuple8_tcr tupInfoStruct |] let betterEntries = Array.append betterTyconEntries decompileTyconEntries @@ -997,11 +1008,11 @@ type TcGlobals( let t = Dictionary.newWithSize entries.Length for nm, tcref, builder in entries do t.Add(nm, - (fun tcref2 tinst2 -> + (fun tcref2 tinst2 nullness -> if tyconRefEq tcref tcref2 then - builder tinst2 + builder tinst2 nullness else - TType_app (tcref2, tinst2, v_knownWithoutNull))) + TType_app (tcref2, tinst2, nullness))) betterTypeDict1 <- t t | _ -> betterTypeDict1 @@ -1023,30 +1034,30 @@ type TcGlobals( /// For logical purposes equate some F# types with .NET types, e.g. TType_tuple == System.Tuple/ValueTuple. /// Doing this normalization is a fairly performance critical piece of code as it is frequently invoked /// in the process of converting .NET metadata to F# internal compiler data structures (see import.fs). - let decompileTy (tcref: EntityRef) tinst = + let decompileTy (tcref: EntityRef) tinst nullness = if compilingFSharpCore then // No need to decompile when compiling FSharp.Core.dll - TType_app (tcref, tinst, v_knownWithoutNull) + TType_app (tcref, tinst, nullness) else let dict = getDecompileTypeDict() match dict.TryGetValue tcref.Stamp with - | true, builder -> builder tinst - | _ -> TType_app (tcref, tinst, v_knownWithoutNull) + | true, builder -> builder tinst nullness + | _ -> TType_app (tcref, tinst, nullness) /// For cosmetic purposes "improve" some .NET types, e.g. Int32 --> int32. /// Doing this normalization is a fairly performance critical piece of code as it is frequently invoked /// in the process of converting .NET metadata to F# internal compiler data structures (see import.fs). - let improveTy (tcref: EntityRef) tinst = + let improveTy (tcref: EntityRef) tinst nullness = if compilingFSharpCore then let dict = getBetterTypeDict1() match dict.TryGetValue tcref.LogicalName with - | true, builder -> builder tcref tinst - | _ -> TType_app (tcref, tinst, v_knownWithoutNull) + | true, builder -> builder tcref tinst nullness + | _ -> TType_app (tcref, tinst, nullness) else let dict = getBetterTypeDict2() match dict.TryGetValue tcref.Stamp with - | true, builder -> builder tinst - | _ -> TType_app (tcref, tinst, v_knownWithoutNull) + | true, builder -> builder tinst nullness + | _ -> TType_app (tcref, tinst, nullness) // Adding an unnecessary "let" instead of inlining into a muiti-line pipelined compute-once "member val" that is too complex for @dsyme let v_attribs_Unsupported = [ @@ -1076,7 +1087,10 @@ type TcGlobals( // A table of all intrinsics that the compiler cares about member _.knownIntrinsics = v_knownIntrinsics - // empty flags + member _.checkNullness = checkNullness + + member _.langFeatureNullness = v_langFeatureNullness + member _.knownWithoutNull = v_knownWithoutNull // A table of known modules in FSharp.Core. Not all modules are necessarily listed, but the more we list the diff --git a/src/Compiler/TypedTree/TypeProviders.fs b/src/Compiler/TypedTree/TypeProviders.fs index dff177e296f..fbb1899bcd1 100644 --- a/src/Compiler/TypedTree/TypeProviders.fs +++ b/src/Compiler/TypedTree/TypeProviders.fs @@ -227,8 +227,13 @@ let TryTypeMemberArray (st: Tainted<_>, fullName, memberName, m, f) = [||] /// Try to access a member on a provided type, catching and reporting errors and checking the result is non-null, +#if NO_CHECKNULLS let TryTypeMemberNonNull<'T, 'U when 'U : null and 'U : not struct>(st: Tainted<'T>, fullName, memberName, m, recover: 'U, (f: 'T -> 'U)) : Tainted<'U> = match TryTypeMember(st, fullName, memberName, m, recover, f) with +#else +let TryTypeMemberNonNull<'T, 'U when 'U : __notnull and 'U : not struct>(st: Tainted<'T>, fullName, memberName, m, recover: 'U, (f: 'T -> 'U __withnull)) : Tainted<'U> = + match TryTypeMember<'T, 'U __withnull>(st, fullName, memberName, m, withNull recover, f) with +#endif | Tainted.Null -> errorR(Error(FSComp.SR.etUnexpectedNullFromProvidedTypeMember(fullName, memberName), m)) st.PApplyNoFailure(fun _ -> recover) @@ -338,7 +343,10 @@ type ProvidedTypeContext = for KeyValue (st, tcref) in d2.Force() do dict.TryAdd(st, f tcref) |> ignore dict)) -[] +[] +#if NO_CHECKNULLS +[] +#endif type ProvidedType (x: Type, ctxt: ProvidedTypeContext) = inherit ProvidedMemberInfo(x, ctxt) @@ -406,7 +414,7 @@ type ProvidedType (x: Type, ctxt: ProvidedTypeContext) = /// Type.BaseType can be null when Type is interface or object member _.BaseType = x.BaseType |> ProvidedType.Create ctxt - member _.GetStaticParameters(provider: ITypeProvider) = provider.GetStaticParameters x |> ProvidedParameterInfo.CreateArray ctxt + member _.GetStaticParameters(provider: ITypeProvider) : ProvidedParameterInfo[] MaybeNull = provider.GetStaticParameters x |> ProvidedParameterInfo.CreateArray ctxt /// Type.GetElementType can be null if i.e. Type is not array\pointer\byref type member _.GetElementType() = x.GetElementType() |> ProvidedType.Create ctxt @@ -465,17 +473,28 @@ type ProvidedType (x: Type, ctxt: ProvidedTypeContext) = let argTypes = args |> Array.map (fun arg -> arg.RawSystemType) ProvidedType.CreateNoContext(x.MakeGenericType(argTypes)) - member _.AsProvidedVar name = ProvidedVar.Create ctxt (Var(name, x)) + member _.AsProvidedVar name = ProvidedVar.CreateNonNull ctxt (Quotations.Var(name, x)) + + static member Create ctxt x : ProvidedType MaybeNull = + match x with + | Null -> null + | NonNull t -> ProvidedType (t, ctxt) - static member Create ctxt x = match x with null -> null | t -> ProvidedType (t, ctxt) + static member CreateNonNull ctxt x = ProvidedType (x, ctxt) - static member CreateWithNullCheck ctxt name x = match x with null -> nullArg name | t -> ProvidedType (t, ctxt) + static member CreateWithNullCheck ctxt name x = + match x with + | Null -> nullArg name + | t -> ProvidedType (t, ctxt) - static member CreateArray ctxt xs = match xs with null -> null | _ -> xs |> Array.map (ProvidedType.Create ctxt) + static member CreateArray ctxt (xs: Type[] MaybeNull) : ProvidedType[] MaybeNull = + match xs with + | Null -> null + | NonNull xs -> xs |> Array.map (ProvidedType.CreateNonNull ctxt) - static member CreateNoContext (x: Type) = ProvidedType.Create ProvidedTypeContext.Empty x + static member CreateNoContext (x:Type) = ProvidedType.Create ProvidedTypeContext.Empty x - static member Void = ProvidedType.CreateNoContext typeof + static member Void = ProvidedType.CreateNoContext typeof member _.Handle = x @@ -494,12 +513,17 @@ type ProvidedType (x: Type, ctxt: ProvidedTypeContext) = static member TaintedEquals (pt1: Tainted, pt2: Tainted) = Tainted.EqTainted (pt1.PApplyNoFailure(fun st -> st.Handle)) (pt2.PApplyNoFailure(fun st -> st.Handle)) -[] +#if NO_CHECKNULLS +[] +#endif type IProvidedCustomAttributeProvider = - abstract GetDefinitionLocationAttribute: provider: ITypeProvider -> (string * int * int) option - abstract GetXmlDocAttributes: provider: ITypeProvider -> string[] - abstract GetHasTypeProviderEditorHideMethodsAttribute: provider: ITypeProvider -> bool - abstract GetAttributeConstructorArgs: provider: ITypeProvider * attribName: string -> (obj option list * (string * obj option) list) option + abstract GetDefinitionLocationAttribute : provider: ITypeProvider -> (string MaybeNull * int * int) option + + abstract GetXmlDocAttributes : provider: ITypeProvider -> string[] + + abstract GetHasTypeProviderEditorHideMethodsAttribute : provider:ITypeProvider -> bool + + abstract GetAttributeConstructorArgs: provider:ITypeProvider * attribName:string -> (obj option list * (string * obj option) list) option type ProvidedCustomAttributeProvider (attributes :ITypeProvider -> seq) = let (|Member|_|) (s: string) (x: CustomAttributeNamedArgument) = if x.MemberName = s then Some x.TypedValue else None @@ -529,7 +553,7 @@ type ProvidedCustomAttributeProvider (attributes :ITypeProvider -> seq Seq.tryFind (findAttrib typeof) |> Option.map (fun a -> - let filePath = defaultArg (a.NamedArguments |> Seq.tryPick (function Member "FilePath" (Arg (:? string as v)) -> Some v | _ -> None)) null + let filePath : string MaybeNull = defaultArg (a.NamedArguments |> Seq.tryPick (function Member "FilePath" (Arg (:? string as v)) -> Some v | _ -> None)) null let line = defaultArg (a.NamedArguments |> Seq.tryPick (function Member "Line" (Arg (:? int as v)) -> Some v | _ -> None)) 0 let column = defaultArg (a.NamedArguments |> Seq.tryPick (function Member "Column" (Arg (:? int as v)) -> Some v | _ -> None)) 0 (filePath, line, column)) @@ -545,7 +569,10 @@ type ProvidedCustomAttributeProvider (attributes :ITypeProvider -> seq Seq.toArray -[] +[] +#if NO_CHECKNULLS +[] +#endif type ProvidedMemberInfo (x: MemberInfo, ctxt) = let provide () = ProvidedCustomAttributeProvider (fun _ -> x.CustomAttributes) :> IProvidedCustomAttributeProvider @@ -567,11 +594,14 @@ type ProvidedMemberInfo (x: MemberInfo, ctxt) = member _.GetAttributeConstructorArgs (provider, attribName) = provide().GetAttributeConstructorArgs (provider, attribName) -[] +[] +#if NO_CHECKNULLS +[] +#endif type ProvidedParameterInfo (x: ParameterInfo, ctxt) = let provide () = ProvidedCustomAttributeProvider (fun _ -> x.CustomAttributes) :> IProvidedCustomAttributeProvider - member _.Name = x.Name + member _.Name = let nm = x.Name in match box nm with null -> "" | _ -> nm member _.IsOut = x.IsOut @@ -584,12 +614,25 @@ type ProvidedParameterInfo (x: ParameterInfo, ctxt) = member _.HasDefaultValue = x.Attributes.HasFlag(ParameterAttributes.HasDefault) /// ParameterInfo.ParameterType cannot be null - member _.ParameterType = ProvidedType.CreateWithNullCheck ctxt "ParameterType" x.ParameterType - - static member Create ctxt x = match x with null -> null | t -> ProvidedParameterInfo (t, ctxt) - - static member CreateArray ctxt xs = match xs with null -> null | _ -> xs |> Array.map (ProvidedParameterInfo.Create ctxt) // TODO null wrong? - + member _.ParameterType = ProvidedType.CreateWithNullCheck ctxt "ParameterType" x.ParameterType + + static member Create ctxt (x: ParameterInfo MaybeNull) : ProvidedParameterInfo MaybeNull = + match x with + | Null -> null + | NonNull x -> ProvidedParameterInfo (x, ctxt) + + static member CreateNonNull ctxt x = ProvidedParameterInfo (x, ctxt) + + static member CreateArray ctxt (xs: ParameterInfo[] MaybeNull) : ProvidedParameterInfo[] MaybeNull = + match xs with + | Null -> null + | NonNull xs -> xs |> Array.map (ProvidedParameterInfo.CreateNonNull ctxt) + + static member CreateArrayNonNull ctxt xs : ProvidedParameterInfo[] = + match box xs with + | Null -> [| |] + | _ -> xs |> Array.map (ProvidedParameterInfo.CreateNonNull ctxt) + interface IProvidedCustomAttributeProvider with member _.GetHasTypeProviderEditorHideMethodsAttribute provider = provide().GetHasTypeProviderEditorHideMethodsAttribute provider @@ -609,7 +652,10 @@ type ProvidedParameterInfo (x: ParameterInfo, ctxt) = override _.GetHashCode() = assert false; x.GetHashCode() -[] +[] +#if NO_CHECKNULLS +[] +#endif type ProvidedAssembly (x: Assembly) = member _.GetName() = x.GetName() @@ -618,7 +664,7 @@ type ProvidedAssembly (x: Assembly) = member _.GetManifestModuleContents(provider: ITypeProvider) = provider.GetGeneratedAssemblyContents x - static member Create (x: Assembly) = match x with null -> null | t -> ProvidedAssembly t + static member Create x : ProvidedAssembly MaybeNull = match x with null -> null | t -> ProvidedAssembly (t) member _.Handle = x @@ -626,7 +672,10 @@ type ProvidedAssembly (x: Assembly) = override _.GetHashCode() = assert false; x.GetHashCode() -[] +[] +#if NO_CHECKNULLS +[] +#endif type ProvidedMethodBase (x: MethodBase, ctxt) = inherit ProvidedMemberInfo(x, ctxt) @@ -661,12 +710,14 @@ type ProvidedMethodBase (x: MethodBase, ctxt) = member _.Handle = x static member TaintedGetHashCode (x: Tainted) = - Tainted.GetHashCodeTainted (x.PApplyNoFailure(fun st -> (st.Name, st.DeclaringType.Assembly.FullName, st.DeclaringType.FullName))) + Tainted.GetHashCodeTainted + (x.PApplyNoFailure(fun st -> (st.Name, (nonNull (nonNull st.DeclaringType).Assembly).FullName, + (nonNull st.DeclaringType).FullName))) static member TaintedEquals (pt1: Tainted, pt2: Tainted) = Tainted.EqTainted (pt1.PApplyNoFailure(fun st -> st.Handle)) (pt2.PApplyNoFailure(fun st -> st.Handle)) - member _.GetStaticParametersForMethod(provider: ITypeProvider) = + member _.GetStaticParametersForMethod(provider: ITypeProvider) : ProvidedParameterInfo[] = let bindingFlags = BindingFlags.Instance ||| BindingFlags.NonPublic ||| BindingFlags.Public let staticParams = @@ -685,7 +736,7 @@ type ProvidedMethodBase (x: MethodBase, ctxt) = with err -> raise (StripException (StripException err)) paramsAsObj :?> ParameterInfo[] - staticParams |> ProvidedParameterInfo.CreateArray ctxt + staticParams |> ProvidedParameterInfo.CreateArrayNonNull ctxt member _.ApplyStaticArgumentsForMethod(provider: ITypeProvider, fullNameAfterArguments: string, staticArgs: obj[]) = let bindingFlags = BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.InvokeMethod @@ -702,7 +753,7 @@ type ProvidedMethodBase (x: MethodBase, ctxt) = [| typeof; typeof; typeof |], null) match meth with - | null -> failwith (FSComp.SR.estApplyStaticArgumentsForMethodNotImplemented()) + | Null -> failwith (FSComp.SR.estApplyStaticArgumentsForMethodNotImplemented()) | _ -> let mbAsObj = try meth.Invoke(provider, bindingFlags ||| BindingFlags.InvokeMethod, null, [| box x; box fullNameAfterArguments; box staticArgs |], null) @@ -712,18 +763,29 @@ type ProvidedMethodBase (x: MethodBase, ctxt) = | :? MethodBase as mb -> mb | _ -> failwith (FSComp.SR.estApplyStaticArgumentsForMethodNotImplemented()) match mb with - | :? MethodInfo as mi -> (mi |> ProvidedMethodInfo.Create ctxt: ProvidedMethodInfo) :> ProvidedMethodBase - | :? ConstructorInfo as ci -> (ci |> ProvidedConstructorInfo.Create ctxt: ProvidedConstructorInfo) :> ProvidedMethodBase + | :? MethodInfo as mi -> (mi |> ProvidedMethodInfo.CreateNonNull ctxt : ProvidedMethodInfo) :> ProvidedMethodBase + | :? ConstructorInfo as ci -> (ci |> ProvidedConstructorInfo.CreateNonNull ctxt : ProvidedConstructorInfo) :> ProvidedMethodBase | _ -> failwith (FSComp.SR.estApplyStaticArgumentsForMethodNotImplemented()) -[] +[] +#if NO_CHECKNULLS +[] +#endif type ProvidedFieldInfo (x: FieldInfo, ctxt) = inherit ProvidedMemberInfo(x, ctxt) - static member Create ctxt x = match x with null -> null | t -> ProvidedFieldInfo (t, ctxt) + static member CreateNonNull ctxt x = ProvidedFieldInfo (x, ctxt) + + static member Create ctxt x : ProvidedFieldInfo MaybeNull = + match x with + | Null -> null + | NonNull x -> ProvidedFieldInfo (x, ctxt) - static member CreateArray ctxt xs = match xs with null -> null | _ -> xs |> Array.map (ProvidedFieldInfo.Create ctxt) + static member CreateArray ctxt (xs: FieldInfo[] MaybeNull) : ProvidedFieldInfo[] MaybeNull = + match xs with + | Null -> null + | NonNull xs -> xs |> Array.map (ProvidedFieldInfo.CreateNonNull ctxt) member _.IsInitOnly = x.IsInitOnly @@ -758,15 +820,27 @@ type ProvidedFieldInfo (x: FieldInfo, ctxt) = static member TaintedEquals (pt1: Tainted, pt2: Tainted) = Tainted.EqTainted (pt1.PApplyNoFailure(fun st -> st.Handle)) (pt2.PApplyNoFailure(fun st -> st.Handle)) -[] +[] +#if NO_CHECKNULLS +[] +#endif type ProvidedMethodInfo (x: MethodInfo, ctxt) = inherit ProvidedMethodBase(x, ctxt) member _.ReturnType = x.ReturnType |> ProvidedType.CreateWithNullCheck ctxt "ReturnType" - static member Create ctxt x = match x with null -> null | t -> ProvidedMethodInfo (t, ctxt) + static member CreateNonNull ctxt (x: MethodInfo) : ProvidedMethodInfo = + ProvidedMethodInfo (x, ctxt) + + static member Create ctxt (x: MethodInfo MaybeNull) : ProvidedMethodInfo MaybeNull = + match x with + | Null -> null + | NonNull x -> ProvidedMethodInfo (x, ctxt) - static member CreateArray ctxt xs = match xs with null -> null | _ -> xs |> Array.map (ProvidedMethodInfo.Create ctxt) + static member CreateArray ctxt (xs: MethodInfo[] MaybeNull) : ProvidedMethodInfo[] MaybeNull = + match xs with + | Null -> null + | NonNull xs -> xs |> Array.map (ProvidedMethodInfo.CreateNonNull ctxt) member _.Handle = x @@ -776,7 +850,10 @@ type ProvidedMethodInfo (x: MethodInfo, ctxt) = override _.GetHashCode() = assert false; x.GetHashCode() -[] +[] +#if NO_CHECKNULLS +[] +#endif type ProvidedPropertyInfo (x: PropertyInfo, ctxt) = inherit ProvidedMemberInfo(x, ctxt) @@ -793,9 +870,17 @@ type ProvidedPropertyInfo (x: PropertyInfo, ctxt) = /// PropertyInfo.PropertyType cannot be null member _.PropertyType = x.PropertyType |> ProvidedType.CreateWithNullCheck ctxt "PropertyType" - static member Create ctxt x = match x with null -> null | t -> ProvidedPropertyInfo (t, ctxt) + static member CreateNonNull ctxt x = ProvidedPropertyInfo (x, ctxt) + + static member Create ctxt x : ProvidedPropertyInfo MaybeNull = + match x with + | Null -> null + | NonNull x -> ProvidedPropertyInfo (x, ctxt) - static member CreateArray ctxt xs = match xs with null -> null | _ -> xs |> Array.map (ProvidedPropertyInfo.Create ctxt) + static member CreateArray ctxt (xs: PropertyInfo[] MaybeNull) : ProvidedPropertyInfo[] MaybeNull = + match xs with + | Null -> null + | NonNull xs -> xs |> Array.map (ProvidedPropertyInfo.CreateNonNull ctxt) member _.Handle = x @@ -804,12 +889,17 @@ type ProvidedPropertyInfo (x: PropertyInfo, ctxt) = override _.GetHashCode() = assert false; x.GetHashCode() static member TaintedGetHashCode (x: Tainted) = - Tainted.GetHashCodeTainted (x.PApplyNoFailure(fun st -> (st.Name, st.DeclaringType.Assembly.FullName, st.DeclaringType.FullName))) + Tainted.GetHashCodeTainted + (x.PApplyNoFailure(fun st -> (st.Name, (nonNull (nonNull st.DeclaringType).Assembly).FullName, + (nonNull st.DeclaringType).FullName))) static member TaintedEquals (pt1: Tainted, pt2: Tainted) = Tainted.EqTainted (pt1.PApplyNoFailure(fun st -> st.Handle)) (pt2.PApplyNoFailure(fun st -> st.Handle)) -[] +[] +#if NO_CHECKNULLS +[] +#endif type ProvidedEventInfo (x: EventInfo, ctxt) = inherit ProvidedMemberInfo(x, ctxt) @@ -819,11 +909,19 @@ type ProvidedEventInfo (x: EventInfo, ctxt) = /// EventInfo.EventHandlerType cannot be null member _.EventHandlerType = x.EventHandlerType |> ProvidedType.CreateWithNullCheck ctxt "EventHandlerType" - - static member Create ctxt x = match x with null -> null | t -> ProvidedEventInfo (t, ctxt) - - static member CreateArray ctxt xs = match xs with null -> null | _ -> xs |> Array.map (ProvidedEventInfo.Create ctxt) - + + static member CreateNonNull ctxt x = ProvidedEventInfo (x, ctxt) + + static member Create ctxt x : ProvidedEventInfo MaybeNull = + match x with + | Null -> null + | NonNull x -> ProvidedEventInfo (x, ctxt) + + static member CreateArray ctxt (xs: EventInfo[] MaybeNull) : ProvidedEventInfo[] MaybeNull = + match xs with + | Null -> null + | NonNull xs -> xs |> Array.map (ProvidedEventInfo.CreateNonNull ctxt) + member _.Handle = x override _.Equals y = assert false; match y with :? ProvidedEventInfo as y -> x.Equals y.Handle | _ -> false @@ -831,18 +929,31 @@ type ProvidedEventInfo (x: EventInfo, ctxt) = override _.GetHashCode() = assert false; x.GetHashCode() static member TaintedGetHashCode (x: Tainted) = - Tainted.GetHashCodeTainted (x.PApplyNoFailure(fun st -> (st.Name, st.DeclaringType.Assembly.FullName, st.DeclaringType.FullName))) + Tainted.GetHashCodeTainted + (x.PApplyNoFailure(fun st -> (st.Name, (nonNull (nonNull st.DeclaringType).Assembly).FullName, + (nonNull st.DeclaringType).FullName))) static member TaintedEquals (pt1: Tainted, pt2: Tainted) = Tainted.EqTainted (pt1.PApplyNoFailure(fun st -> st.Handle)) (pt2.PApplyNoFailure(fun st -> st.Handle)) -[] +[] +#if NO_CHECKNULLS +[] +#endif type ProvidedConstructorInfo (x: ConstructorInfo, ctxt) = inherit ProvidedMethodBase(x, ctxt) - static member Create ctxt x = match x with null -> null | t -> ProvidedConstructorInfo (t, ctxt) + static member CreateNonNull ctxt x = ProvidedConstructorInfo (x, ctxt) + + static member Create ctxt (x: ConstructorInfo MaybeNull) : ProvidedConstructorInfo MaybeNull = + match x with + | Null -> null + | NonNull x -> ProvidedConstructorInfo (x, ctxt) - static member CreateArray ctxt xs = match xs with null -> null | _ -> xs |> Array.map (ProvidedConstructorInfo.Create ctxt) + static member CreateArray ctxt (xs: ConstructorInfo[] MaybeNull) : ProvidedConstructorInfo[] MaybeNull = + match xs with + | Null -> null + | NonNull xs -> xs |> Array.map (ProvidedConstructorInfo.CreateNonNull ctxt) member _.Handle = x @@ -875,7 +986,11 @@ type ProvidedExprType = | ProvidedIfThenElseExpr of ProvidedExpr * ProvidedExpr * ProvidedExpr | ProvidedVarExpr of ProvidedVar +#if NO_CHECKNULLS [] +#else +[] +#endif type ProvidedExpr (x: Expr, ctxt) = member _.Type = x.Type |> ProvidedType.Create ctxt @@ -889,74 +1004,94 @@ type ProvidedExpr (x: Expr, ctxt) = member _.GetExprType() = match x with | Patterns.NewObject(ctor, args) -> - Some (ProvidedNewObjectExpr (ProvidedConstructorInfo.Create ctxt ctor, [| for a in args -> ProvidedExpr.Create ctxt a |])) + Some (ProvidedNewObjectExpr (ProvidedConstructorInfo.CreateNonNull ctxt ctor, [| for a in args -> ProvidedExpr.CreateNonNull ctxt a |])) | Patterns.WhileLoop(guardExpr, bodyExpr) -> - Some (ProvidedWhileLoopExpr (ProvidedExpr.Create ctxt guardExpr, ProvidedExpr.Create ctxt bodyExpr)) + Some (ProvidedWhileLoopExpr (ProvidedExpr.CreateNonNull ctxt guardExpr, ProvidedExpr.CreateNonNull ctxt bodyExpr)) | Patterns.NewDelegate(ty, vs, expr) -> - Some (ProvidedNewDelegateExpr(ProvidedType.Create ctxt ty, ProvidedVar.CreateArray ctxt (List.toArray vs), ProvidedExpr.Create ctxt expr)) + Some (ProvidedNewDelegateExpr(ProvidedType.CreateNonNull ctxt ty, ProvidedVar.CreateArray ctxt (List.toArray vs), ProvidedExpr.CreateNonNull ctxt expr)) | Patterns.Call(objOpt, meth, args) -> - Some (ProvidedCallExpr((match objOpt with None -> None | Some obj -> Some (ProvidedExpr.Create ctxt obj)), - ProvidedMethodInfo.Create ctxt meth, [| for a in args -> ProvidedExpr.Create ctxt a |])) + Some (ProvidedCallExpr((match objOpt with None -> None | Some obj -> Some (ProvidedExpr.CreateNonNull ctxt obj)), + ProvidedMethodInfo.CreateNonNull ctxt meth, [| for a in args -> ProvidedExpr.CreateNonNull ctxt a |])) | Patterns.DefaultValue ty -> - Some (ProvidedDefaultExpr (ProvidedType.Create ctxt ty)) + Some (ProvidedDefaultExpr (ProvidedType.CreateNonNull ctxt ty)) | Patterns.Value(obj, ty) -> - Some (ProvidedConstantExpr (obj, ProvidedType.Create ctxt ty)) + Some (ProvidedConstantExpr (obj, ProvidedType.CreateNonNull ctxt ty)) | Patterns.Coerce(arg, ty) -> - Some (ProvidedTypeAsExpr (ProvidedExpr.Create ctxt arg, ProvidedType.Create ctxt ty)) + Some (ProvidedTypeAsExpr (ProvidedExpr.CreateNonNull ctxt arg, ProvidedType.CreateNonNull ctxt ty)) | Patterns.NewTuple args -> Some (ProvidedNewTupleExpr(ProvidedExpr.CreateArray ctxt (Array.ofList args))) | Patterns.TupleGet(arg, n) -> - Some (ProvidedTupleGetExpr (ProvidedExpr.Create ctxt arg, n)) + Some (ProvidedTupleGetExpr (ProvidedExpr.CreateNonNull ctxt arg, n)) | Patterns.NewArray(ty, args) -> - Some (ProvidedNewArrayExpr(ProvidedType.Create ctxt ty, ProvidedExpr.CreateArray ctxt (Array.ofList args))) + Some (ProvidedNewArrayExpr(ProvidedType.CreateNonNull ctxt ty, ProvidedExpr.CreateArray ctxt (Array.ofList args))) | Patterns.Sequential(e1, e2) -> - Some (ProvidedSequentialExpr(ProvidedExpr.Create ctxt e1, ProvidedExpr.Create ctxt e2)) + Some (ProvidedSequentialExpr(ProvidedExpr.CreateNonNull ctxt e1, ProvidedExpr.CreateNonNull ctxt e2)) | Patterns.Lambda(v, body) -> - Some (ProvidedLambdaExpr (ProvidedVar.Create ctxt v, ProvidedExpr.Create ctxt body)) + Some (ProvidedLambdaExpr (ProvidedVar.CreateNonNull ctxt v, ProvidedExpr.CreateNonNull ctxt body)) | Patterns.TryFinally(b1, b2) -> - Some (ProvidedTryFinallyExpr (ProvidedExpr.Create ctxt b1, ProvidedExpr.Create ctxt b2)) + Some (ProvidedTryFinallyExpr (ProvidedExpr.CreateNonNull ctxt b1, ProvidedExpr.CreateNonNull ctxt b2)) | Patterns.TryWith(b, v1, e1, v2, e2) -> - Some (ProvidedTryWithExpr (ProvidedExpr.Create ctxt b, ProvidedVar.Create ctxt v1, ProvidedExpr.Create ctxt e1, ProvidedVar.Create ctxt v2, ProvidedExpr.Create ctxt e2)) + Some (ProvidedTryWithExpr (ProvidedExpr.CreateNonNull ctxt b, ProvidedVar.CreateNonNull ctxt v1, ProvidedExpr.CreateNonNull ctxt e1, ProvidedVar.CreateNonNull ctxt v2, ProvidedExpr.CreateNonNull ctxt e2)) #if PROVIDED_ADDRESS_OF - | Patterns.AddressOf e -> Some (ProvidedAddressOfExpr (ProvidedExpr.Create ctxt e)) + | Patterns.AddressOf e -> Some (ProvidedAddressOfExpr (ProvidedExpr.CreateNonNull ctxt e)) #endif | Patterns.TypeTest(e, ty) -> - Some (ProvidedTypeTestExpr(ProvidedExpr.Create ctxt e, ProvidedType.Create ctxt ty)) + Some (ProvidedTypeTestExpr(ProvidedExpr.CreateNonNull ctxt e, ProvidedType.CreateNonNull ctxt ty)) | Patterns.Let(v, e, b) -> - Some (ProvidedLetExpr (ProvidedVar.Create ctxt v, ProvidedExpr.Create ctxt e, ProvidedExpr.Create ctxt b)) + Some (ProvidedLetExpr (ProvidedVar.CreateNonNull ctxt v, ProvidedExpr.CreateNonNull ctxt e, ProvidedExpr.CreateNonNull ctxt b)) | Patterns.ForIntegerRangeLoop (v, e1, e2, e3) -> - Some (ProvidedForIntegerRangeLoopExpr (ProvidedVar.Create ctxt v, ProvidedExpr.Create ctxt e1, ProvidedExpr.Create ctxt e2, ProvidedExpr.Create ctxt e3)) + Some (ProvidedForIntegerRangeLoopExpr (ProvidedVar.CreateNonNull ctxt v, ProvidedExpr.CreateNonNull ctxt e1, ProvidedExpr.CreateNonNull ctxt e2, ProvidedExpr.CreateNonNull ctxt e3)) | Patterns.VarSet(v, e) -> - Some (ProvidedVarSetExpr (ProvidedVar.Create ctxt v, ProvidedExpr.Create ctxt e)) + Some (ProvidedVarSetExpr (ProvidedVar.CreateNonNull ctxt v, ProvidedExpr.CreateNonNull ctxt e)) | Patterns.IfThenElse(g, t, e) -> - Some (ProvidedIfThenElseExpr (ProvidedExpr.Create ctxt g, ProvidedExpr.Create ctxt t, ProvidedExpr.Create ctxt e)) + Some (ProvidedIfThenElseExpr (ProvidedExpr.CreateNonNull ctxt g, ProvidedExpr.CreateNonNull ctxt t, ProvidedExpr.CreateNonNull ctxt e)) | Patterns.Var v -> - Some (ProvidedVarExpr (ProvidedVar.Create ctxt v)) + Some (ProvidedVarExpr (ProvidedVar.CreateNonNull ctxt v)) | _ -> None + static member Create ctxt t : ProvidedExpr MaybeNull = + match box t with + | Null -> null + | _ -> ProvidedExpr (t, ctxt) - static member Create ctxt t = match box t with null -> null | _ -> ProvidedExpr (t, ctxt) + static member CreateNonNull ctxt t : ProvidedExpr = + ProvidedExpr (t, ctxt) - static member CreateArray ctxt xs = match xs with null -> null | _ -> xs |> Array.map (ProvidedExpr.Create ctxt) + static member CreateArray ctxt xs : ProvidedExpr[] = + match box xs with + | Null -> [| |] + | _ -> xs |> Array.map (ProvidedExpr.CreateNonNull ctxt) override _.Equals y = match y with :? ProvidedExpr as y -> x.Equals y.Handle | _ -> false override _.GetHashCode() = x.GetHashCode() +#if NO_CHECKNULLS [] +#else +[] +#endif type ProvidedVar (x: Var, ctxt) = member _.Type = x.Type |> ProvidedType.Create ctxt member _.Name = x.Name member _.IsMutable = x.IsMutable member _.Handle = x member _.Context = ctxt - static member Create ctxt t = match box t with null -> null | _ -> ProvidedVar (t, ctxt) - static member CreateArray ctxt xs = match xs with null -> null | _ -> xs |> Array.map (ProvidedVar.Create ctxt) + + static member CreateNonNull ctxt t = + ProvidedVar (t, ctxt) + + static member CreateArray ctxt xs : ProvidedVar[] = + match box xs with + | Null -> [| |] + | _ -> xs |> Array.map (ProvidedVar.CreateNonNull ctxt) + override _.Equals y = match y with :? ProvidedVar as y -> x.Equals y.Handle | _ -> false + override _.GetHashCode() = x.GetHashCode() /// Get the provided invoker expression for a particular use of a method. let GetInvokerExpression (provider: ITypeProvider, methodBase: ProvidedMethodBase, paramExprs: ProvidedVar[]) = - provider.GetInvokerExpression(methodBase.Handle, [| for p in paramExprs -> Expr.Var p.Handle |]) |> ProvidedExpr.Create methodBase.Context + provider.GetInvokerExpression(methodBase.Handle, [| for p in paramExprs -> Quotations.Expr.Var p.Handle |]) |> ProvidedExpr.Create methodBase.Context /// Compute the Name or FullName property of a provided type, reporting appropriate errors let CheckAndComputeProvidedNameProperty(m, st: Tainted, proj, propertyString) = @@ -976,8 +1111,7 @@ let ValidateAttributesOfProvidedType (m, st: Tainted) = errorR(Error(FSComp.SR.etMustNotBeGeneric fullName, m)) if TryTypeMember(st, fullName, "IsArray", m, false, fun st->st.IsArray) |> unmarshal then errorR(Error(FSComp.SR.etMustNotBeAnArray fullName, m)) - TryTypeMemberNonNull(st, fullName, "GetInterfaces", m, [||], fun st -> st.GetInterfaces()) |> ignore - + TryTypeMemberNonNull(st, fullName, "GetInterfaces", m, [||], fun st -> st.GetInterfaces()) |> ignore /// Verify that a provided type has the expected name let ValidateExpectedName m expectedPath expectedName (st: Tainted) = @@ -985,19 +1119,23 @@ let ValidateExpectedName m expectedPath expectedName (st: Tainted) if name <> expectedName then raise (TypeProviderError(FSComp.SR.etProvidedTypeHasUnexpectedName(expectedName, name), st.TypeProviderDesignation, m)) +#if NO_CHECKNULLS let namespaceName = TryTypeMember(st, name, "Namespace", m, "", fun st -> st.Namespace) |> unmarshal +#else + let namespaceName = TryTypeMember<_, string?>(st, name, "Namespace", m, "", fun st -> st.Namespace) |> unmarshal // TODO NULLNESS: why is this explicit instantiation needed? +#endif let rec declaringTypes (st: Tainted) accu = match TryTypeMember(st, name, "DeclaringType", m, null, fun st -> st.DeclaringType) with | Tainted.Null -> accu - | dt -> declaringTypes dt (CheckAndComputeProvidedNameProperty(m, dt, (fun dt -> dt.Name), "Name") :: accu) + | Tainted.NonNull dt -> declaringTypes dt (CheckAndComputeProvidedNameProperty(m, dt, (fun dt -> dt.Name), "Name") :: accu) let path = - [| match namespaceName with - | null -> () - | _ -> yield! namespaceName.Split([|'.'|]) - yield! declaringTypes st [] |] - + [| match namespaceName with + | Null -> () + | NonNull namespaceName -> yield! namespaceName.Split([|'.'|]) + yield! declaringTypes st [] |] + if path <> expectedPath then let expectedPath = String.Join(".", expectedPath) let path = String.Join(".", path) @@ -1008,7 +1146,11 @@ let ValidateProvidedTypeAfterStaticInstantiation(m, st: Tainted, e // Do all the calling into st up front with recovery let fullName, namespaceName, usedMembers = let name = CheckAndComputeProvidedNameProperty(m, st, (fun st -> st.Name), "Name") +#if NO_CHECKNULLS let namespaceName = TryTypeMember(st, name, "Namespace", m, FSComp.SR.invalidNamespaceForProvidedType(), fun st -> st.Namespace) |> unmarshal +#else + let namespaceName = TryTypeMember<_, string?>(st, name, "Namespace", m, FSComp.SR.invalidNamespaceForProvidedType(), fun st -> st.Namespace) |> unmarshal +#endif let fullName = TryTypeMemberNonNull(st, name, "FullName", m, FSComp.SR.invalidFullNameForProvidedType(), fun st -> st.FullName) |> unmarshal ValidateExpectedName m expectedPath expectedName st // Must be able to call (GetMethods|GetEvents|GetProperties|GetNestedTypes|GetConstructors)(bindingFlags). @@ -1033,19 +1175,18 @@ let ValidateProvidedTypeAfterStaticInstantiation(m, st: Tainted, e for mi in usedMembers do match mi with | Tainted.Null -> errorR(Error(FSComp.SR.etNullMember fullName, m)) - | _ -> + | Tainted.NonNull _ -> let memberName = TryMemberMember(mi, fullName, "Name", "Name", m, "invalid provided type member name", fun mi -> mi.Name) |> unmarshal if String.IsNullOrEmpty memberName then errorR(Error(FSComp.SR.etNullOrEmptyMemberName fullName, m)) else let miDeclaringType = TryMemberMember(mi, fullName, memberName, "DeclaringType", m, ProvidedType.CreateNoContext(typeof), fun mi -> mi.DeclaringType) - match miDeclaringType with // Generated nested types may have null DeclaringType | Tainted.Null when mi.OfType().IsSome -> () | Tainted.Null -> errorR(Error(FSComp.SR.etNullMemberDeclaringType(fullName, memberName), m)) - | _ -> + | Tainted.NonNull miDeclaringType -> let miDeclaringTypeFullName = TryMemberMember (miDeclaringType, fullName, memberName, "FullName", m, "invalid declaring type full name", @@ -1062,11 +1203,9 @@ let ValidateProvidedTypeAfterStaticInstantiation(m, st: Tainted, e if not isPublic || isGenericMethod then errorR(Error(FSComp.SR.etMethodHasRequirements(fullName, memberName), m)) | None -> - match mi.OfType() with | Some subType -> ValidateAttributesOfProvidedType(m, subType) | None -> - match mi.OfType() with | Some pi -> // Property must have a getter or setter @@ -1091,8 +1230,8 @@ let ValidateProvidedTypeAfterStaticInstantiation(m, st: Tainted, e | true, false -> errorR(Error(FSComp.SR.etPropertyHasSetterButNoCanWrite(memberName, fullName), m)) if not canRead && not canWrite then errorR(Error(FSComp.SR.etPropertyNeedsCanWriteOrCanRead(memberName, fullName), m)) - | None -> + | None -> match mi.OfType() with | Some ei -> // Event must have adder and remover @@ -1104,11 +1243,9 @@ let ValidateProvidedTypeAfterStaticInstantiation(m, st: Tainted, e | _, Tainted.Null -> errorR(Error(FSComp.SR.etEventNoRemove(memberName, fullName), m)) | _, _ -> () | None -> - match mi.OfType() with | Some _ -> () // TODO: Constructors must be public etc. | None -> - match mi.OfType() with | Some _ -> () // TODO: Fields must be public, literals must have a value etc. | None -> @@ -1118,7 +1255,11 @@ let ValidateProvidedTypeDefinition(m, st: Tainted, expectedPath: s // Validate the Name, Namespace and FullName properties let name = CheckAndComputeProvidedNameProperty(m, st, (fun st -> st.Name), "Name") +#if NO_CHECKNULLS let _namespaceName = TryTypeMember(st, name, "Namespace", m, FSComp.SR.invalidNamespaceForProvidedType(), fun st -> st.Namespace) |> unmarshal +#else + let _namespaceName = TryTypeMember<_, (string?)>(st, name, "Namespace", m, FSComp.SR.invalidNamespaceForProvidedType(), fun st -> st.Namespace) |> unmarshal +#endif let _fullname = TryTypeMemberNonNull(st, name, "FullName", m, FSComp.SR.invalidFullNameForProvidedType(), fun st -> st.FullName) |> unmarshal ValidateExpectedName m expectedPath expectedName st @@ -1129,15 +1270,15 @@ let ValidateProvidedTypeDefinition(m, st: Tainted, expectedPath: s | -1 -> () | n -> errorR(Error(FSComp.SR.etIllegalCharactersInTypeName(string expectedName[n], expectedName), m)) - let staticParameters = st.PApplyWithProvider((fun (st, provider) -> st.GetStaticParameters provider), range=m) - if staticParameters.PUntaint((fun a -> a.Length), m) = 0 then + let staticParameters : Tainted = st.PApplyWithProvider((fun (st, provider) -> st.GetStaticParameters provider), range=m) + if staticParameters.PUntaint((fun a -> (nonNull a).Length), m) = 0 then ValidateProvidedTypeAfterStaticInstantiation(m, st, expectedPath, expectedName) /// Resolve a (non-nested) provided type given a full namespace name and a type name. /// May throw an exception which will be turned into an error message by one of the 'Try' function below. /// If resolution is successful the type is then validated. -let ResolveProvidedType (resolver: Tainted, m, moduleOrNamespace: string[], typeName) = +let ResolveProvidedType (resolver: Tainted, m, moduleOrNamespace: string[], typeName) : Tainted = let displayName = String.Join(".", moduleOrNamespace) // Try to find the type in the given provided namespace @@ -1167,7 +1308,7 @@ let ResolveProvidedType (resolver: Tainted, m, moduleOrNamespace: match tryNamespaces providedNamespaces with | None -> resolver.PApply((fun _ -> null), m) | Some res -> res - + /// Try to resolve a type against the given host with the given resolution environment. let TryResolveProvidedType(resolver: Tainted, m, moduleOrNamespace, typeName) = try @@ -1184,14 +1325,14 @@ let ILPathToProvidedType (st: Tainted, m) = match st.PApply((fun st -> st.DeclaringType), m) with | Tainted.Null -> match st.PUntaint((fun st -> st.Namespace), m) with - | Null -> typeName + | Null -> typeName | NonNull ns -> ns + "." + typeName | _ -> typeName let rec encContrib (st: Tainted) = match st.PApply((fun st ->st.DeclaringType), m) with | Tainted.Null -> [] - | enc -> encContrib enc @ [ nameContrib enc ] + | Tainted.NonNull enc -> encContrib enc @ [ nameContrib enc ] encContrib st, nameContrib st @@ -1212,7 +1353,6 @@ let TryApplyProvidedMethod(methBeforeArgs: Tainted, staticAr let staticParams = methBeforeArgs.PApplyWithProvider((fun (mb, resolver) -> mb.GetStaticParametersForMethod resolver), range=m) let mangledName = ComputeMangledNameForApplyStaticParameters(nm, staticArgs, staticParams, m) mangledName - match methBeforeArgs.PApplyWithProvider((fun (mb, provider) -> mb.ApplyStaticArgumentsForMethod(provider, mangledName, staticArgs)), range=m) with | Tainted.Null -> None | Tainted.NonNull methWithArguments -> @@ -1227,7 +1367,7 @@ let TryApplyProvidedType(typeBeforeArguments: Tainted, optGenerate if staticArgs.Length = 0 then Some (typeBeforeArguments, (fun () -> ())) else - + let fullTypePathAfterArguments = // If there is a generated type name, then use that match optGeneratedTypePath with @@ -1236,10 +1376,10 @@ let TryApplyProvidedType(typeBeforeArguments: Tainted, optGenerate // Otherwise, use the full path of the erased type, including mangled arguments let nm = typeBeforeArguments.PUntaint((fun x -> x.Name), m) let enc, _ = ILPathToProvidedType (typeBeforeArguments, m) - let staticParams = typeBeforeArguments.PApplyWithProvider((fun (mb, resolver) -> mb.GetStaticParameters resolver), range=m) + let staticParams : Tainted = typeBeforeArguments.PApplyWithProvider((fun (st, resolver) -> st.GetStaticParameters resolver |> nonNull), range=m) let mangledName = ComputeMangledNameForApplyStaticParameters(nm, staticArgs, staticParams, m) enc @ [ mangledName ] - + match typeBeforeArguments.PApplyWithProvider((fun (typeBeforeArguments, provider) -> typeBeforeArguments.ApplyStaticArguments(provider, Array.ofList fullTypePathAfterArguments, staticArgs)), range=m) with | Tainted.Null -> None | Tainted.NonNull typeWithArguments -> @@ -1253,7 +1393,7 @@ let TryApplyProvidedType(typeBeforeArguments: Tainted, optGenerate /// Given a mangled name reference to a non-nested provided type, resolve it. /// If necessary, demangle its static arguments before applying them. let TryLinkProvidedType(resolver: Tainted, moduleOrNamespace: string[], typeLogicalName: string, range: range) = - + // Demangle the static parameters let typeName, argNamesAndValues = try @@ -1266,7 +1406,7 @@ let TryLinkProvidedType(resolver: Tainted, moduleOrNamespace: str match typeBeforeArguments with | Tainted.Null -> None - | _ -> + | Tainted.NonNull typeBeforeArguments -> // Take the static arguments (as strings, taken from the text in the reference we're relinking), // and convert them to objects of the appropriate type, based on the expected kind. let staticParameters = @@ -1274,7 +1414,7 @@ let TryLinkProvidedType(resolver: Tainted, moduleOrNamespace: str typeBeforeArguments.GetStaticParameters resolver),range=range0) let staticParameters = staticParameters.PApplyArray(id, "", range) - + let staticArgs = staticParameters |> Array.map (fun sp -> let typeBeforeArgumentsName = typeBeforeArguments.PUntaint ((fun st -> st.Name), range) @@ -1312,7 +1452,7 @@ let TryLinkProvidedType(resolver: Tainted, moduleOrNamespace: str | NonNull v -> v else error(Error(FSComp.SR.etProvidedTypeReferenceMissingArgument spName, range0))) - + match TryApplyProvidedType(typeBeforeArguments, None, staticArgs, range0) with | Some (typeWithArguments, checkTypeName) -> @@ -1321,7 +1461,7 @@ let TryLinkProvidedType(resolver: Tainted, moduleOrNamespace: str | None -> None /// Get the parts of a .NET namespace. Special rules: null means global, empty is not allowed. -let GetPartsOfNamespaceRecover(namespaceName: string) = +let GetPartsOfNamespaceRecover(namespaceName: string MaybeNull) = match namespaceName with | Null -> [] | NonNull namespaceName -> @@ -1329,7 +1469,7 @@ let GetPartsOfNamespaceRecover(namespaceName: string) = else splitNamespace (nonNull namespaceName) /// Get the parts of a .NET namespace. Special rules: null means global, empty is not allowed. -let GetProvidedNamespaceAsPath (m, resolver: Tainted, namespaceName: string) = +let GetProvidedNamespaceAsPath (m, resolver: Tainted, namespaceName:string MaybeNull) = match namespaceName with | Null -> [] | NonNull namespaceName -> @@ -1342,7 +1482,7 @@ let GetFSharpPathToProvidedType (st: Tainted, range) = // Can't use st.Fullname because it may be like IEnumerable // We want [System;Collections;Generic] let namespaceParts = GetPartsOfNamespaceRecover(st.PUntaint((fun st -> st.Namespace), range)) - let rec walkUpNestedClasses(st: Tainted, soFar) = + let rec walkUpNestedClasses(st: Tainted, soFar) = match st with | Tainted.Null -> soFar | Tainted.NonNull st -> walkUpNestedClasses(st.PApply((fun st ->st.DeclaringType), range), soFar) @ [st.PUntaint((fun st -> st.Name), range)] @@ -1359,8 +1499,8 @@ let GetOriginalILAssemblyRefOfProvidedAssembly (assembly: Tainted, range) = - - let aref = GetOriginalILAssemblyRefOfProvidedAssembly (st.PApply((fun st -> st.Assembly), range), range) + + let aref = GetOriginalILAssemblyRefOfProvidedAssembly (st.PApply((fun st -> nonNull st.Assembly), range), range) // NULLNESS TODO: why is explicit instantiation needed here let scoperef = ILScopeRef.Assembly aref let enc, nm = ILPathToProvidedType (st, range) let tref = ILTypeRef.Create(scoperef, enc, nm) @@ -1379,7 +1519,6 @@ type ProviderGeneratedType = ProviderGeneratedType of ilOrigTyRef: ILTypeRef * i /// names in the statically linked, embedded assembly, plus what types are nested in side what types. type ProvidedAssemblyStaticLinkingMap = { ILTypeMap: Dictionary } - static member CreateNew() = { ILTypeMap = Dictionary() } diff --git a/src/Compiler/TypedTree/TypeProviders.fsi b/src/Compiler/TypedTree/TypeProviders.fsi index b1e3ccaf85c..6b264a118e7 100755 --- a/src/Compiler/TypedTree/TypeProviders.fsi +++ b/src/Compiler/TypedTree/TypeProviders.fsi @@ -9,6 +9,7 @@ module internal rec FSharp.Compiler.TypeProviders open System open System.Collections.Concurrent open System.Collections.Generic +open Internal.Utilities.Library open FSharp.Core.CompilerServices open FSharp.Compiler.AbstractIL.IL open FSharp.Compiler.Text @@ -90,7 +91,10 @@ type ProvidedTypeContext = /// Map the TyconRef objects, if any member RemapTyconRefs: (obj -> obj) -> ProvidedTypeContext -[] +[] +#if NO_CHECKNULLS +[] +#endif type ProvidedType = inherit ProvidedMemberInfo @@ -110,7 +114,7 @@ type ProvidedType = member Assembly: ProvidedAssembly - member BaseType: ProvidedType + member BaseType: ProvidedType MaybeNull member GetNestedType: string -> ProvidedType @@ -204,9 +208,10 @@ type ProvidedType = static member TaintedEquals: Tainted * Tainted -> bool +#if NO_CHECKNULLS [] +#endif type IProvidedCustomAttributeProvider = - abstract GetHasTypeProviderEditorHideMethodsAttribute: provider: ITypeProvider -> bool abstract GetDefinitionLocationAttribute: provider: ITypeProvider -> (string * int * int) option @@ -215,10 +220,12 @@ type IProvidedCustomAttributeProvider = abstract GetAttributeConstructorArgs: provider: ITypeProvider * attribName: string -> (obj option list * (string * obj option) list) option - -[] -type ProvidedAssembly = - + +[] +#if NO_CHECKNULLS +[] +#endif +type ProvidedAssembly = member GetName: unit -> System.Reflection.AssemblyName member FullName: string @@ -227,18 +234,23 @@ type ProvidedAssembly = member Handle: System.Reflection.Assembly -[] -type ProvidedMemberInfo = +[] +#if NO_CHECKNULLS +[] +#endif +type ProvidedMemberInfo = member Name: string - member DeclaringType: ProvidedType - - interface IProvidedCustomAttributeProvider + member DeclaringType: ProvidedType MaybeNull -[] -type ProvidedMethodBase = + interface IProvidedCustomAttributeProvider +[] +#if NO_CHECKNULLS +[] +#endif +type ProvidedMethodBase = inherit ProvidedMemberInfo member IsGenericMethod: bool @@ -273,8 +285,11 @@ type ProvidedMethodBase = static member TaintedEquals: Tainted * Tainted -> bool -[] -type ProvidedMethodInfo = +[] +#if NO_CHECKNULLS +[] +#endif +type ProvidedMethodInfo = inherit ProvidedMethodBase @@ -282,8 +297,11 @@ type ProvidedMethodInfo = member MetadataToken: int -[] -type ProvidedParameterInfo = +[] +#if NO_CHECKNULLS +[] +#endif +type ProvidedParameterInfo = member Name: string @@ -301,8 +319,11 @@ type ProvidedParameterInfo = interface IProvidedCustomAttributeProvider -[] -type ProvidedFieldInfo = +[] +#if NO_CHECKNULLS +[] +#endif +type ProvidedFieldInfo = inherit ProvidedMemberInfo @@ -330,8 +351,11 @@ type ProvidedFieldInfo = static member TaintedEquals: Tainted * Tainted -> bool -[] -type ProvidedPropertyInfo = +[] +#if NO_CHECKNULLS +[] +#endif +type ProvidedPropertyInfo = inherit ProvidedMemberInfo @@ -351,8 +375,11 @@ type ProvidedPropertyInfo = static member TaintedEquals: Tainted * Tainted -> bool -[] -type ProvidedEventInfo = +[] +#if NO_CHECKNULLS +[] +#endif +type ProvidedEventInfo = inherit ProvidedMemberInfo @@ -366,8 +393,11 @@ type ProvidedEventInfo = static member TaintedEquals: Tainted * Tainted -> bool -[] -type ProvidedConstructorInfo = +[] +#if NO_CHECKNULLS +[] +#endif +type ProvidedConstructorInfo = inherit ProvidedMethodBase type ProvidedExprType = @@ -415,8 +445,11 @@ type ProvidedExprType = | ProvidedIfThenElseExpr of ProvidedExpr * ProvidedExpr * ProvidedExpr | ProvidedVarExpr of ProvidedVar - -[] + +[] +#if NO_CHECKNULLS +[] +#endif type ProvidedExpr = member Type: ProvidedType @@ -426,7 +459,10 @@ type ProvidedExpr = member GetExprType: unit -> ProvidedExprType option -[] +[] +#if NO_CHECKNULLS +[] +#endif type ProvidedVar = member Type: ProvidedType diff --git a/src/Compiler/TypedTree/TypedTree.fs b/src/Compiler/TypedTree/TypedTree.fs index b18cdd1d2a9..8213e884d4b 100644 --- a/src/Compiler/TypedTree/TypedTree.fs +++ b/src/Compiler/TypedTree/TypedTree.fs @@ -2305,14 +2305,18 @@ type Typar = | None -> () /// Links a previously unlinked type variable to the given data. Only used during unpickling of F# metadata. - member x.AsType = - let ty = x.typar_astype - match box ty with - | null -> - let ty2 = TType_var (x, 0uy) - x.typar_astype <- ty2 - ty2 - | _ -> ty + member x.AsType nullness = + match nullness with + | Nullness.Known NullnessInfo.AmbivalentToNull -> + let ty = x.typar_astype + match box ty with + | null -> + let ty2 = TType_var (x, Nullness.Known NullnessInfo.AmbivalentToNull) + x.typar_astype <- ty2 + ty2 + | _ -> ty + | _ -> + TType_var (x, nullness) /// Indicates if a type variable has been linked. Only used during unpickling of F# metadata. member x.IsLinked = x.typar_stamp <> -1L @@ -2373,6 +2377,9 @@ type TyparConstraint = /// A constraint that a type has a 'null' value | SupportsNull of range: range + /// A constraint that a type doesn't support nullness + | NotSupportsNull of range + /// A constraint that a type has a member with the given signature | MayResolveMember of constraintInfo: TraitConstraintInfo * range: range @@ -4174,6 +4181,63 @@ type RecdFieldRef = override x.ToString() = x.FieldName +[] +type Nullness = + | Known of NullnessInfo + | Variable of NullnessVar + + member n.Evaluate() = + match n with + | Known info -> info + | Variable v -> v.Evaluate() + + member n.TryEvaluate() = + match n with + | Known info -> ValueSome info + | Variable v -> v.TryEvaluate() + + override n.ToString() = match n.Evaluate() with NullnessInfo.WithNull -> "?" | NullnessInfo.WithoutNull -> "" | NullnessInfo.AmbivalentToNull -> "%" + +// Note, nullness variables are only created if the nullness checking feature is on +type NullnessVar() = + let mutable solution: Nullness option = None + + member nv.Evaluate() = + match solution with + | None -> NullnessInfo.WithoutNull + | Some soln -> soln.Evaluate() + + member nv.TryEvaluate() = + match solution with + | None -> ValueNone + | Some soln -> soln.TryEvaluate() + + member nv.IsSolved = solution.IsSome + + member nv.Set(nullness) = + assert (not nv.IsSolved) + solution <- Some nullness + + member nv.Unset() = + assert nv.IsSolved + solution <- None + + member nv.Solution = + assert nv.IsSolved + solution.Value + +[] +type NullnessInfo = + + /// we know that there is an extra null value in the type + | WithNull + + /// we know that there is no extra null value in the type + | WithoutNull + + /// we know we don't care + | AmbivalentToNull + /// Represents a type in the typed abstract syntax [] type TType = @@ -4181,10 +4245,10 @@ type TType = /// Indicates the type is a universal type, only used for types of values and members | TType_forall of typars: Typars * bodyTy: TType - /// Indicates the type is built from a named type and a number of type arguments. + /// TType_app(tyconRef, typeInstantiation, nullness). /// - /// 'flags' is a placeholder for future features, in particular nullness analysis - | TType_app of tyconRef: TyconRef * typeInstantiation: TypeInst * flags: byte + /// Indicates the type is built from a named type and a number of type arguments + | TType_app of tyconRef: TyconRef * typeInstantiation: TypeInst * nullness: Nullness /// Indicates the type is an anonymous record type whose compiled representation is located in the given assembly | TType_anon of anonInfo: AnonRecdTypeInfo * tys: TType list @@ -4192,10 +4256,10 @@ type TType = /// Indicates the type is a tuple type. elementTypes must be of length 2 or greater. | TType_tuple of tupInfo: TupInfo * elementTypes: TTypes - /// Indicates the type is a function type. + /// TType_fun(domainType, rangeType, nullness). /// - /// 'flags' is a placeholder for future features, in particular nullness analysis. - | TType_fun of domainType: TType * rangeType: TType * flags: byte + /// Indicates the type is a function type + | TType_fun of domainType: TType * rangeType: TType * nullness: Nullness /// Indicates the type is a non-F#-visible type representing a "proof" that a union value belongs to a particular union case /// These types are not user-visible and will never appear as an inferred type. They are the types given to @@ -4203,9 +4267,7 @@ type TType = | TType_ucase of unionCaseRef: UnionCaseRef * typeInstantiation: TypeInst /// Indicates the type is a variable type, whether declared, generalized or an inference type parameter - /// - /// 'flags' is a placeholder for future features, in particular nullness analysis - | TType_var of typar: Typar * flags: byte + | TType_var of typar: Typar * nullness: Nullness /// Indicates the type is a unit-of-measure expression being used as an argument to a type or member | TType_measure of measure: Measure @@ -4231,18 +4293,18 @@ type TType = override x.ToString() = match x with | TType_forall (_tps, ty) -> "forall ... " + ty.ToString() - | TType_app (tcref, tinst, _) -> tcref.DisplayName + (match tinst with [] -> "" | tys -> "<" + String.concat "," (List.map string tys) + ">") + | TType_app (tcref, tinst, nullness) -> tcref.DisplayName + (match tinst with [] -> "" | tys -> "<" + String.concat "," (List.map string tys) + ">") + nullness.ToString() | TType_tuple (tupInfo, tinst) -> (match tupInfo with | TupInfo.Const false -> "" | TupInfo.Const true -> "struct ") - + String.concat "," (List.map string tinst) + + String.concat "," (List.map string tinst) + ")" | TType_anon (anonInfo, tinst) -> (match anonInfo.TupInfo with | TupInfo.Const false -> "" | TupInfo.Const true -> "struct ") + "{|" + String.concat "," (Seq.map2 (fun nm ty -> nm + " " + string ty + ";") anonInfo.SortedNames tinst) + "|}" - | TType_fun (domainTy, retTy, _) -> "(" + string domainTy + " -> " + string retTy + ")" + | TType_fun (domainTy, retTy, nullness) -> "(" + string domainTy + " -> " + string retTy + ")" + nullness.ToString() | TType_ucase (uc, tinst) -> "ucase " + uc.CaseName + (match tinst with [] -> "" | tys -> "<" + String.concat "," (List.map string tys) + ">") | TType_var (tp, _) -> match tp.Solution with @@ -4304,7 +4366,7 @@ type AnonRecdTypeInfo = x.Stamp <- d.Stamp x.SortedNames <- sortedNames - member x.IsLinked = (match x.SortedIds with null -> true | _ -> false) + member x.IsLinked = (match box x.SortedIds with null -> true | _ -> false) member x.DisplayNameCoreByIdx idx = x.SortedNames[idx] @@ -5512,12 +5574,12 @@ type CcuThunk = /// Dereference the assembly reference member ccu.Deref = - if isNull (ccu.target :> obj) then + if isNull (box ccu.target) then raise(UnresolvedReferenceNoRange ccu.name) ccu.target /// Indicates if this assembly reference is unresolved - member ccu.IsUnresolvedReference = isNull (ccu.target :> obj) + member ccu.IsUnresolvedReference = isNull (box ccu.target) /// Ensure the ccu is derefable in advance. Supply a path to attach to any resulting error message. member ccu.EnsureDerefable(requiringPath: string[]) = @@ -5772,7 +5834,7 @@ type Construct() = let lazyBaseTy = LazyWithContext.Create ((fun (m, objTy) -> - let baseSystemTy = st.PApplyOption((fun st -> match st.BaseType with null -> None | ty -> Some ty), m) + let baseSystemTy = st.PApplyOption((fun st -> match st.BaseType with null -> None | ty -> Some (nonNull ty)), m) match baseSystemTy with | None -> objTy | Some t -> importProvidedType t), @@ -5786,7 +5848,9 @@ type Construct() = IsDelegate = (fun () -> st.PUntaint((fun st -> let baseType = st.BaseType match baseType with - | null -> false + | Null -> false + | NonNull x -> + match x with | x when x.IsGenericType -> false | x when x.DeclaringType <> null -> false | x -> x.FullName = "System.Delegate" || x.FullName = "System.MulticastDelegate"), m)) @@ -6080,8 +6144,8 @@ type Construct() = static member ComputeDefinitionLocationOfProvidedItem<'T when 'T :> IProvidedCustomAttributeProvider> (p: Tainted<'T>) : range option = let attrs = p.PUntaintNoFailure(fun x -> x.GetDefinitionLocationAttribute(p.TypeProvider.PUntaintNoFailure id)) match attrs with - | None | Some (null, _, _) -> None - | Some (filePath, line, column) -> + | None | Some (Null, _, _) -> None + | Some (NonNull filePath, line, column) -> // Coordinates from type provider are 1-based for lines and columns // Coordinates internally in the F# compiler are 1-based for lines and 0-based for columns let pos = Position.mkPos line (max 0 (column - 1)) diff --git a/src/Compiler/TypedTree/TypedTree.fsi b/src/Compiler/TypedTree/TypedTree.fsi index bcc951ecc4d..e6724a16fb4 100644 --- a/src/Compiler/TypedTree/TypedTree.fsi +++ b/src/Compiler/TypedTree/TypedTree.fsi @@ -1530,7 +1530,7 @@ type Typar = override ToString: unit -> string /// Links a previously unlinked type variable to the given data. Only used during unpickling of F# metadata. - member AsType: TType + member AsType: nullness: Nullness -> TType /// The declared attributes of the type parameter. Empty for type inference variables type parameters from .NET. member Attribs: Attribs @@ -1615,6 +1615,9 @@ type TyparConstraint = /// A constraint that a type has a 'null' value | SupportsNull of range: range + /// A constraint that a type doesn't support nullness + | NotSupportsNull of range + /// A constraint that a type has a member with the given signature | MayResolveMember of constraintInfo: TraitConstraintInfo * range: range @@ -3008,6 +3011,36 @@ type RecdFieldRef = /// Get a reference to the type containing this union case member TyconRef: TyconRef +[] +type NullnessInfo = + + /// we know that there is an extra null value in the type + | WithNull + + /// we know that there is no extra null value in the type + | WithoutNull + + /// we know we don't care + | AmbivalentToNull + +[] +type Nullness = + | Known of NullnessInfo + | Variable of NullnessVar + + member Evaluate: unit -> NullnessInfo + + member TryEvaluate: unit -> NullnessInfo voption + +type NullnessVar = + new: unit -> NullnessVar + member Evaluate: unit -> NullnessInfo + member TryEvaluate: unit -> NullnessInfo voption + member IsSolved: bool + member Set: Nullness -> unit + member Unset: unit -> unit + member Solution: Nullness + /// Represents a type in the typed abstract syntax [] type TType = @@ -3018,7 +3051,7 @@ type TType = /// Indicates the type is built from a named type type a number of type arguments. /// /// 'flags' is a placeholder for future features, in particular nullness analysis - | TType_app of tyconRef: TyconRef * typeInstantiation: TypeInst * flags: byte + | TType_app of tyconRef: TyconRef * typeInstantiation: TypeInst * nullness: Nullness /// Indicates the type is an anonymous record type whose compiled representation is located in the given assembly | TType_anon of anonInfo: AnonRecdTypeInfo * tys: TType list @@ -3029,7 +3062,7 @@ type TType = /// Indicates the type is a function type. /// /// 'flags' is a placeholder for future features, in particular nullness analysis. - | TType_fun of domainType: TType * rangeType: TType * flags: byte + | TType_fun of domainType: TType * rangeType: TType * nullness: Nullness /// Indicates the type is a non-F#-visible type representing a "proof" that a union value belongs to a particular union case /// These types are not user-visible type will never appear as an inferred type. They are the types given to @@ -3039,7 +3072,7 @@ type TType = /// Indicates the type is a variable type, whether declared, generalized or an inference type parameter /// /// 'flags' is a placeholder for future features, in particular nullness analysis - | TType_var of typar: Typar * flags: byte + | TType_var of typar: Typar * nullness: Nullness /// Indicates the type is a unit-of-measure expression being used as an argument to a type or member | TType_measure of measure: Measure diff --git a/src/Compiler/TypedTree/TypedTreeBasics.fs b/src/Compiler/TypedTree/TypedTreeBasics.fs index 651eadd0d65..04052291cb1 100644 --- a/src/Compiler/TypedTree/TypedTreeBasics.fs +++ b/src/Compiler/TypedTree/TypedTreeBasics.fs @@ -189,9 +189,17 @@ let ccuOfTyconRef eref = // Type parameters and inference unknowns //------------------------------------------------------------------------- -let mkTyparTy (tp: Typar) = +let NewNullnessVar() = Nullness.Variable (NullnessVar()) // we don't known (and if we never find out then it's non-null) + +let KnownAmbivalentToNull = Nullness.Known NullnessInfo.AmbivalentToNull + +let KnownWithNull = Nullness.Known NullnessInfo.WithNull + +let KnownWithoutNull = Nullness.Known NullnessInfo.WithoutNull + +let mkTyparTy (tp:Typar) = match tp.Kind with - | TyparKind.Type -> tp.AsType + | TyparKind.Type -> tp.AsType KnownWithoutNull // TODO NULLNESS: check various callers | TyparKind.Measure -> TType_measure (Measure.Var tp) // For fresh type variables clear the StaticReq when copying because the requirement will be re-established through the @@ -234,9 +242,49 @@ let rec stripUnitEqnsAux canShortcut unt = | Measure.Var r when r.IsSolved -> stripUnitEqnsAux canShortcut (tryShortcutSolvedUnitPar canShortcut r) | _ -> unt -let rec stripTyparEqnsAux canShortcut ty = +let combineNullness (nullnessOrig: Nullness) (nullnessNew: Nullness) = + match nullnessOrig.Evaluate() with + | NullnessInfo.WithoutNull -> nullnessNew + | NullnessInfo.AmbivalentToNull -> nullnessOrig + | NullnessInfo.WithNull -> + match nullnessNew.Evaluate() with + | NullnessInfo.WithoutNull -> nullnessOrig + | NullnessInfo.AmbivalentToNull -> nullnessNew + | NullnessInfo.WithNull -> nullnessOrig + +let tryAddNullnessToTy nullnessNew (ty:TType) = + match ty with + | TType_var (tp, nullnessOrig) -> + // TODO NULLNESS: make this avoid allocation if no change + Some (TType_var (tp, combineNullness nullnessOrig nullnessNew)) + | TType_app (tcr, tinst, nullnessOrig) -> + // TODO NULLNESS: make this avoid allocation if no change + Some (TType_app (tcr, tinst, combineNullness nullnessOrig nullnessNew)) + | TType_ucase _ -> None // TODO NULLNESS + | TType_tuple _ -> None // TODO NULLNESS + | TType_anon _ -> None // TODO NULLNESS + | TType_fun (d, r, nullnessOrig) -> + // TODO NULLNESS: make this avoid allocation if no change + Some (TType_fun (d, r, combineNullness nullnessOrig nullnessNew)) + | TType_forall _ -> None + | TType_measure _ -> None + +let addNullnessToTy (nullness: Nullness) (ty:TType) = + match nullness.Evaluate() with + | NullnessInfo.WithoutNull -> ty + | _ -> + match ty with + | TType_var (tp, nullnessOrig) -> TType_var (tp, combineNullness nullnessOrig nullness) + | TType_app (tcr, tinst, nullnessOrig) -> TType_app (tcr, tinst, combineNullness nullnessOrig nullness) + | TType_fun (d, r, nullnessOrig) -> TType_fun (d, r, combineNullness nullnessOrig nullness) + //| TType_ucase _ -> None // TODO NULLNESS + //| TType_tuple _ -> None // TODO NULLNESS + //| TType_anon _ -> None // TODO NULLNESS + | _ -> ty + +let rec stripTyparEqnsAux nullness0 canShortcut ty = match ty with - | TType_var (r, _) -> + | TType_var (r, nullness) -> match r.Solution with | Some soln -> if canShortcut then @@ -245,23 +293,36 @@ let rec stripTyparEqnsAux canShortcut ty = // This is only because IterType likes to walk _all_ the constraints _everywhere_ in a type, including // those attached to _solved_ type variables. In an ideal world this would never be needed - see the notes // on IterType. - | TType_var (r2, _) when r2.Constraints.IsEmpty -> - match r2.Solution with - | None -> () - | Some _ as soln2 -> - r.typar_solution <- soln2 + | TType_var (r2, nullness2) when r2.Constraints.IsEmpty -> + match nullness2.Evaluate() with + | NullnessInfo.WithoutNull -> + match r2.Solution with + | None -> () + | Some _ as soln2 -> + r.typar_solution <- soln2 + | _ -> () | _ -> () - stripTyparEqnsAux canShortcut soln + stripTyparEqnsAux (combineNullness nullness0 nullness) canShortcut soln | None -> - ty + addNullnessToTy nullness0 ty | TType_measure unt -> TType_measure (stripUnitEqnsAux canShortcut unt) | _ -> ty -let stripTyparEqns ty = stripTyparEqnsAux false ty +let stripTyparEqns ty = stripTyparEqnsAux KnownWithoutNull false ty let stripUnitEqns unt = stripUnitEqnsAux false unt +let replaceNullnessOfTy nullness (ty:TType) = + match stripTyparEqns ty with + | TType_var (tp, _) -> TType_var (tp, nullness) + | TType_app (tcr, tinst, _) -> TType_app (tcr, tinst, nullness) + | TType_fun (d, r, _) -> TType_fun (d, r, nullness) + //| TType_ucase _ -> None // TODO NULLNESS + //| TType_tuple _ -> None // TODO NULLNESS + //| TType_anon _ -> None // TODO NULLNESS + | sty -> sty + /// Detect a use of a nominal type, including type abbreviations. let (|AbbrevOrAppTy|_|) (ty: TType) = match stripTyparEqns ty with diff --git a/src/Compiler/TypedTree/TypedTreeBasics.fsi b/src/Compiler/TypedTree/TypedTreeBasics.fsi index 8a73a609316..7770c392f78 100644 --- a/src/Compiler/TypedTree/TypedTreeBasics.fsi +++ b/src/Compiler/TypedTree/TypedTreeBasics.fsi @@ -120,6 +120,20 @@ val ccuOfValRef: vref: ValRef -> CcuThunk option val ccuOfTyconRef: eref: EntityRef -> CcuThunk option +val NewNullnessVar: unit -> Nullness + +val KnownAmbivalentToNull: Nullness + +val KnownWithNull: Nullness + +val KnownWithoutNull: Nullness + +val combineNullness: Nullness -> Nullness -> Nullness + +val tryAddNullnessToTy: Nullness -> TType -> TType option + +val addNullnessToTy: Nullness -> TType -> TType + val mkTyparTy: tp: Typar -> TType val copyTypars: clearStaticReq: bool -> tps: Typar list -> Typar list @@ -128,7 +142,9 @@ val tryShortcutSolvedUnitPar: canShortcut: bool -> r: Typar -> Measure val stripUnitEqnsAux: canShortcut: bool -> unt: Measure -> Measure -val stripTyparEqnsAux: canShortcut: bool -> ty: TType -> TType +val stripTyparEqnsAux: nullness0: Nullness -> canShortcut: bool -> ty: TType -> TType + +val replaceNullnessOfTy: nullness: Nullness -> ty: TType -> TType val stripTyparEqns: ty: TType -> TType diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 702f7292429..755e2e725ab 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -160,8 +160,9 @@ let generalizeTypars tps = List.map generalizeTypar tps let rec remapTypeAux (tyenv: Remap) (ty: TType) = let ty = stripTyparEqns ty match ty with - | TType_var (tp, _) as ty -> - instTyparRef tyenv.tpinst ty tp + | TType_var (tp, nullness) as ty -> + let res = instTyparRef tyenv.tpinst ty tp + addNullnessToTy nullness res | TType_app (tcref, tinst, flags) as ty -> match tyenv.tyconRefRemap.TryFind tcref with @@ -251,6 +252,7 @@ and remapTyparConstraintsAux tyenv cs = | TyparConstraint.SupportsComparison _ | TyparConstraint.SupportsEquality _ | TyparConstraint.SupportsNull _ + | TyparConstraint.NotSupportsNull _ | TyparConstraint.IsUnmanaged _ | TyparConstraint.IsNonNullableStruct _ | TyparConstraint.IsReferenceType _ @@ -616,12 +618,12 @@ let mkByrefTyWithInference (g: TcGlobals) ty1 ty2 = else TType_app (g.byref_tcr, [ty1], g.knownWithoutNull) -let mkArrayTy (g: TcGlobals) rank ty m = +let mkArrayTy (g: TcGlobals) rank nullness ty m = if rank < 1 || rank > 32 then errorR(Error(FSComp.SR.tastopsMaxArrayThirtyTwo rank, m)) - TType_app (g.il_arr_tcr_map[3], [ty], g.knownWithoutNull) + TType_app (g.il_arr_tcr_map[3], [ty], nullness) else - TType_app (g.il_arr_tcr_map[rank - 1], [ty], g.knownWithoutNull) + TType_app (g.il_arr_tcr_map[rank - 1], [ty], nullness) //-------------------------------------------------------------------------- // Tuple compilation (types) @@ -720,13 +722,15 @@ let reduceTyconRefMeasureableOrProvided (g: TcGlobals) (tcref: TyconRef) tyargs reduceTyconMeasureableOrProvided g tcref.Deref tyargs let rec stripTyEqnsA g canShortcut ty = - let ty = stripTyparEqnsAux canShortcut ty + let ty = stripTyparEqnsAux KnownWithoutNull canShortcut ty match ty with - | TType_app (tcref, tinst, _) -> + | TType_app (tcref, tinst, nullness) -> let tycon = tcref.Deref match tycon.TypeAbbrev with | Some abbrevTy -> - stripTyEqnsA g canShortcut (applyTyconAbbrev abbrevTy tycon tinst) + let reducedTy = applyTyconAbbrev abbrevTy tycon tinst + let reducedTy2 = addNullnessToTy nullness reducedTy + stripTyEqnsA g canShortcut reducedTy2 | None -> // This is the point where we get to add additional conditional normalizing equations // into the type system. Such power! @@ -738,7 +742,9 @@ let rec stripTyEqnsA g canShortcut ty = // Add the equation double<1> = double for units of measure. elif tycon.IsMeasureableReprTycon && List.forall (isDimensionless g) tinst then - stripTyEqnsA g canShortcut (reduceTyconMeasureableOrProvided g tycon tinst) + let reducedTy = reduceTyconMeasureableOrProvided g tycon tinst + let reducedTy2 = addNullnessToTy nullness reducedTy + stripTyEqnsA g canShortcut reducedTy2 else ty | ty -> ty @@ -759,17 +765,19 @@ let evalAnonInfoIsStruct (anonInfo: AnonRecdTypeInfo) = let rec stripTyEqnsAndErase eraseFuncAndTuple (g: TcGlobals) ty = let ty = stripTyEqns g ty match ty with - | TType_app (tcref, args, _) -> + | TType_app (tcref, args, nullness) -> let tycon = tcref.Deref - if tycon.IsErased then - stripTyEqnsAndErase eraseFuncAndTuple g (reduceTyconMeasureableOrProvided g tycon args) + if tycon.IsErased then + let reducedTy = reduceTyconMeasureableOrProvided g tycon args + let reducedTy2 = addNullnessToTy nullness reducedTy + stripTyEqnsAndErase eraseFuncAndTuple g reducedTy2 elif tyconRefEq g tcref g.nativeptr_tcr && eraseFuncAndTuple then stripTyEqnsAndErase eraseFuncAndTuple g g.nativeint_ty else ty - | TType_fun(domainTy, rangeTy, flags) when eraseFuncAndTuple -> - TType_app(g.fastFunc_tcr, [ domainTy; rangeTy ], flags) + | TType_fun(domainTy, rangeTy, nullness) when eraseFuncAndTuple -> + TType_app(g.fastFunc_tcr, [ domainTy; rangeTy ], nullness) | TType_tuple(tupInfo, l) when eraseFuncAndTuple -> mkCompiledTupleTy g (evalTupInfoIsStruct tupInfo) l @@ -843,7 +851,7 @@ let isMeasureTy g ty = ty |> stripTyEqns g |> (function TType_measure _ -> true let isProvenUnionCaseTy ty = match ty with TType_ucase _ -> true | _ -> false -let mkAppTy tcref tyargs = TType_app(tcref, tyargs, 0uy) +let mkAppTy tcref tyargs = TType_app(tcref, tyargs, KnownWithoutNull) // TODO NULLNESS - check various callers let mkProvenUnionCaseTy ucref tyargs = TType_ucase(ucref, tyargs) @@ -855,6 +863,8 @@ let destAppTy g ty = ty |> stripTyEqns g |> (function TType_app(tcref, tinst, _) let tcrefOfAppTy g ty = ty |> stripTyEqns g |> (function TType_app(tcref, _, _) -> tcref | _ -> failwith "tcrefOfAppTy") +let nullnessOfTy g ty = ty |> stripTyEqns g |> (function TType_app(_, _, nullness) | TType_fun (_, _, nullness) | TType_var (_, nullness) -> nullness | _ -> g.knownWithoutNull) + let argsOfAppTy g ty = ty |> stripTyEqns g |> (function TType_app(_, tinst, _) -> tinst | _ -> []) let tryDestTyparTy g ty = ty |> stripTyEqns g |> (function TType_var (v, _) -> ValueSome v | _ -> ValueNone) @@ -876,14 +886,14 @@ let (|RefTupleTy|_|) g ty = ty |> stripTyEqns g |> (function TType_tuple(tupInfo let (|FunTy|_|) g ty = ty |> stripTyEqns g |> (function TType_fun(domainTy, rangeTy, _) -> Some (domainTy, rangeTy) | _ -> None) let tryNiceEntityRefOfTy ty = - let ty = stripTyparEqnsAux false ty + let ty = stripTyparEqnsAux KnownWithoutNull false ty match ty with | TType_app (tcref, _, _) -> ValueSome tcref | TType_measure (Measure.Const tcref) -> ValueSome tcref | _ -> ValueNone let tryNiceEntityRefOfTyOption ty = - let ty = stripTyparEqnsAux false ty + let ty = stripTyparEqnsAux KnownWithoutNull false ty match ty with | TType_app (tcref, _, _) -> Some tcref | TType_measure (Measure.Const tcref) -> Some tcref @@ -912,9 +922,9 @@ let convertToTypeWithMetadataIfPossible g ty = let stripMeasuresFromTy g ty = match ty with - | TType_app(tcref, tinst, flags) -> + | TType_app(tcref, tinst, nullness) -> let tinstR = tinst |> List.filter (isMeasureTy g >> not) - TType_app(tcref, tinstR, flags) + TType_app(tcref, tinstR, nullness) | _ -> ty //--------------------------------------------------------------------------- @@ -999,6 +1009,7 @@ and typarConstraintsAEquivAux erasureFlag g aenv tpc1 tpc2 = | TyparConstraint.SupportsComparison _, TyparConstraint.SupportsComparison _ | TyparConstraint.SupportsEquality _, TyparConstraint.SupportsEquality _ | TyparConstraint.SupportsNull _, TyparConstraint.SupportsNull _ + | TyparConstraint.NotSupportsNull _, TyparConstraint.NotSupportsNull _ | TyparConstraint.IsNonNullableStruct _, TyparConstraint.IsNonNullableStruct _ | TyparConstraint.IsReferenceType _, TyparConstraint.IsReferenceType _ | TyparConstraint.IsUnmanaged _, TyparConstraint.IsUnmanaged _ @@ -1025,7 +1036,7 @@ and typeAEquivAux erasureFlag g aenv ty1 ty2 = | TType_forall(tps1, rty1), TType_forall(tps2, retTy2) -> typarsAEquivAux erasureFlag g aenv tps1 tps2 && typeAEquivAux erasureFlag g (aenv.BindEquivTypars tps1 tps2) rty1 retTy2 - | TType_var (tp1, _), TType_var (tp2, _) when typarEq tp1 tp2 -> + | TType_var (tp1, _), TType_var (tp2, _) when typarEq tp1 tp2 -> // NOTE: nullness annotations are ignored for type equivalence true | TType_var (tp1, _), _ -> @@ -1033,7 +1044,8 @@ and typeAEquivAux erasureFlag g aenv ty1 ty2 = | Some tpTy1 -> typeEquivAux erasureFlag g tpTy1 ty2 | None -> false - | TType_app (tcref1, tinst1, _), TType_app (tcref2, tinst2, _) -> + // NOTE: nullness annotations are ignored for type equivalence + | TType_app (tcref1, tinst1, _), TType_app (tcref2, tinst2, _) -> tcrefAEquiv g aenv tcref1 tcref2 && typesAEquivAux erasureFlag g aenv tinst1 tinst2 @@ -1045,13 +1057,14 @@ and typeAEquivAux erasureFlag g aenv ty1 ty2 = | TType_tuple (tupInfo1, l1), TType_tuple (tupInfo2, l2) -> structnessAEquiv tupInfo1 tupInfo2 && typesAEquivAux erasureFlag g aenv l1 l2 + // NOTE: nullness annotations are ignored for type equivalence + | TType_fun (domainTy1, rangeTy1, _), TType_fun (domainTy2, rangeTy2, _) -> + typeAEquivAux erasureFlag g aenv domainTy1 domainTy2 && typeAEquivAux erasureFlag g aenv rangeTy1 rangeTy2 + | TType_anon (anonInfo1, l1), TType_anon (anonInfo2, l2) -> anonInfoEquiv anonInfo1 anonInfo2 && typesAEquivAux erasureFlag g aenv l1 l2 - | TType_fun (domainTy1, rangeTy1, _), TType_fun (domainTy2, rangeTy2, _) -> - typeAEquivAux erasureFlag g aenv domainTy1 domainTy2 && typeAEquivAux erasureFlag g aenv rangeTy1 rangeTy2 - | TType_measure m1, TType_measure m2 -> match erasureFlag with | EraseNone -> measureAEquiv g aenv m1 m2 @@ -1124,12 +1137,24 @@ let rec getErasedTypes g ty = match ty with | TType_forall(_, bodyTy) -> getErasedTypes g bodyTy - | TType_var (tp, _) -> - if tp.IsErased then [ty] else [] - | TType_app (_, b, _) | TType_ucase(_, b) | TType_anon (_, b) | TType_tuple (_, b) -> + + | TType_var (tp, nullness) -> + match nullness.Evaluate() with + | NullnessInfo.WithNull -> [ty] // with-null annotations can't be tested at runtime (TODO NULLNESS: for value types Nullable<_> they can be) + | _ -> if tp.IsErased then [ty] else [] + + | TType_app (_, b, nullness) -> + match nullness.Evaluate() with + | NullnessInfo.WithNull -> [ty] + | _ -> List.foldBack (fun ty tys -> getErasedTypes g ty @ tys) b [] + + | TType_ucase(_, b) | TType_anon (_, b) | TType_tuple (_, b) -> List.foldBack (fun ty tys -> getErasedTypes g ty @ tys) b [] - | TType_fun (domainTy, rangeTy, _) -> - getErasedTypes g domainTy @ getErasedTypes g rangeTy + + | TType_fun (domainTy, rangeTy, nullness) -> + match nullness.Evaluate() with + | NullnessInfo.WithNull -> [ty] + | _ -> getErasedTypes g domainTy @ getErasedTypes g rangeTy | TType_measure _ -> [ty] @@ -1808,6 +1833,7 @@ let isInByrefTy g ty = | _ -> false) let isOutByrefTag g ty = ty |> stripTyEqns g |> (function TType_app(tcref, [], _) -> tyconRefEq g g.byrefkind_Out_tcr tcref | _ -> false) + let isOutByrefTy g ty = ty |> stripTyEqns g |> (function | TType_app(tcref, [_; tagTy], _) when g.byref2_tcr.CanDeref -> tyconRefEq g g.byref2_tcr tcref && isOutByrefTag g tagTy @@ -2233,6 +2259,7 @@ and accFreeInTyparConstraint opts tpc acc = | TyparConstraint.SupportsComparison _ | TyparConstraint.SupportsEquality _ | TyparConstraint.SupportsNull _ + | TyparConstraint.NotSupportsNull _ | TyparConstraint.IsNonNullableStruct _ | TyparConstraint.IsReferenceType _ | TyparConstraint.IsUnmanaged _ @@ -2368,6 +2395,7 @@ and accFreeInTyparConstraintLeftToRight g cxFlag thruFlag acc tpc = | TyparConstraint.SupportsComparison _ | TyparConstraint.SupportsEquality _ | TyparConstraint.SupportsNull _ + | TyparConstraint.NotSupportsNull _ | TyparConstraint.IsNonNullableStruct _ | TyparConstraint.IsUnmanaged _ | TyparConstraint.IsReferenceType _ @@ -2416,7 +2444,7 @@ and accFreeInTypeLeftToRight g cxFlag thruFlag acc ty = let racc = accFreeInTypeLeftToRight g cxFlag thruFlag emptyFreeTyparsLeftToRight r unionFreeTyparsLeftToRight (boundTyparsLeftToRight g cxFlag thruFlag tps racc) acc - | TType_measure unt -> + | TType_measure unt -> let mvars = ListMeasureVarOccsWithNonZeroExponents unt List.foldBack (fun (tp, _) acc -> accFreeTyparRefLeftToRight g cxFlag thruFlag acc tp) mvars acc @@ -2754,7 +2782,10 @@ let generalizedTyconRef (g: TcGlobals) tcref = let tinst = generalTyconRefInst tcref TType_app(tcref, tinst, g.knownWithoutNull) -let isTTyparCoercesToType = function TyparConstraint.CoercesTo _ -> true | _ -> false +let isTTyparCoercesToType tpc = + match tpc with + | TyparConstraint.CoercesTo _ -> true + | _ -> false //-------------------------------------------------------------------------- // Print Signatures/Types - prelude @@ -3878,32 +3909,44 @@ module DebugPrint = else tupleL tinstL ^^ tcL + and auxAddNullness coreL (nullness: Nullness) = + match nullness.Evaluate() with + | NullnessInfo.WithNull -> coreL ^^ wordL (tagText "?") + | NullnessInfo.WithoutNull -> coreL + | NullnessInfo.AmbivalentToNull -> coreL //^^ wordL (tagText "%") + and auxTypeWrapL env isAtomic ty = let wrap x = bracketIfL isAtomic x in // wrap iff require atomic expr match stripTyparEqns ty with | TType_forall (typars, bodyTy) -> (leftL (tagText "!") ^^ layoutTyparDecls typars --- auxTypeL env bodyTy) |> wrap - | TType_ucase (UnionCaseRef(tcref, _), tinst) - - | TType_app (tcref, tinst, _) -> + | TType_ucase (UnionCaseRef(tcref, _), tinst) -> let prefix = tcref.IsPrefixDisplay let tcL = layoutTyconRef tcref auxTyparsL env tcL prefix tinst + | TType_app (tcref, tinst, nullness) -> + let prefix = tcref.IsPrefixDisplay + let tcL = layoutTyconRef tcref + let coreL = auxTyparsL env tcL prefix tinst + auxAddNullness coreL nullness + | TType_tuple (_tupInfo, tys) -> sepListL (wordL (tagText "*")) (List.map (auxTypeAtomL env) tys) |> wrap - | TType_fun (domainTy, rangeTy, _) -> - ((auxTypeAtomL env domainTy ^^ wordL (tagText "->")) --- auxTypeL env rangeTy) |> wrap + | TType_fun (domainTy, rangeTy, nullness) -> + let coreL = ((auxTypeAtomL env domainTy ^^ wordL (tagText "->")) --- auxTypeL env rangeTy) |> wrap + auxAddNullness coreL nullness - | TType_var (typar, _) -> - auxTyparWrapL env isAtomic typar + | TType_var (typar, nullness) -> + let coreL = auxTyparWrapL env isAtomic typar + auxAddNullness coreL nullness | TType_anon (anonInfo, tys) -> - braceBarL (sepListL (wordL (tagText ";")) (List.map2 (fun nm ty -> wordL (tagField nm) --- auxTypeAtomL env ty) (Array.toList anonInfo.SortedNames) tys)) + braceBarL (sepListL (wordL (tagText ";")) (List.map2 (fun nm ty -> wordL (tagField nm) --- auxTypeAtomL env ty) (Array.toList anonInfo.SortedNames) tys)) - | TType_measure unt -> + | TType_measure unt -> #if DEBUG leftL (tagText "{") ^^ (match global_g with @@ -4003,6 +4046,8 @@ module DebugPrint = wordL (tagText "struct") |> constraintPrefix | TyparConstraint.IsReferenceType _ -> wordL (tagText "not struct") |> constraintPrefix + | TyparConstraint.NotSupportsNull _ -> + wordL (tagText "not null") |> constraintPrefix | TyparConstraint.IsUnmanaged _ -> wordL (tagText "unmanaged") |> constraintPrefix | TyparConstraint.SimpleChoice(tys, _) -> @@ -8850,15 +8895,34 @@ let TypeNullNever g ty = isByrefTy g underlyingTy || isNonNullableStructTyparTy g ty -/// Indicates if the type admits the use of 'null' as a value +let TyconRefNullIsExtraValueAux isNew g m (tcref: TyconRef) = + not tcref.IsStructOrEnumTycon && + not (isByrefLikeTyconRef g m tcref) && + (if tcref.IsILTycon then + // Putting AllowNullLiteralAttribute(false) on an IL or provided type means 'null' can't be used with that type + (not isNew && TryFindTyconRefBoolAttribute g m g.attrib_AllowNullLiteralAttribute tcref <> Some false) + else +// Putting AllowNullLiteralAttribute(true) on an F# type means it always admits null even in the new model + (TryFindTyconRefBoolAttribute g m g.attrib_AllowNullLiteralAttribute tcref = Some true)) + +let TyconRefNullIsExtraValue g m tcref = TyconRefNullIsExtraValueAux false g m tcref +let TyconRefNullIsExtraValueNew g m tcref = TyconRefNullIsExtraValueAux true g m tcref + +/// The F# 4.5 logic about whether a type admits the use of 'null' as a value. let TypeNullIsExtraValue g m ty = if isILReferenceTy g ty || isDelegateTy g ty then - // Putting AllowNullLiteralAttribute(false) on an IL or provided type means 'null' can't be used with that type - not (match tryTcrefOfAppTy g ty with ValueSome tcref -> TryFindTyconRefBoolAttribute g m g.attrib_AllowNullLiteralAttribute tcref = Some false | _ -> false) + match tryTcrefOfAppTy g ty with + | ValueSome tcref -> + // In F# 4.x, putting AllowNullLiteralAttribute(false) on an IL or provided + // type means 'null' can't be used with that type, otherwise it can + TryFindTyconRefBoolAttribute g m g.attrib_AllowNullLiteralAttribute tcref <> Some false + | _ -> + // In F# 4.5, other IL reference types (e.g. arrays) always support null + true elif TypeNullNever g ty then false else - // Putting AllowNullLiteralAttribute(true) on an F# type means 'null' can be used with that type + // In F# 4.x, putting AllowNullLiteralAttribute(true) on an F# type means 'null' can be used with that type match tryTcrefOfAppTy g ty with | ValueSome tcref -> TryFindTyconRefBoolAttribute g m g.attrib_AllowNullLiteralAttribute tcref = Some true | ValueNone -> @@ -8869,12 +8933,33 @@ let TypeNullIsExtraValue g m ty = else false +/// The new logic about whether a type admits the use of 'null' as a value. +let TypeNullIsExtraValueNew g m ty = + let sty = stripTyparEqns ty + (match tryTcrefOfAppTy g sty with + | ValueSome tcref -> + not tcref.IsStructOrEnumTycon && + not (isByrefLikeTyconRef g m tcref) && + (TryFindTyconRefBoolAttribute g m g.attrib_AllowNullLiteralAttribute tcref = Some true) + | _ -> false) + || + (match (nullnessOfTy g sty).Evaluate() with + | NullnessInfo.AmbivalentToNull -> false + | NullnessInfo.WithoutNull -> false + | NullnessInfo.WithNull -> true) + || + (isReferenceTyparTy g ty && + (destTyparTy g ty).Constraints |> List.exists (function TyparConstraint.SupportsNull _ -> true | _ -> false)) + +/// The F# 4.5 and 5.0 logic about whether a type uses 'null' as a true representation value let TypeNullIsTrueValue g ty = (match tryTcrefOfAppTy g ty with | ValueSome tcref -> IsUnionTypeWithNullAsTrueValue g tcref.Deref | _ -> false) || isUnitTy g ty +/// Indicates if unbox(null) is actively rejected at runtime. See nullability RFC. This applies to types that don't have null +/// as a valid runtime representation under old compatiblity rules. let TypeNullNotLiked g m ty = not (TypeNullIsExtraValue g m ty) && not (TypeNullIsTrueValue g ty) @@ -8884,15 +8969,11 @@ let TypeNullNotLiked g m ty = let TypeSatisfiesNullConstraint g m ty = TypeNullIsExtraValue g m ty -// The non-inferring counter-part to SolveTypeRequiresDefaultValue (and SolveTypeRequiresDefaultConstructor for struct types) -let rec TypeHasDefaultValue g m ty = +let rec TypeHasDefaultValueAux isNew g m ty = let ty = stripTyEqnsAndMeasureEqns g ty - // Check reference types - precisely the ones satisfying the ': null' constraint have default values - TypeSatisfiesNullConstraint g m ty - || - // Check nominal struct types - (isStructTy g ty && - // F# struct types have a DefaultValue if all their field types have a default value excluding those with DefaultValue(false) + (if isNew then TypeNullIsExtraValueNew g m ty else TypeNullIsExtraValue g m ty) + || (isStructTy g ty && + // Is it an F# struct type? (if isFSharpStructTy g ty then let tcref, tinst = destAppTy g ty let flds = @@ -8901,17 +8982,17 @@ let rec TypeHasDefaultValue g m ty = // We can ignore fields with the DefaultValue(false) attribute |> List.filter (fun fld -> not (TryFindFSharpBoolAttribute g g.attrib_DefaultValueAttribute fld.FieldAttribs = Some false)) - flds |> List.forall (actualTyOfRecdField (mkTyconRefInst tcref tinst) >> TypeHasDefaultValue g m) + flds |> List.forall (actualTyOfRecdField (mkTyconRefInst tcref tinst) >> TypeHasDefaultValueAux isNew g m) // Struct tuple types have a DefaultValue if all their element types have a default value elif isStructTupleTy g ty then - destStructTupleTy g ty |> List.forall (TypeHasDefaultValue g m) + destStructTupleTy g ty |> List.forall (TypeHasDefaultValueAux isNew g m) // Struct anonymous record types have a DefaultValue if all their element types have a default value elif isStructAnonRecdTy g ty then match tryDestAnonRecdTy g ty with | ValueNone -> true - | ValueSome (_, ptys) -> ptys |> List.forall (TypeHasDefaultValue g m) + | ValueSome (_, ptys) -> ptys |> List.forall (TypeHasDefaultValueAux isNew g m) else // All nominal struct types defined in other .NET languages have a DefaultValue regardless of their instantiation true)) @@ -8920,6 +9001,10 @@ let rec TypeHasDefaultValue g m ty = (isNonNullableStructTyparTy g ty && (destTyparTy g ty).Constraints |> List.exists (function TyparConstraint.RequiresDefaultConstructor _ -> true | _ -> false)) +let TypeHasDefaultValue g m ty = TypeHasDefaultValueAux false g m ty + +let TypeHasDefaultValueNew g m ty = TypeHasDefaultValueAux true g m ty + /// Determines types that are potentially known to satisfy the 'comparable' constraint and returns /// a set of residual types that must also satisfy the constraint let (|SpecialComparableHeadType|_|) g ty = diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index 8582edaa5fc..1dee86e329b 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -635,6 +635,8 @@ val destMeasureTy: TcGlobals -> TType -> Measure val tryDestForallTy: TcGlobals -> TType -> Typars * TType +val nullnessOfTy: TcGlobals -> TType -> Nullness + val isFunTy: TcGlobals -> TType -> bool val isForallTy: TcGlobals -> TType -> bool @@ -1613,7 +1615,7 @@ val destArrayTy: TcGlobals -> TType -> TType val destListTy: TcGlobals -> TType -> TType /// Build an array type of the given rank -val mkArrayTy: TcGlobals -> int -> TType -> range -> TType +val mkArrayTy: TcGlobals -> int -> Nullness -> TType -> range -> TType /// Check if a type definition is one of the artificial type definitions used for array types of different ranks val isArrayTyconRef: TcGlobals -> TyconRef -> bool @@ -1738,15 +1740,19 @@ val ModuleNameIsMangled: TcGlobals -> Attribs -> bool val CompileAsEvent: TcGlobals -> Attribs -> bool +val TypeNullIsTrueValue: TcGlobals -> TType -> bool + +val TypeNullIsExtraValueNew: TcGlobals -> range -> TType -> bool + val TypeNullIsExtraValue: TcGlobals -> range -> TType -> bool -val TypeNullIsTrueValue: TcGlobals -> TType -> bool +val TyconRefNullIsExtraValue: TcGlobals -> range -> TyconRef -> bool -val TypeNullNotLiked: TcGlobals -> range -> TType -> bool +val TyconRefNullIsExtraValueNew: TcGlobals -> range -> TyconRef -> bool val TypeNullNever: TcGlobals -> TType -> bool -val TypeSatisfiesNullConstraint: TcGlobals -> range -> TType -> bool +val TypeHasDefaultValueNew: TcGlobals -> range -> TType -> bool val TypeHasDefaultValue: TcGlobals -> range -> TType -> bool diff --git a/src/Compiler/TypedTree/TypedTreePickle.fs b/src/Compiler/TypedTree/TypedTreePickle.fs index f7f98e5620b..880082e9a1d 100644 --- a/src/Compiler/TypedTree/TypedTreePickle.fs +++ b/src/Compiler/TypedTree/TypedTreePickle.fs @@ -121,6 +121,7 @@ type NodeOutTable<'Data, 'Node> = [] type WriterState = { os: ByteBuffer + osB: ByteBuffer oscope: CcuThunk occus: Table oentities: NodeOutTable @@ -154,6 +155,8 @@ type NodeInTable<'Data, 'Node> = [] type ReaderState = { is: ByteStream + // secondary stream of information for F# 5.0 + isB: ByteStream iilscope: ILScopeRef iccus: InputTable ientities: NodeInTable @@ -178,18 +181,28 @@ type 'T pickler = 'T -> WriterState -> unit let p_byte b st = st.os.EmitIntAsByte b +let p_byteB b st = st.osB.EmitIntAsByte b + let p_bool b st = p_byte (if b then 1 else 0) st -let prim_p_int32 i st = +/// Write an uncompressed integer to the main stream. +let prim_p_int32 i st = p_byte (b0 i) st p_byte (b1 i) st p_byte (b2 i) st p_byte (b3 i) st -/// Compress integers according to the same scheme used by CLR metadata -/// This halves the size of pickled data -let p_int32 n st = - if n >= 0 && n <= 0x7F then +/// Write an uncompressed integer to the B stream. +let prim_p_int32B i st = + p_byteB (b0 i) st + p_byteB (b1 i) st + p_byteB (b2 i) st + p_byteB (b3 i) st + +/// Compress integers according to the same scheme used by CLR metadata +/// This halves the size of pickled data +let p_int32 n st = + if n >= 0 && n <= 0x7F then p_byte (b0 n) st else if n >= 0x80 && n <= 0x3FFF then p_byte (0x80 ||| (n >>> 8)) st @@ -198,6 +211,17 @@ let p_int32 n st = p_byte 0xFF st prim_p_int32 n st +/// Write a compressed integer to the B stream. +let p_int32B n st = + if n >= 0 && n <= 0x7F then + p_byteB (b0 n) st + else if n >= 0x80 && n <= 0x3FFF then + p_byteB ( (0x80 ||| (n >>> 8))) st + p_byteB ( (n &&& 0xFF)) st + else + p_byteB 0xFF st + prim_p_int32B n st + let space = () let p_space n () st = for i = 0 to n - 1 do @@ -227,6 +251,7 @@ let p_prim_string (s: string) st = st.os.EmitBytes bytes let p_int c st = p_int32 c st +let p_intB c st = p_int32B c st let p_int8 (i: sbyte) st = p_int32 (int32 i) st let p_uint8 (i: byte) st = p_byte (int i) st let p_int16 (i: int16) st = p_int32 (int32 i) st @@ -267,12 +292,16 @@ let inline p_tup11 p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 (a, b, c, d, e, f, x7, x8 let u_byte st = int (st.is.ReadByte()) +/// Unpickle an uncompressed integer from the B stream +/// The extra B stream of bytes is implicitly 0 if not present +let u_byteB st = + if st.isB.IsEOF then 0 else int (st.isB.ReadByte()) + type unpickler<'T> = ReaderState -> 'T let u_bool st = let b = u_byte st in (b = 1) - - +/// Unpickle an uncompressed integer from the main stream let prim_u_int32 st = let b0 = (u_byte st) let b1 = (u_byte st) @@ -280,6 +309,14 @@ let prim_u_int32 st = let b3 = (u_byte st) b0 ||| (b1 <<< 8) ||| (b2 <<< 16) ||| (b3 <<< 24) +/// Unpickle an uncompressed integer from the B stream +let prim_u_int32B st = + let b0 = u_byteB st + let b1 = u_byteB st + let b2 = u_byteB st + let b3 = u_byteB st + b0 ||| (b1 <<< 8) ||| (b2 <<< 16) ||| (b3 <<< 24) + let u_int32 st = let b0 = u_byte st if b0 <= 0x7F then b0 @@ -291,6 +328,19 @@ let u_int32 st = assert(b0 = 0xFF) prim_u_int32 st +/// Unpickle a compressed integer from the B stream. +/// The integer is 0 if the B stream is not present. +let u_int32B st = + let b0 = u_byteB st + if b0 <= 0x7F then b0 + else if b0 <= 0xbf then + let b0 = b0 &&& 0x7F + let b1 = u_byteB st + (b0 <<< 8) ||| b1 + else + assert(b0 = 0xFF) + prim_u_int32B st + let u_byte_memory st = let n = (u_int32 st) st.is.ReadBytes n @@ -303,6 +353,7 @@ let u_prim_string st = st.is.ReadUtf8String len let u_int st = u_int32 st +let u_intB st = u_int32B st let u_int8 st = sbyte (u_int32 st) let u_uint8 st = byte (u_byte st) let u_int16 st = int16 (u_int32 st) @@ -444,7 +495,12 @@ let p_list_core f (xs: 'T list) st = let p_list f x st = p_int (List.length x) st p_list_core f x st -let p_list_ext extraf f x st = + +let p_listB f x st = + p_intB (List.length x) st + p_list_core f x st + +let p_list_ext extraf f x st = let n = List.length x let n = if Option.isSome extraf then n ||| 0x80000000 else n p_int n st @@ -530,6 +586,12 @@ let u_list_core f n st = let u_list f st = let n = u_int st u_list_core f n st + +/// Unpickle a list from the B stream. The resulting list is empty if the B stream is not present. +let u_listB f st = + let n = u_intB st + u_list_core f n st + let u_list_ext extra f st = let n = u_int st let extraItem = @@ -682,7 +744,12 @@ let p_nleref x st = p_int (encode_nleref st.occus st.ostrings st.onlerefs st.osc // Simple types are types like "int", represented as TType(Ref_nonlocal(..., "int"), []). // A huge number of these occur in pickled F# data, so make them unique. -let decode_simpletyp st _ccuTab _stringTab nlerefTab a = TType_app(ERefNonLocal (lookup_nleref st nlerefTab a), [], 0uy) +// +// NULLNESS - the simpletyp table now holds KnownAmbivalentToNull by default. +// For old assemblies it is, if we give those assemblies the ambivalent interpretation. +// For new asemblies compiled with null-checking on it isn't, if the default is to give +// those the KnownWithoutNull interpretation by default. +let decode_simpletyp st _ccuTab _stringTab nlerefTab a = TType_app(ERefNonLocal (lookup_nleref st nlerefTab a), [], KnownAmbivalentToNull) let u_encoded_simpletyp st = u_int st let u_simpletyp st = lookup_uniq st st.isimpletys (u_int st) let encode_simpletyp ccuTab stringTab nlerefTab simpleTyTab thisCcu a = encode_uniq simpleTyTab (encode_nleref ccuTab stringTab nlerefTab thisCcu a) @@ -696,6 +763,7 @@ let PickleBufferCapacity = 50000 let pickleObjWithDanglingCcus inMem file g scope p x = let st1 = { os = ByteBuffer.Create(PickleBufferCapacity, useArrayPool = true) + osB = ByteBuffer.Create(PickleBufferCapacity, useArrayPool = true) oscope=scope occus= Table<_>.Create "occus" oentities=NodeOutTable<_, _>.Create((fun (tc: Tycon) -> tc.Stamp), (fun tc -> tc.LogicalName), (fun tc -> tc.Range), id , "otycons") @@ -709,22 +777,24 @@ let pickleObjWithDanglingCcus inMem file g scope p x = oglobals=g ofile=file oInMem=inMem - isStructThisArgPos = false} - let ccuNameTab, (ntycons, ntypars, nvals, nanoninfos), stringTab, pubpathTab, nlerefTab, simpleTyTab, phase1bytes = + isStructThisArgPos = false } + + let ccuNameTab,(ntycons, ntypars, nvals, nanoninfos),stringTab,pubpathTab,nlerefTab,simpleTyTab,phase1bytes,phase1bytesB = p x st1 let sizes = st1.oentities.Size, st1.otypars.Size, st1.ovals.Size, st1.oanoninfos.Size - st1.occus, sizes, st1.ostrings, st1.opubpaths, st1.onlerefs, st1.osimpletys, st1.os.AsMemory() + st1.occus, sizes, st1.ostrings, st1.opubpaths, st1.onlerefs, st1.osimpletys, st1.os.AsMemory(), st1.osB let st2 = { os = ByteBuffer.Create(PickleBufferCapacity, useArrayPool = true) + osB = ByteBuffer.Create(PickleBufferCapacity, useArrayPool = true) oscope=scope occus= Table<_>.Create "occus (fake)" - oentities=NodeOutTable<_, _>.Create((fun (tc: Tycon) -> tc.Stamp), (fun tc -> tc.LogicalName), (fun tc -> tc.Range), id , "otycons") - otypars=NodeOutTable<_, _>.Create((fun (tp: Typar) -> tp.Stamp), (fun tp -> tp.DisplayName), (fun tp -> tp.Range), id , "otypars") + oentities=NodeOutTable<_, _>.Create((fun (tc: Tycon) -> tc.Stamp), (fun tc -> tc.LogicalName), (fun tc -> tc.Range), id, "otycons") + otypars=NodeOutTable<_, _>.Create((fun (tp: Typar) -> tp.Stamp), (fun tp -> tp.DisplayName), (fun tp -> tp.Range), id, "otypars") ovals=NodeOutTable<_, _>.Create((fun (v: Val) -> v.Stamp), (fun v -> v.LogicalName), (fun v -> v.Range), (fun osgn -> osgn), "ovals") oanoninfos=NodeOutTable<_, _>.Create((fun (v: AnonRecdTypeInfo) -> v.Stamp), (fun v -> string v.Stamp), (fun _ -> range0), id, "oanoninfos") ostrings=Table<_>.Create "ostrings (fake)" @@ -752,12 +822,18 @@ let pickleObjWithDanglingCcus inMem file g scope p x = p_memory (stringTab.AsArray, pubpathTab.AsArray, nlerefTab.AsArray, simpleTyTab.AsArray, phase1bytes) st2 + + // The B stream should be empty in the second phase + let phase2bytesB = st2.osB.AsMemory() + if phase2bytesB.Length <> 0 then failwith "expected phase2bytesB.Length = 0" + (st2.osB :> System.IDisposable).Dispose() st2.os (st1.os :> System.IDisposable).Dispose() - phase2bytes -let check (ilscope: ILScopeRef) (inMap: NodeInTable<_, _>) = + phase2bytes, phase1bytesB + +let check (ilscope: ILScopeRef) (inMap: NodeInTable<_,_>) = for i = 0 to inMap.Count - 1 do let n = inMap.Get i if not (inMap.IsLinked n) then @@ -767,9 +843,10 @@ let check (ilscope: ILScopeRef) (inMap: NodeInTable<_, _>) = // an identical copy of the source for the DLL containing the data being unpickled. A message will // then be printed indicating the name of the item. -let unpickleObjWithDanglingCcus file viewedScope (ilModule: ILModuleDef option) u (phase2bytes: ReadOnlyByteMemory) = +let unpickleObjWithDanglingCcus file viewedScope (ilModule: ILModuleDef option) u (phase2bytes: ReadOnlyByteMemory) (phase1bytesB: ReadOnlyByteMemory) = let st2 = { is = ByteStream.FromBytes (phase2bytes, 0, phase2bytes.Length) + isB = ByteStream.FromBytes (ByteMemory.FromArray([| |]).AsReadOnly(), 0, 0) iilscope = viewedScope iccus = new_itbl "iccus (fake)" [| |] ientities = NodeInTable<_, _>.Create (Tycon.NewUnlinked, (fun osgn tg -> osgn.Link tg), (fun osgn -> osgn.IsLinked), "itycons", 0) @@ -803,6 +880,7 @@ let unpickleObjWithDanglingCcus file viewedScope (ilModule: ILModuleDef option) let data = let st1 = { is = ByteStream.FromBytes (phase1bytes, 0, phase1bytes.Length) + isB = ByteStream.FromBytes (phase1bytesB, 0, phase1bytesB.Length) iccus = ccuTab iilscope = viewedScope ientities = NodeInTable<_, _>.Create(Tycon.NewUnlinked, (fun osgn tg -> osgn.Link tg), (fun osgn -> osgn.IsLinked), "itycons", ntycons) @@ -817,6 +895,7 @@ let unpickleObjWithDanglingCcus file viewedScope (ilModule: ILModuleDef option) iILModule = ilModule } let res = u st1 check viewedScope st1.ientities + check viewedScope st1.ientities check viewedScope st1.ivals check viewedScope st1.itypars res @@ -1564,7 +1643,20 @@ let p_tyar_constraint x st = | TyparConstraint.SupportsComparison _ -> p_byte 10 st | TyparConstraint.SupportsEquality _ -> p_byte 11 st | TyparConstraint.IsUnmanaged _ -> p_byte 12 st -let p_tyar_constraints = (p_list p_tyar_constraint) + | TyparConstraint.NotSupportsNull _ -> + failwith "NotSupportsNull constraints should only be emitted to streamB" + +// Some extra F# 5.0 constraints are stored in stream B, these will be ignored by earlier F# compilers +let p_tyar_constraintB x st = + match x with + | TyparConstraint.NotSupportsNull _ -> p_byteB 1 st + | _ -> failwith "only NotSupportsNull constraints should be emitted to streamB" + +let p_tyar_constraints cxs st = + let cxs1, cxs2 = cxs |> List.partition (function TyparConstraint.NotSupportsNull _ -> false | _ -> true) + p_list p_tyar_constraint cxs1 st + // Some extra F# 5.0 constraints are stored in stream B, these will be ignored by earlier F# compilers + p_listB p_tyar_constraintB cxs2 st let u_tyar_constraint st = let tag = u_byte st @@ -1584,9 +1676,21 @@ let u_tyar_constraint st = | 12 -> (fun _ -> TyparConstraint.IsUnmanaged range0) | _ -> ufailwith st "u_tyar_constraint" - -let u_tyar_constraints = (u_list_revi u_tyar_constraint) - +// Some extra F# 5.0 constraints are stored in stream B, these will be ignored by earlier F# compilers +let u_tyar_constraintB st = + let tag = u_byteB st + match tag with + | 1 -> TyparConstraint.NotSupportsNull range0 + | _ -> ufailwith st "u_tyar_constraintB - unexpected constraint in streamB" + +let u_tyar_constraints st = + let cxs1 = u_list_revi u_tyar_constraint st + // Some extra F# 5.0 constraints are stored in stream B, these will be ignored by earlier F# compilers + // + // If the B stream is not present (e.g. reading F# 4.5 components) then this list will be empty + // via the implementation of u_listB. + let cxs2 = u_listB u_tyar_constraintB st + cxs1 @ cxs2 let p_tyar_spec_data (x: Typar) st = p_tup5 @@ -1639,19 +1743,41 @@ let _ = fill_p_ty2 (fun isStructThisArgPos ty st -> else p_byte 0 st; p_tys l st - | TType_app(ERefNonLocal nleref, [], _) -> + | TType_app(ERefNonLocal nleref, [], nullness) -> + if st.oglobals.langFeatureNullness then + match nullness.Evaluate() with + | NullnessInfo.WithNull -> p_byteB 9 st + | NullnessInfo.WithoutNull -> p_byteB 10 st + | NullnessInfo.AmbivalentToNull -> p_byteB 11 st p_byte 1 st; p_simpletyp nleref st - | TType_app (tc, tinst, _) -> - p_byte 2 st; p_tup2 (p_tcref "typ") p_tys (tc, tinst) st - - | TType_fun (d, r, _) -> + | TType_app (tc, tinst, nullness) -> + if st.oglobals.langFeatureNullness then + match nullness.Evaluate() with + | NullnessInfo.WithNull -> p_byteB 12 st + | NullnessInfo.WithoutNull -> p_byteB 13 st + | NullnessInfo.AmbivalentToNull -> p_byteB 14 st + p_byte 2 st; p_tcref "typ" tc st; p_tys tinst st + + | TType_fun (d,r,nullness) -> + if st.oglobals.langFeatureNullness then + match nullness.Evaluate() with + | NullnessInfo.WithNull -> p_byteB 15 st + | NullnessInfo.WithoutNull -> p_byteB 16 st + | NullnessInfo.AmbivalentToNull -> p_byteB 17 st p_byte 3 st // Note, the "this" argument may be found in the domain position of a function type, so propagate the isStructThisArgPos value p_ty2 isStructThisArgPos d st p_ty r st - | TType_var (r, _) -> p_byte 4 st; p_tpref r st + | TType_var (r, nullness) -> + if st.oglobals.langFeatureNullness then + match nullness.Evaluate() with + | NullnessInfo.WithNull -> p_byteB 18 st + | NullnessInfo.WithoutNull -> p_byteB 19 st + | NullnessInfo.AmbivalentToNull -> p_byteB 20 st + p_byte 4 st + p_tpref r st | TType_forall (tps, r) -> p_byte 5 st @@ -1677,27 +1803,59 @@ let _ = fill_p_ty2 (fun isStructThisArgPos ty st -> let _ = fill_u_ty (fun st -> let tag = u_byte st + match tag with | 0 -> let l = u_tys st TType_tuple (tupInfoRef, l) - - | 1 -> - u_simpletyp st - - | 2 -> - let tc = u_tcref st + | 1 -> + let tagB = u_byteB st + let sty = u_simpletyp st + match tagB with + | 0 -> + sty + | 9 -> + match sty with + | TType_app(tcref, _, _) -> TType_app(tcref, [], KnownWithNull) + | _ -> ufailwith st "u_ty 9a" + | 10 -> + match sty with + | TType_app(tcref, _, _) -> TType_app(tcref, [], KnownWithoutNull) + | _ -> ufailwith st "u_ty 9b" + | 11 -> + match sty with + | TType_app(tcref, _, _) -> TType_app(tcref, [], KnownAmbivalentToNull) + | _ -> ufailwith st "u_ty 9c" + | b -> ufailwith st (sprintf "u_ty - 1/B, byte = %A" b) + | 2 -> + let tagB = u_byteB st + let tcref = u_tcref st let tinst = u_tys st - TType_app (tc, tinst, 0uy) - - | 3 -> + match tagB with + | 0 -> TType_app (tcref, tinst, KnownAmbivalentToNull) + | 12 -> TType_app (tcref, tinst, KnownWithNull) + | 13 -> TType_app (tcref, tinst, KnownWithoutNull) + | 14 -> TType_app (tcref, tinst, KnownAmbivalentToNull) + | _ -> ufailwith st "u_ty - 2/B" + | 3 -> + let tagB = u_byteB st let d = u_ty st let r = u_ty st - TType_fun (d, r, 0uy) - + match tagB with + | 0 -> TType_fun (d, r, KnownAmbivalentToNull) + | 15 -> TType_fun (d, r, KnownWithNull) + | 16 -> TType_fun (d, r, KnownWithoutNull) + | 17 -> TType_fun (d, r, KnownAmbivalentToNull) + | _ -> ufailwith st "u_ty - 3/B" | 4 -> + let tagB = u_byteB st let r = u_tpref st - r.AsType + match tagB with + | 0 -> r.AsType KnownAmbivalentToNull + | 18 -> r.AsType KnownWithNull + | 19 -> r.AsType KnownWithoutNull + | 20 -> r.AsType KnownAmbivalentToNull + | _ -> ufailwith st "u_ty - 4/B" | 5 -> let tps = u_tyar_specs st diff --git a/src/Compiler/TypedTree/TypedTreePickle.fsi b/src/Compiler/TypedTree/TypedTreePickle.fsi index 2d6fc2bdc4d..66a0017c001 100644 --- a/src/Compiler/TypedTree/TypedTreePickle.fsi +++ b/src/Compiler/TypedTree/TypedTreePickle.fsi @@ -85,7 +85,7 @@ val internal pickleCcuInfo: pickler /// Serialize an arbitrary object using the given pickler val pickleObjWithDanglingCcus: - inMem: bool -> file: string -> TcGlobals -> scope: CcuThunk -> pickler<'T> -> 'T -> ByteBuffer + inMem: bool -> file: string -> TcGlobals -> scope: CcuThunk -> pickler<'T> -> 'T -> ByteBuffer * ByteBuffer /// The type of state unpicklers read from type ReaderState @@ -151,5 +151,6 @@ val internal unpickleObjWithDanglingCcus: viewedScope: ILScopeRef -> ilModule: ILModuleDef option -> 'T unpickler -> + ReadOnlyByteMemory -> ReadOnlyByteMemory -> PickledDataWithReferences<'T> diff --git a/src/Compiler/TypedTree/tainted.fs b/src/Compiler/TypedTree/tainted.fs index 0eb274fa48b..0c825c3a11b 100644 --- a/src/Compiler/TypedTree/tainted.fs +++ b/src/Compiler/TypedTree/tainted.fs @@ -131,8 +131,8 @@ type internal Tainted<'T> (context: TaintedContext, value: 'T) = let u = this.Protect (fun x -> f (x, context.TypeProvider)) range Tainted(context, u) - member this.PApplyArray(f, methodName, range: range) = - let a : 'U[] = this.Protect f range + member this.PApplyArray(f, methodName, range:range) = + let a : 'U[] MaybeNull = this.Protect f range match a with | Null -> raise <| TypeProviderError(FSComp.SR.etProviderReturnedNull(methodName), this.TypeProviderDesignation, range) | NonNull a -> a |> Array.map (fun u -> Tainted(context,u)) @@ -163,8 +163,14 @@ type internal Tainted<'T> (context: TaintedContext, value: 'T) = Tainted(context, this.Protect(fun value -> box value :?> 'U) range) module internal Tainted = - let (|Null|NonNull|) (p:Tainted<'T>) : Choice> when 'T : null and 'T : not struct = + +#if NO_CHECKNULLS + let (|Null|NonNull|) (p:Tainted<'T>) : Choice> when 'T : null and 'T : not struct = if p.PUntaintNoFailure isNull then Null else NonNull (p.PApplyNoFailure id) +#else + let (|Null|NonNull|) (p:Tainted<'T __withnull>) : Choice> when 'T : __notnull and 'T : not struct = + if p.PUntaintNoFailure isNull then Null else NonNull (p.PApplyNoFailure nonNull) +#endif let Eq (p:Tainted<'T>) (v:'T) = p.PUntaintNoFailure (fun pv -> pv = v) diff --git a/src/Compiler/TypedTree/tainted.fsi b/src/Compiler/TypedTree/tainted.fsi index ee1a6d94069..8ad58f6f265 100644 --- a/src/Compiler/TypedTree/tainted.fsi +++ b/src/Compiler/TypedTree/tainted.fsi @@ -101,7 +101,11 @@ type internal Tainted<'T> = module internal Tainted = /// Test whether the tainted value is null - val (|Null|NonNull|): Tainted<'T MaybeNull> -> Choice> when 'T: null and 'T: not struct +#if NO_CHECKNULLS + val (|Null|NonNull|) : Tainted<'T MaybeNull> -> Choice> when 'T : null and 'T : not struct +#else + val (|Null|NonNull|) : Tainted<'T MaybeNull> -> Choice> when 'T : __notnull and 'T : not struct +#endif /// Test whether the tainted value equals given value. /// Failure in call to equality operation will be blamed on type provider of first operand diff --git a/src/Compiler/Utilities/illib.fs b/src/Compiler/Utilities/illib.fs index 8dbf5fd4534..7942b78593e 100644 --- a/src/Compiler/Utilities/illib.fs +++ b/src/Compiler/Utilities/illib.fs @@ -37,10 +37,11 @@ module internal PervasiveAutoOpens = | [ _ ] -> true | _ -> false - type 'T MaybeNull when 'T: null and 'T: not struct = 'T - let inline isNotNull (x: 'T) = not (isNull x) +#if NO_CHECKNULLS + type 'T MaybeNull when 'T: null and 'T: not struct = 'T + let inline (|NonNullQuick|) (x: 'T MaybeNull) = match x with | null -> raise (NullReferenceException()) @@ -60,6 +61,10 @@ module internal PervasiveAutoOpens = match x with | null -> raise (ArgumentNullException(paramName)) | v -> v +#else + type 'T MaybeNull when 'T: __notnull and 'T: not struct = 'T __withnull + +#endif let inline (===) x y = LanguagePrimitives.PhysicalEquality x y @@ -279,10 +284,7 @@ module Array = /// ~0.8x slower for ints let inline areEqual (xs: 'T[]) (ys: 'T[]) = match xs, ys with - | null, null -> true | [||], [||] -> true - | null, _ - | _, null -> false | _ when xs.Length <> ys.Length -> false | _ -> let mutable break' = false diff --git a/src/Compiler/Utilities/illib.fsi b/src/Compiler/Utilities/illib.fsi index 7272dace378..b2dabd69fde 100644 --- a/src/Compiler/Utilities/illib.fsi +++ b/src/Compiler/Utilities/illib.fsi @@ -26,6 +26,7 @@ module internal PervasiveAutoOpens = /// Returns true if the argument is non-null. val inline isNotNull: x: 'T -> bool when 'T: null +#if NO_CHECKNULLS /// Indicates that a type may be null. 'MaybeNull' is used internally in the F# compiler as /// replacement for 'string?' to align with FS-1060. type 'T MaybeNull when 'T: null and 'T: not struct = 'T @@ -41,6 +42,11 @@ module internal PervasiveAutoOpens = /// Checks the argument is non-null val inline nullArgCheck: paramName: string -> x: 'T MaybeNull -> 'T +#else + /// Indicates that a type may be null. 'MaybeNull' used internally in the F# compiler as unchecked + /// replacement for 'string?' + type 'T MaybeNull when 'T: __notnull and 'T: not struct = 'T __withnull +#endif val inline (===): x: 'a -> y: 'a -> bool when 'a: not struct diff --git a/src/Compiler/Utilities/lib.fs b/src/Compiler/Utilities/lib.fs index f4c37de4cf9..fd13790a90e 100755 --- a/src/Compiler/Utilities/lib.fs +++ b/src/Compiler/Utilities/lib.fs @@ -332,15 +332,15 @@ type Graph<'Data, 'Id when 'Id : comparison and 'Id : equality> // with care. //---------------------------------------------------------------------------- -type NonNullSlot<'T> = 'T -let nullableSlotEmpty() = Unchecked.defaultof<'T> -let nullableSlotFull x = x +type NonNullSlot<'T when 'T : not struct> = 'T +let nullableSlotEmpty() : NonNullSlot<'T> = Unchecked.defaultof<_> +let nullableSlotFull (x: 'T) : NonNullSlot<'T> = x //--------------------------------------------------------------------------- // Caches, mainly for free variables //--------------------------------------------------------------------------- -type cache<'T> = { mutable cacheVal: 'T NonNullSlot } +type cache<'T when 'T : not struct> = { mutable cacheVal: NonNullSlot<'T> } let newCache() = { cacheVal = nullableSlotEmpty() } let inline cached cache resF = diff --git a/src/Compiler/Utilities/lib.fsi b/src/Compiler/Utilities/lib.fsi index 2e6b0318f7a..a8f41f0cfd8 100644 --- a/src/Compiler/Utilities/lib.fsi +++ b/src/Compiler/Utilities/lib.fsi @@ -228,24 +228,24 @@ type Graph<'Data, 'Id when 'Id: comparison> = /// This is an unsafe trick, as it relies on the fact that the type of values /// being placed into the slot never utilizes "null" as a representation. To be used with /// with care. -type NonNullSlot<'T> = 'T +type NonNullSlot<'T when 'T: not struct> = 'T -val nullableSlotEmpty: unit -> 'T +val nullableSlotEmpty: unit -> NonNullSlot<'T> -val nullableSlotFull: x: 'a -> 'a +val nullableSlotFull: x: 'a -> NonNullSlot<'a> /// Caches, mainly for free variables -type cache<'T> = { mutable cacheVal: NonNullSlot<'T> } +type cache<'T when 'T: not struct> = { mutable cacheVal: NonNullSlot<'T> } -val newCache: unit -> cache<'a> +val newCache: unit -> cache<'a> when 'a: not struct -val inline cached: cache: cache<'a> -> resF: (unit -> 'a) -> 'a +val inline cached: cache: cache<'a> -> resF: (unit -> 'a) -> 'a when 'a: not struct val inline cacheOptByref: cache: byref<'T option> -> f: (unit -> 'T) -> 'T val inline cacheOptRef: cache: 'a option ref -> f: (unit -> 'a) -> 'a -val inline tryGetCacheValue: cache: cache<'a> -> NonNullSlot<'a> voption +val inline tryGetCacheValue: cache: cache<'a> -> NonNullSlot<'a> voption when 'a: not struct module UnmanagedProcessExecutionOptions = val EnableHeapTerminationOnCorruption: unit -> unit diff --git a/src/Compiler/Utilities/sformat.fs b/src/Compiler/Utilities/sformat.fs index 5d42dae0b45..bb119712d5a 100644 --- a/src/Compiler/Utilities/sformat.fs +++ b/src/Compiler/Utilities/sformat.fs @@ -116,6 +116,13 @@ type IEnvironment = abstract MaxColumns: int abstract MaxRows: int +#if NO_CHECKNULLS +[] +module NullShim = + // Shim to match nullness checking library support in preview + let inline (|Null|NonNull|) (x: 'T) : Choice = match x with null -> Null | v -> NonNull v +#endif + [] module TaggedText = let mkTag tag text = TaggedText(tag, text) @@ -491,7 +498,7 @@ module ReflectUtils = // of an F# value. let GetValueInfoOfObject (bindingFlags: BindingFlags) (obj: obj) = match obj with - | null -> NullValue + | Null -> NullValue | _ -> let reprty = obj.GetType() @@ -556,9 +563,8 @@ module ReflectUtils = let GetValueInfo bindingFlags (x: 'a, ty: Type) (* x could be null *) = let obj = (box x) - match obj with - | null -> + | Null -> let isNullaryUnion = match ty.GetCustomAttributes(typeof, false) with | [| :? CompilationRepresentationAttribute as attr |] -> @@ -576,7 +582,8 @@ module ReflectUtils = UnitValue else NullValue - | _ -> GetValueInfoOfObject bindingFlags obj + | NonNull obj -> + GetValueInfoOfObject bindingFlags obj module Display = open ReflectUtils @@ -836,8 +843,8 @@ module Display = let getListValueInfo bindingFlags (x: obj, ty: Type) = match x with - | null -> None - | _ -> + | Null -> None + | NonNull x -> match Value.GetValueInfo bindingFlags (x, ty) with | UnionCaseValue ("Cons", recd) -> Some(unpackCons recd) | UnionCaseValue ("Empty", [||]) -> None @@ -986,14 +993,13 @@ module Display = and objL showMode depthLim prec (x: obj, ty: Type) = let info = Value.GetValueInfo bindingFlags (x, ty) - try if depthLim <= 0 || exceededPrintSize () then wordL (tagPunctuation "...") else match x with - | null -> reprL showMode (depthLim - 1) prec info x - | _ -> + | Null -> reprL showMode (depthLim - 1) prec info x + | NonNull x -> if (path.ContainsKey(x)) then wordL (tagPunctuation "...") else @@ -1007,10 +1013,9 @@ module Display = Some(wordL (tagText (x.ToString()))) else // Try the StructuredFormatDisplayAttribute extensibility attribute - match ty.GetCustomAttributes(typeof, true) with - | null - | [||] -> None - | res -> structuredFormatObjectL showMode ty depthLim (res[0] :?> StructuredFormatDisplayAttribute) x + match ty.GetCustomAttributes (typeof, true) with + | Null | [| |] -> None + | NonNull res -> structuredFormatObjectL showMode ty depthLim (res[0] :?> StructuredFormatDisplayAttribute) x #if COMPILER // This is the PrintIntercepts extensibility point currently revealed by fsi.exe's AddPrinter @@ -1043,8 +1048,7 @@ module Display = // Format an object that has a layout specified by StructuredFormatAttribute and structuredFormatObjectL showMode ty depthLim (attr: StructuredFormatDisplayAttribute) (obj: obj) = let txt = attr.Value - - if isNull txt || txt.Length <= 1 then + if isNull (box txt) || txt.Length <= 1 then None else let messageRegexPattern = @"^(?
.*?)(?.*?)(?.*)$"
@@ -1514,7 +1518,7 @@ module Display =
 
     let leafFormatter (opts: FormatOptions) (obj: obj) =
         match obj with
-        | null -> tagKeyword "null"
+        | Null -> tagKeyword "null"
         | :? double as d ->
             let s = d.ToString(opts.FloatingPointFormat, opts.FormatProvider)
 
diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy
index 3d460406c80..29c6bb5071f 100644
--- a/src/Compiler/pars.fsy
+++ b/src/Compiler/pars.fsy
@@ -89,6 +89,7 @@ let parse_error_rich = Some(fun (ctxt: ParseErrorContext<_>) ->
 %token GREATER_RBRACK STRUCT SIG
 %token STATIC MEMBER CLASS ABSTRACT OVERRIDE DEFAULT CONSTRUCTOR INHERIT
 %token EXTERN VOID PUBLIC PRIVATE INTERNAL GLOBAL
+%token MAYBENULL__ NOTNULL__ WITHNULL__
 
 /* for parser 'escape hatch' out of expression context without consuming the 'recover' token */
 %token TYPE_COMING_SOON TYPE_IS_HERE MODULE_COMING_SOON MODULE_IS_HERE
@@ -1546,21 +1547,22 @@ tyconDefn:
            SynTypeDefn($1, SynTypeDefnRepr.Simple(SynTypeDefnSimpleRepr.None($1.Range), $1.Range), [], None, $1.Range, trivia) }
 
   | typeNameInfo opt_equals tyconDefnRhsBlock
-     { match $2 with
-       | Some _ -> ()
-       | None ->
-            let (SynComponentInfo(_, _, _, lid, _, _, _, _)) = $1
-            // While the spec doesn't allow long idents here, the parser doesn't enforce this, so take one ident
-            let typeNameId = List.last lid
-            raiseParseErrorAt (rhs parseState 2) (FSComp.SR.parsEqualsMissingInTypeDefinition(typeNameId.ToString()))
+     { //match $2 with
+       //| Some _ -> ()
+       //| None ->
+       //     let (SynComponentInfo(_, _, _, lid, _, _, _, _)) = $1
+       //     // While the spec doesn't allow long idents here, the parser doesn't enforce this, so take one ident
+       //     let typeNameId = List.last lid
+       //     raiseParseErrorAt (rhs parseState 2) (FSComp.SR.parsEqualsMissingInTypeDefinition(typeNameId.ToString()))
 
        let nameRange = rhs parseState 1
        let (tcDefRepr: SynTypeDefnRepr), mWith, members = $3 nameRange
        let declRange = unionRanges (rhs parseState 1) tcDefRepr.Range
        let mWhole = (declRange, members) ||> unionRangeWithListBy (fun (mem: SynMemberDefn) -> mem.Range)
+       let mEquals = rhs parseState 2
 
        fun leadingKeyword ->
-           let trivia: SynTypeDefnTrivia = { LeadingKeyword = leadingKeyword; EqualsRange = $2; WithKeyword = mWith }
+           let trivia: SynTypeDefnTrivia = { LeadingKeyword = leadingKeyword; EqualsRange = Some mEquals; WithKeyword = mWith }
            SynTypeDefn($1, tcDefRepr, members, None, mWhole, trivia) }
 
   | typeNameInfo tyconDefnAugmentation
@@ -2311,11 +2313,20 @@ typeConstraint:
      { SynTypeConstraint.WhereTyparIsValueType($1, lhs parseState) }
 
   | typar COLON IDENT STRUCT
-     { if $3 <> "not" then reportParseErrorAt (rhs parseState 3) (FSComp.SR.parsUnexpectedIdentifier($3))
+     { if $3 <> "not" then reportParseErrorAt (rhs parseState 3) (FSComp.SR.parsUnexpectedIdentifier($3 + " (1)"))
        SynTypeConstraint.WhereTyparIsReferenceType($1, lhs parseState) }
 
   | typar COLON NULL
      { SynTypeConstraint.WhereTyparSupportsNull($1, lhs parseState) }
+/*
+** TODO: This rule is not triggering, faking it with __notnull for now
+
+  | typar COLON IDENT NULL
+      { if $3 <> "not" then reportParseErrorAt (rhs parseState 3) (FSComp.SR.parsUnexpectedIdentifier($3 + " (2)"))  
+        SynTypeConstraint.WhereTyparNotSupportsNull($1, lhs parseState) }
+*/
+  | typar COLON NOTNULL__
+      { SynTypeConstraint.WhereTyparNotSupportsNull($1, lhs parseState) }
 
   | typar COLON LPAREN classMemberSpfn rparen
      { let tp = $1
@@ -2335,14 +2346,14 @@ typeConstraint:
        | "enum" ->
            let _ltm, _gtm, args, _commas, mWhole = $4
            SynTypeConstraint.WhereTyparIsEnum($1, args, unionRanges $1.Range mWhole)
-       | nm -> raiseParseErrorAt (rhs parseState 3) (FSComp.SR.parsUnexpectedIdentifier(nm)) }
+       | nm -> raiseParseErrorAt (rhs parseState 3) (FSComp.SR.parsUnexpectedIdentifier(nm + " (3)")) }
 
   | typar COLON IDENT
      { match $3 with
        | "comparison" -> SynTypeConstraint.WhereTyparIsComparable($1, lhs parseState)
        | "equality" -> SynTypeConstraint.WhereTyparIsEquatable($1, lhs parseState)
        | "unmanaged" -> SynTypeConstraint.WhereTyparIsUnmanaged($1, lhs parseState)
-       | nm -> raiseParseErrorAt (rhs parseState 3) (FSComp.SR.parsUnexpectedIdentifier(nm)) }
+       | nm -> raiseParseErrorAt (rhs parseState 3) (FSComp.SR.parsUnexpectedIdentifier(nm + " (4)")) }
 
   | appType
      { SynTypeConstraint.WhereSelfConstrained($1, lhs parseState) }
@@ -2827,6 +2838,13 @@ cType:
      { let m = lhs parseState
        SynType.App(SynType.LongIdent(SynLongIdent([ident("nativeptr", m)], [], [ Some(IdentTrivia.OriginalNotation "*") ])), None, [$1], [], None, true, m) }
 
+/*
+  | cType QMARK
+    { SynType.WithNull($1, false, lhs parseState) }
+*/
+  | cType WITHNULL__
+    { SynType.WithNull($1, false, lhs parseState) }
+
   | cType AMP
      { let m = lhs parseState
        SynType.App(SynType.LongIdent(SynLongIdent([ident("byref", m)], [], [ Some(IdentTrivia.OriginalNotation "&") ])), None, [$1], [], None, true, m) }
@@ -5536,8 +5554,19 @@ appTypeConPower:
     { $1 }
 
 appType:
+  | appType MAYBENULL__
+    { SynType.WithNull($1, true, lhs parseState) }
+
+  | appType WITHNULL__
+    { SynType.WithNull($1, false, lhs parseState) }
+
+/*
+  | appType QMARK
+    { SynType.WithNull($1, false, lhs parseState) }
+*/
+
   | appType arrayTypeSuffix
-      { SynType.Array($2, $1, lhs parseState) }
+      {  SynType.Array($2, $1, lhs parseState) }
 
   | appType HIGH_PRECEDENCE_BRACK_APP arrayTypeSuffix /* only HPA for "name[]" allowed here */
       { SynType.Array($3, $1, lhs parseState) }
@@ -5765,7 +5794,7 @@ atomType:
 
   | NULL
      { let m = rhs parseState 1
-       SynType.StaticConstant(SynConst.String(null, SynStringKind.Regular, m), m) }
+       SynType.StaticConstantNull(m) }
 
   | CONST atomicExpr
      { let e, _ = $2
diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf
index 68d321a7cdf..0c35b2fb1a1 100644
--- a/src/Compiler/xlf/FSComp.txt.cs.xlf
+++ b/src/Compiler/xlf/FSComp.txt.cs.xlf
@@ -92,6 +92,11 @@
         Použití incr z knihovny F# je zastaralé. Více informací: https://aka.ms/fsharp-refcell-ops. Změňte prosím například incr cell na cell.Value <- cell.Value + 1.
         
       
+      
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        
+      
       
         If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance let bindings are not allowed.
         Pokud typ používá atribut [<Sealed>] i [<AbstractClass>], znamená to, že je statický. Členové instance nejsou povoleni.
@@ -117,6 +122,11 @@
         Dostupná přetížení:\n{0}
         
       
+      
+        The constraints 'delegate' and 'comparison' are inconsistent
+        The constraints 'delegate' and 'comparison' are inconsistent
+        
+      
       
         A generic construct requires that a generic type parameter be known as a struct or reference type. Consider adding a type annotation.
         Obecná konstrukce vyžaduje, aby byl parametr obecného typu známý jako typ struct nebo reference. Zvažte možnost přidat anotaci typu.
@@ -147,6 +157,11 @@
         Známý typ parametru: {0}
         
       
+      
+        The constraints 'null' and 'not null' are inconsistent
+        The constraints 'null' and 'not null' are inconsistent
+        
+      
       
         Argument at index {0} doesn't match
         Argument na indexu {0} neodpovídá
@@ -157,6 +172,21 @@
         Argument {0} neodpovídá
         
       
+      
+        The constraints 'struct' and 'null' are inconsistent
+        The constraints 'struct' and 'null' are inconsistent
+        
+      
+      
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        
+      
+      
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        
+      
       
         All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n    {1}    \nThis branch returns a tuple of length {2} of type\n    {3}    \n
         Všechny větve výrazu if musí vracet hodnoty implicitně převoditelné na typ první větve, což je řazená kolekce členů o délce {0} typu\n    {1}    \nTato větev vrací řazenou kolekci členů o délce {2} typu\n    {3}    \n
@@ -177,6 +207,11 @@
         Atribut sestavení {0} odkazuje na navržené sestavení {1}, které se nedá načíst nebo neexistuje. Ohlášená výjimka: {2} – {3}
         
       
+      
+        {0} for F# {1}
+        {0} for F# {1}
+        
+      
       
         additional type-directed conversions
         další převody orientované na typ
@@ -332,6 +367,11 @@
         nepovinný zprostředkovatel komunikace s možnou hodnotou null
         
       
+      
+        nullness checking
+        nullness checking
+        
+      
       
         open type declaration
         Otevřít deklaraci typu
@@ -647,6 +687,11 @@
         Vytiskněte odvozená rozhraní všech kompilovaných souborů do přidružených souborů podpisu.
         
       
+      
+        Enable nullness declarations and checks
+        Enable nullness declarations and checks
+        
+      
       
         Clear the package manager results cache
         Vymazat mezipaměť výsledků správce balíčků
@@ -957,6 +1002,11 @@
         Atributy nejde použít pro rozšíření typů.
         
       
+      
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        
+      
       
         This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.
         Tento výraz záznamu kopírování a aktualizace mění všechna pole typu záznamu '{0}'. Zvažte použití syntaxe konstrukce záznamu.
@@ -1102,6 +1152,11 @@
         Hodnota {0} není funkce a nepodporuje zápis indexu.
         
       
+      
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        
+      
       
         The syntax 'expr1[expr2]' is ambiguous when used as an argument. See https://aka.ms/fsharp-index-notation. If you intend indexing or slicing then you must use 'expr1.[expr2]' in argument position. If calling a function with multiple curried arguments, add a space between them, e.g. 'someFunction expr1 [expr2]'.
         Syntaxe expr1[expr2] je při použití jako argument nejednoznačná. Více informací: https://aka.ms/fsharp-index-notation. Pokud plánujete indexování nebo vytváření řezů, musíte použít expr1.[expr2] na pozici argumentu. Pokud voláte funkci s vícenásobnými curryfikovanými argumenty, přidejte mezi ně mezeru, třeba someFunction expr1 [expr2].
@@ -1167,6 +1222,11 @@
         Tento výraz implicitně převede typ {0} na typ {1}. Přečtěte si téma https://aka.ms/fsharp-implicit-convs.
         
       
+      
+        The type '{0}' does not support a nullness qualitification.
+        The type '{0}' does not support a nullness qualitification.
+        
+      
       
         SynType.Or is not permitted in this declaration
         SynType.Or není v této deklaraci povoleno
diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf
index ee52b662d1d..be02e5e8b23 100644
--- a/src/Compiler/xlf/FSComp.txt.de.xlf
+++ b/src/Compiler/xlf/FSComp.txt.de.xlf
@@ -92,6 +92,11 @@
         Die Verwendung von "incr" aus der F#-Bibliothek ist veraltet. Siehe https://aka.ms/fsharp-refcell-ops. Ändern Sie z. B. "incr cell" in "cell.Value <- cell.Value + 1".
         
       
+      
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        
+      
       
         If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance let bindings are not allowed.
         Wenn ein Typ sowohl das Attribute [<Sealed>] wie auch [<AbstractClass>] verwendet, bedeutet dies, dass er statisch ist. Let-Bindungen der Instanz sind nicht zulässig.
@@ -117,6 +122,11 @@
         Verfügbare Überladungen:\n{0}
         
       
+      
+        The constraints 'delegate' and 'comparison' are inconsistent
+        The constraints 'delegate' and 'comparison' are inconsistent
+        
+      
       
         A generic construct requires that a generic type parameter be known as a struct or reference type. Consider adding a type annotation.
         Für ein generisches Konstrukt muss ein generischer Typparameter als Struktur- oder Verweistyp bekannt sein. Erwägen Sie das Hinzufügen einer Typanmerkung.
@@ -147,6 +157,11 @@
         Bekannter Typparameter: {0}
         
       
+      
+        The constraints 'null' and 'not null' are inconsistent
+        The constraints 'null' and 'not null' are inconsistent
+        
+      
       
         Argument at index {0} doesn't match
         Das Argument bei Index {0} stimmt nicht überein.
@@ -157,6 +172,21 @@
         Das Argument "{0}" stimmt nicht überein.
         
       
+      
+        The constraints 'struct' and 'null' are inconsistent
+        The constraints 'struct' and 'null' are inconsistent
+        
+      
+      
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        
+      
+      
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        
+      
       
         All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n    {1}    \nThis branch returns a tuple of length {2} of type\n    {3}    \n
         Alle Verzweigungen eines „If-Ausdrucks“ müssen Werte zurückgeben, die implizit in den Typ der ersten Verzweigung konvertierbar sind. In diesem Fall handelt es sich dabei um ein Tupel der Länge {0} des Typs.\n    {1}    \nDiese Verzweigung gibt ein Tupel der Länge {2} des Typs\n    {3}    \nzurück.
@@ -177,6 +207,11 @@
         Das Assemblyattribut "{0}" verweist auf eine Designerassembly "{1}", die entweder nicht geladen werden kann oder nicht vorhanden ist. Gemeldete Ausnahme: {2} – {3}
         
       
+      
+        {0} for F# {1}
+        {0} for F# {1}
+        
+      
       
         additional type-directed conversions
         zusätzliche typgesteuerte Konvertierungen
@@ -332,6 +367,11 @@
         Interop, NULL-Werte zulassend, optional
         
       
+      
+        nullness checking
+        nullness checking
+        
+      
       
         open type declaration
         Deklaration für offene Typen
@@ -647,6 +687,11 @@
         Drucken der abgeleiteten Schnittstellen aller Dateien an zugehörige Signaturdateien
         
       
+      
+        Enable nullness declarations and checks
+        Enable nullness declarations and checks
+        
+      
       
         Clear the package manager results cache
         Löschen Sie den Ergebniscache des Paketmanagers
@@ -957,6 +1002,11 @@
         Attribute können nicht auf Typerweiterungen angewendet werden.
         
       
+      
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        
+      
       
         This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.
         Dieser Ausdruck zum Kopieren und Aktualisieren von Datensätzen ändert alle Felder des Datensatztyps "{0}". Erwägen Sie stattdessen die Verwendung der Datensatzerstellungssyntax.
@@ -1102,6 +1152,11 @@
         Der Wert "{0}" ist keine Funktion und unterstützt keine Indexnotation.
         
       
+      
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        
+      
       
         The syntax 'expr1[expr2]' is ambiguous when used as an argument. See https://aka.ms/fsharp-index-notation. If you intend indexing or slicing then you must use 'expr1.[expr2]' in argument position. If calling a function with multiple curried arguments, add a space between them, e.g. 'someFunction expr1 [expr2]'.
         Die Syntax "expr1[expr2]" ist mehrdeutig, wenn sie als Argument verwendet wird. Siehe https://aka.ms/fsharp-index-notation. Wenn Sie indizieren oder aufteilen möchten, müssen Sie "expr1.[expr2]' in Argumentposition verwenden. Wenn Sie eine Funktion mit mehreren geschweiften Argumenten aufrufen, fügen Sie ein Leerzeichen dazwischen hinzu, z. B. "someFunction expr1 [expr2]".
@@ -1167,6 +1222,11 @@
         Dieser Ausdruck konvertiert den Typ "{0}" implizit in den Typ "{1}". Siehe https://aka.ms/fsharp-implicit-convs.
         
       
+      
+        The type '{0}' does not support a nullness qualitification.
+        The type '{0}' does not support a nullness qualitification.
+        
+      
       
         SynType.Or is not permitted in this declaration
         SynType.Or ist in dieser Deklaration nicht zulässig.
diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf
index 9748064eb19..b840c9cf4f3 100644
--- a/src/Compiler/xlf/FSComp.txt.es.xlf
+++ b/src/Compiler/xlf/FSComp.txt.es.xlf
@@ -92,6 +92,11 @@
         El uso de "incr" de la biblioteca de F# está en desuso. Vea https://aka.ms/fsharp-refcell-ops. Por ejemplo, cambie "incr cell" a "cell.Value <- cell.Value + 1".
         
       
+      
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        
+      
       
         If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance let bindings are not allowed.
         Si un tipo utiliza ambos atributos [<Sealed>] y [<AbstractClass>], significa que es estático. No se permiten los enlaces let de instancia.
@@ -117,6 +122,11 @@
         Sobrecargas disponibles:\n{0}
         
       
+      
+        The constraints 'delegate' and 'comparison' are inconsistent
+        The constraints 'delegate' and 'comparison' are inconsistent
+        
+      
       
         A generic construct requires that a generic type parameter be known as a struct or reference type. Consider adding a type annotation.
         Una construcción genérica requiere que un parámetro de tipo genérico se conozca como tipo de referencia o estructura. Puede agregar una anotación de tipo.
@@ -147,6 +157,11 @@
         Parámetro de tipo conocido: {0}
         
       
+      
+        The constraints 'null' and 'not null' are inconsistent
+        The constraints 'null' and 'not null' are inconsistent
+        
+      
       
         Argument at index {0} doesn't match
         El argumento del índice {0} no coincide.
@@ -157,6 +172,21 @@
         El argumento "{0}" no coincide.
         
       
+      
+        The constraints 'struct' and 'null' are inconsistent
+        The constraints 'struct' and 'null' are inconsistent
+        
+      
+      
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        
+      
+      
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        
+      
       
         All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n    {1}    \nThis branch returns a tuple of length {2} of type\n    {3}    \n
         Todas las ramas de una expresión 'if' deben devolver valores implícitamente convertibles al tipo de la primera rama, que aquí es una tupla de longitud {0} de tipo\n    {1}    \nEsta rama devuelve una tupla de longitud {2} de tipo\n    {3}    \n
@@ -177,6 +207,11 @@
         El atributo de ensamblado "{0}" hace referencia a un ensamblado de diseñador "{1}" que no se puede cargar o no existe. Se notificó la excepción: {2} - {3}.
         
       
+      
+        {0} for F# {1}
+        {0} for F# {1}
+        
+      
       
         additional type-directed conversions
         conversiones adicionales dirigidas a tipos
@@ -332,6 +367,11 @@
         interoperabilidad opcional que admite valores NULL
         
       
+      
+        nullness checking
+        nullness checking
+        
+      
       
         open type declaration
         declaración de tipo abierto
@@ -647,6 +687,11 @@
         Imprimir las interfaces deducidas de todos los archivos de compilación en los archivos de signatura asociados
         
       
+      
+        Enable nullness declarations and checks
+        Enable nullness declarations and checks
+        
+      
       
         Clear the package manager results cache
         Borrar la caché de resultados del administrador de paquetes
@@ -957,6 +1002,11 @@
         Los atributos no se pueden aplicar a las extensiones de tipo.
         
       
+      
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        
+      
       
         This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.
         Esta expresión de copia y actualización de registros cambia todos los campos de tipo de registro "{0}". Es preferible utilizar la sintaxis de construcción de registros.
@@ -1102,6 +1152,11 @@
         El valor "{0}" no es una función y no admite la notación de índices.
         
       
+      
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        
+      
       
         The syntax 'expr1[expr2]' is ambiguous when used as an argument. See https://aka.ms/fsharp-index-notation. If you intend indexing or slicing then you must use 'expr1.[expr2]' in argument position. If calling a function with multiple curried arguments, add a space between them, e.g. 'someFunction expr1 [expr2]'.
         La sintaxis "expr1[expr2]" es ambigua cuando se usa como argumento. Vea https://aka.ms/fsharp-index-notation. Si piensa indexar o segmentar, debe usar "expr1.[expr2]" en la posición del argumento. Si se llama a una función con varios argumentos currificados, se agregará un espacio entre ellos, por ejemplo, "unaFunción expr1 [expr2]".
@@ -1167,6 +1222,11 @@
         Esta expresión convierte implícitamente el tipo '{0}' al tipo '{1}'. Consulte https://aka.ms/fsharp-implicit-convs.
         
       
+      
+        The type '{0}' does not support a nullness qualitification.
+        The type '{0}' does not support a nullness qualitification.
+        
+      
       
         SynType.Or is not permitted in this declaration
         SynType.Or no se permite en esta declaración
diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf
index d0eb8a412f3..e7ffcb0e4c1 100644
--- a/src/Compiler/xlf/FSComp.txt.fr.xlf
+++ b/src/Compiler/xlf/FSComp.txt.fr.xlf
@@ -92,6 +92,11 @@
         L’utilisation de « incr » à partir de la bibliothèque F# est déconseillée. Voir https://aka.ms/fsharp-refcell-ops. Par exemple, veuillez remplacer « incr cell » par « cell.Value <- cell.Value + 1 ».
         
       
+      
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        
+      
       
         If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance let bindings are not allowed.
         Si un type utilise à la fois les attributs [<Sealed>] et [<AbstractClass>], cela signifie qu'il est statique. Les liaisons let d'instance ne sont pas autorisées.
@@ -117,6 +122,11 @@
         Surcharges disponibles :\n{0}
         
       
+      
+        The constraints 'delegate' and 'comparison' are inconsistent
+        The constraints 'delegate' and 'comparison' are inconsistent
+        
+      
       
         A generic construct requires that a generic type parameter be known as a struct or reference type. Consider adding a type annotation.
         L'utilisation d'une construction générique est possible uniquement si un paramètre de type générique est connu en tant que type struct ou type référence. Ajoutez une annotation de type.
@@ -147,6 +157,11 @@
         Paramètre de type connu : {0}
         
       
+      
+        The constraints 'null' and 'not null' are inconsistent
+        The constraints 'null' and 'not null' are inconsistent
+        
+      
       
         Argument at index {0} doesn't match
         L'argument à l'index {0} ne correspond pas
@@ -157,6 +172,21 @@
         L'argument '{0}' ne correspond pas
         
       
+      
+        The constraints 'struct' and 'null' are inconsistent
+        The constraints 'struct' and 'null' are inconsistent
+        
+      
+      
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        
+      
+      
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        
+      
       
         All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n    {1}    \nThis branch returns a tuple of length {2} of type\n    {3}    \n
         Toutes les branches d’une expression « if » doivent retourner des valeurs implicitement convertibles au type de la première branche, qui est ici un tuple de longueur {0} de type\n    {1}    \nCette branche renvoie un tuple de longueur {2} de type\n    {3}    \n
@@ -177,6 +207,11 @@
         L'attribut d'assembly '{0}' fait référence à un assembly de concepteur '{1}' qui ne peut pas être chargé ou qui n'existe pas. Exception signalée : {2} - {3}
         
       
+      
+        {0} for F# {1}
+        {0} for F# {1}
+        
+      
       
         additional type-directed conversions
         conversions supplémentaires dirigées vers le type
@@ -332,6 +367,11 @@
         interopérabilité facultative pouvant accepter une valeur null
         
       
+      
+        nullness checking
+        nullness checking
+        
+      
       
         open type declaration
         déclaration de type ouverte
@@ -647,6 +687,11 @@
         Imprimer les interfaces inférées de tous les fichiers de compilation sur les fichiers de signature associés
         
       
+      
+        Enable nullness declarations and checks
+        Enable nullness declarations and checks
+        
+      
       
         Clear the package manager results cache
         Effacer le cache des résultats du Gestionnaire de package
@@ -957,6 +1002,11 @@
         Impossible d'appliquer des attributs aux extensions de type.
         
       
+      
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        
+      
       
         This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.
         Cette expression d'enregistrement de copie et de mise à jour modifie tous les champs du type d'enregistrement '{0}'. Envisagez d'utiliser la syntaxe de construction d'enregistrement à la place.
@@ -1102,6 +1152,11 @@
         La valeur « {0} » n’est pas une fonction et ne prend pas en charge la notation d’index.
         
       
+      
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        
+      
       
         The syntax 'expr1[expr2]' is ambiguous when used as an argument. See https://aka.ms/fsharp-index-notation. If you intend indexing or slicing then you must use 'expr1.[expr2]' in argument position. If calling a function with multiple curried arguments, add a space between them, e.g. 'someFunction expr1 [expr2]'.
         La syntaxe « expr1[expr2] » est ambiguë lorsqu’elle est utilisée comme argument. Voir https://aka.ms/fsharp-index-notation. Si vous avez l’intention d’indexer ou de découper, vous devez utiliser « expr1.[expr2] » en position d’argument. Si vous appelez une fonction avec plusieurs arguments codés, ajoutez un espace entre eux, par exemple « someFunction expr1 [expr2] ».
@@ -1167,6 +1222,11 @@
         Cette expression convertit implicitement le type « {0} » en type « {1} ». Voir https://aka.ms/fsharp-implicit-convs.
         
       
+      
+        The type '{0}' does not support a nullness qualitification.
+        The type '{0}' does not support a nullness qualitification.
+        
+      
       
         SynType.Or is not permitted in this declaration
         SynType.Or n’est pas autorisé dans cette déclaration
diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf
index a1c4c79cf5a..ec86e99587b 100644
--- a/src/Compiler/xlf/FSComp.txt.it.xlf
+++ b/src/Compiler/xlf/FSComp.txt.it.xlf
@@ -92,6 +92,11 @@
         L'uso di 'incr' dalla libreria F # è deprecato. Vedere https://aka.ms/fsharp-refcell-ops. Ad esempio, modificare 'incr cell' in 'cell.Value <- cell.Value + 1'.
         
       
+      
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        
+      
       
         If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance let bindings are not allowed.
         Se un tipo usa entrambi gli attributi [<Sealed>] e [<AbstractClass>], significa che è statico. Non sono consentite associazioni let delle istanze.
@@ -117,6 +122,11 @@
         Overload disponibili:\n{0}
         
       
+      
+        The constraints 'delegate' and 'comparison' are inconsistent
+        The constraints 'delegate' and 'comparison' are inconsistent
+        
+      
       
         A generic construct requires that a generic type parameter be known as a struct or reference type. Consider adding a type annotation.
         Un costrutto generico richiede che un parametro di tipo generico sia noto come tipo riferimento o struct. Provare ad aggiungere un'annotazione di tipo.
@@ -147,6 +157,11 @@
         Parametro di tipo noto: {0}
         
       
+      
+        The constraints 'null' and 'not null' are inconsistent
+        The constraints 'null' and 'not null' are inconsistent
+        
+      
       
         Argument at index {0} doesn't match
         L'argomento alla posizione di indice {0} non corrisponde
@@ -157,6 +172,21 @@
         L'argomento '{0}' non corrisponde
         
       
+      
+        The constraints 'struct' and 'null' are inconsistent
+        The constraints 'struct' and 'null' are inconsistent
+        
+      
+      
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        
+      
+      
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        
+      
       
         All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n    {1}    \nThis branch returns a tuple of length {2} of type\n    {3}    \n
         Tutti i rami di un'espressione 'if' devono restituire valori convertibili in modo implicito nel tipo del primo ramo, che è una tupla di lunghezza {0} di tipo\n    {1}     \nQuesto ramo restituisce una tupla di lunghezza {2} di tipo\n    {3} \n
@@ -177,6 +207,11 @@
         L'attributo di assembly '{0}' fa riferimento a un assembly '{1}' della finestra di progettazione che non è stato caricato o non esiste. L'eccezione restituita è {2} - {3}
         
       
+      
+        {0} for F# {1}
+        {0} for F# {1}
+        
+      
       
         additional type-directed conversions
         conversioni aggiuntive dirette ai tipi
@@ -332,6 +367,11 @@
         Interop facoltativo nullable
         
       
+      
+        nullness checking
+        nullness checking
+        
+      
       
         open type declaration
         dichiarazione di tipo aperto
@@ -647,6 +687,11 @@
         Stampare le interfacce derivate di tutti i file di compilazione nei file di firma associati
         
       
+      
+        Enable nullness declarations and checks
+        Enable nullness declarations and checks
+        
+      
       
         Clear the package manager results cache
         Cancellare la cache dei risultati di Gestione pacchetti
@@ -957,6 +1002,11 @@
         Gli attributi non possono essere applicati a estensioni di tipo.
         
       
+      
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        
+      
       
         This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.
         Questa espressione di record di copia e aggiornamento modifica tutti i campi del tipo di record '{0}'. Provare a usare la sintassi di costruzione dei record.
@@ -1102,6 +1152,11 @@
         Questo valore '{0}' non è una funzione e non supporta la notazione degli indici.
         
       
+      
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        
+      
       
         The syntax 'expr1[expr2]' is ambiguous when used as an argument. See https://aka.ms/fsharp-index-notation. If you intend indexing or slicing then you must use 'expr1.[expr2]' in argument position. If calling a function with multiple curried arguments, add a space between them, e.g. 'someFunction expr1 [expr2]'.
         La sintassi 'expr1[expr2]' è ambigua se usata come argomento. Vedere https://aka.ms/fsharp-index-notation. Se si intende eseguire l'indicizzazione o il sezionamento, è necessario usare 'expr1.[expr2]' nella posizione dell'argomento. Se si chiama una funzione con più argomenti sottoposti a corsi, aggiungere uno spazio tra di essi, ad esempio 'someFunction expr1 [expr2]'.
@@ -1167,6 +1222,11 @@
         Questa espressione converte in modo implicito il tipo '{0}' nel tipo '{1}'. Vedere https://aka.ms/fsharp-implicit-convs.
         
       
+      
+        The type '{0}' does not support a nullness qualitification.
+        The type '{0}' does not support a nullness qualitification.
+        
+      
       
         SynType.Or is not permitted in this declaration
         SynType.Or non è consentito in questa dichiarazione
diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf
index a1e4157a4de..39416b6337c 100644
--- a/src/Compiler/xlf/FSComp.txt.ja.xlf
+++ b/src/Compiler/xlf/FSComp.txt.ja.xlf
@@ -92,6 +92,11 @@
         F# ライブラリからの 'incr' の使用は非推奨です。https://aka.ms/fsharp-refcell-ops を参照してください。たとえば、'incr cell' を 'cell.Value <- cell.Value +1' に変更してください。
         
       
+      
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        
+      
       
         If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance let bindings are not allowed.
         型が [<Sealed>] と [<AbstractClass>] の両方の属性を使用する場合、それは静的であることを意味します。インスタンス let バインドは許可されません。
@@ -117,6 +122,11 @@
         使用可能なオーバーロード:\n{0}
         
       
+      
+        The constraints 'delegate' and 'comparison' are inconsistent
+        The constraints 'delegate' and 'comparison' are inconsistent
+        
+      
       
         A generic construct requires that a generic type parameter be known as a struct or reference type. Consider adding a type annotation.
         ジェネリック コンストラクトでは、ジェネリック型パラメーターが構造体または参照型として認識されている必要があります。型の注釈の追加を検討してください。
@@ -147,6 +157,11 @@
         既知の型パラメーター: {0}
         
       
+      
+        The constraints 'null' and 'not null' are inconsistent
+        The constraints 'null' and 'not null' are inconsistent
+        
+      
       
         Argument at index {0} doesn't match
         インデックス {0} の引数が一致しません
@@ -157,6 +172,21 @@
         引数 '{0}' が一致しません
         
       
+      
+        The constraints 'struct' and 'null' are inconsistent
+        The constraints 'struct' and 'null' are inconsistent
+        
+      
+      
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        
+      
+      
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        
+      
       
         All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n    {1}    \nThis branch returns a tuple of length {2} of type\n    {3}    \n
         'if' 式のすべての分岐は、最初の分岐の型に暗黙的に変換できる値を返す必要があります。これは、型の長さ {0} のタプルです\n    {1}    \nこの分岐は、型の長さ {2} のタプルを返します\n    {3}    \n
@@ -177,6 +207,11 @@
         アセンブリ属性 '{0}' は、デザイナー アセンブリ '{1}' を参照していますが、これは読み込むことができないか、存在していません。報告された例外: {2} - {3}
         
       
+      
+        {0} for F# {1}
+        {0} for F# {1}
+        
+      
       
         additional type-directed conversions
         その他の型指定された変換
@@ -332,6 +367,11 @@
         Null 許容のオプションの相互運用
         
       
+      
+        nullness checking
+        nullness checking
+        
+      
       
         open type declaration
         オープン型宣言
@@ -647,6 +687,11 @@
         すべてのコンパイル ファイルの推定されたインターフェイスを関連する署名ファイルに印刷します
         
       
+      
+        Enable nullness declarations and checks
+        Enable nullness declarations and checks
+        
+      
       
         Clear the package manager results cache
         パッケージ マネージャーの結果キャッシュをクリアする
@@ -957,6 +1002,11 @@
         属性を型拡張に適用することはできません。
         
       
+      
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        
+      
       
         This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.
         この copy-and-update レコード式は、レコードの種類が '{0}' であるすべてのフィールドを変更します。代わりにレコード構築構文を使用することを検討してください。
@@ -1102,6 +1152,11 @@
         値 '{0}' は関数ではなく、インデックス表記をサポートしていません。
         
       
+      
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        
+      
       
         The syntax 'expr1[expr2]' is ambiguous when used as an argument. See https://aka.ms/fsharp-index-notation. If you intend indexing or slicing then you must use 'expr1.[expr2]' in argument position. If calling a function with multiple curried arguments, add a space between them, e.g. 'someFunction expr1 [expr2]'.
         構文 'expr1[expr2]' は引数として使用されている場合、あいまいです。https://aka.ms/fsharp-index-notation を参照してください。インデックス作成またはスライスを行う場合は、'expr1.[expr2]' を引数の位置に使用する必要があります。複数のカリー化された引数を持つ関数を呼び出す場合は、'expr1 [expr2]' のように間にスペースを追加します。
@@ -1167,6 +1222,11 @@
         この式は、型 '{0}' を型 '{1}' に暗黙的に変換します。https://aka.ms/fsharp-implicit-convs を参照してください。
         
       
+      
+        The type '{0}' does not support a nullness qualitification.
+        The type '{0}' does not support a nullness qualitification.
+        
+      
       
         SynType.Or is not permitted in this declaration
         SynType.Or はこの宣言では許可されていません
diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf
index 7c4f481d2a2..607b67baaed 100644
--- a/src/Compiler/xlf/FSComp.txt.ko.xlf
+++ b/src/Compiler/xlf/FSComp.txt.ko.xlf
@@ -92,6 +92,11 @@
         F# 라이브러리의 'incr' 사용은 더 이상 사용되지 않습니다. https://aka.ms/fsharp-refcell-ops를 참조하세요. 예를 들어 'incr cell'을 'cell.Value <- cell.Value + 1'로 변경하세요.
         
       
+      
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        
+      
       
         If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance let bindings are not allowed.
         형식이 [<Sealed>] 및 [<AbstractClass>] 특성을 모두 사용하는 경우 정적임을 의미합니다. 인스턴스 let 바인딩은 허용되지 않습니다.
@@ -117,6 +122,11 @@
         사용 가능한 오버로드:\n{0}
         
       
+      
+        The constraints 'delegate' and 'comparison' are inconsistent
+        The constraints 'delegate' and 'comparison' are inconsistent
+        
+      
       
         A generic construct requires that a generic type parameter be known as a struct or reference type. Consider adding a type annotation.
         제네릭 구문을 사용하려면 구조체 또는 참조 형식의 제네릭 형식 매개 변수가 필요합니다. 형식 주석을 추가하세요.
@@ -147,6 +157,11 @@
         알려진 형식 매개 변수: {0}
         
       
+      
+        The constraints 'null' and 'not null' are inconsistent
+        The constraints 'null' and 'not null' are inconsistent
+        
+      
       
         Argument at index {0} doesn't match
         인덱스 {0}의 인수가 일치하지 않습니다.
@@ -157,6 +172,21 @@
         '{0}' 인수가 일치하지 않습니다.
         
       
+      
+        The constraints 'struct' and 'null' are inconsistent
+        The constraints 'struct' and 'null' are inconsistent
+        
+      
+      
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        
+      
+      
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        
+      
       
         All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n    {1}    \nThis branch returns a tuple of length {2} of type\n    {3}    \n
         'if' 식의 모든 분기는 첫 번째 분기의 유형으로 암시적으로 변환 가능한 값을 반환해야 합니다. 여기서는 형식이 \n    {1}이고 길이가 {0}인 튜플입니다.    \n이 분기는 형식이 \n    {3}이고 길이가 {2}인 튜플을 반환합니다.    \n
@@ -177,6 +207,11 @@
         '{0}' 어셈블리 특성이 로드할 수 없거나 존재하지 않는 디자이너 어셈블리'{1}'을(를) 참조합니다. 보고된 예외: {2} - {3}
         
       
+      
+        {0} for F# {1}
+        {0} for F# {1}
+        
+      
       
         additional type-directed conversions
         추가 형식-디렉션 변환
@@ -332,6 +367,11 @@
         nullable 선택적 interop
         
       
+      
+        nullness checking
+        nullness checking
+        
+      
       
         open type declaration
         개방형 형식 선언
@@ -647,6 +687,11 @@
         모든 컴파일 파일의 유추된 인터페이스를 관련 서명 파일로 인쇄합니다.
         
       
+      
+        Enable nullness declarations and checks
+        Enable nullness declarations and checks
+        
+      
       
         Clear the package manager results cache
         패키지 관리자 결과 캐시 지우기
@@ -957,6 +1002,11 @@
         형식 확장에 특성을 적용할 수 없습니다.
         
       
+      
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        
+      
       
         This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.
         이 레코드 복사 및 업데이트 식은 '{0}' 레코드 형식의 모든 필드를 변경합니다. 레코드 생성 구문을 대신 사용하는 것이 좋습니다.
@@ -1102,6 +1152,11 @@
         '{0}' 값은 함수가 아니며 인덱스 표기법을 지원하지 않습니다.
         
       
+      
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        
+      
       
         The syntax 'expr1[expr2]' is ambiguous when used as an argument. See https://aka.ms/fsharp-index-notation. If you intend indexing or slicing then you must use 'expr1.[expr2]' in argument position. If calling a function with multiple curried arguments, add a space between them, e.g. 'someFunction expr1 [expr2]'.
         'expr1[expr2]' 구문은 인수로 사용될 때 모호합니다. https://aka.ms/fsharp-index-notation을 참조하세요. 인덱싱이나 슬라이싱을 하려면 인수 위치에 'expr1.[expr2]'를 사용해야 합니다. 여러 개의 커리된 인수로 함수를 호출하는 경우 그 사이에 공백을 추가하세요(예: 'someFunction expr1 [expr2]').
@@ -1167,6 +1222,11 @@
         이 식은 암시적으로 '{0}' 형식을 '{1}' 형식으로 변환 합니다. https://aka.ms/fsharp-implicit-convs 참조
         
       
+      
+        The type '{0}' does not support a nullness qualitification.
+        The type '{0}' does not support a nullness qualitification.
+        
+      
       
         SynType.Or is not permitted in this declaration
         SynType.Or는 이 선언에서 허용되지 않습니다.
diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf
index cea66cde2e9..f33dd980b7d 100644
--- a/src/Compiler/xlf/FSComp.txt.pl.xlf
+++ b/src/Compiler/xlf/FSComp.txt.pl.xlf
@@ -92,6 +92,11 @@
         Użycie elementu „incr” z biblioteki języka F# jest przestarzałe. Sprawdź stronę https://aka.ms/fsharp-refcell-ops. Na przykład zmień wyrażenie „incr cell” na „cell.Value <- cell.Value + 1”.
         
       
+      
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        
+      
       
         If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance let bindings are not allowed.
         Jeśli typ używa obu atrybutów [<Sealed>] i [< AbstractClass>], oznacza to, że jest on statyczny. Połączenia let wystąpienia są niedozwolone.
@@ -117,6 +122,11 @@
         Dostępne przeciążenia:\n{0}
         
       
+      
+        The constraints 'delegate' and 'comparison' are inconsistent
+        The constraints 'delegate' and 'comparison' are inconsistent
+        
+      
       
         A generic construct requires that a generic type parameter be known as a struct or reference type. Consider adding a type annotation.
         Konstrukcja ogólna wymaga, aby parametr typu ogólnego był znany jako struktura lub typ referencyjny. Rozważ dodanie adnotacji typu.
@@ -147,6 +157,11 @@
         Parametr znanego typu: {0}
         
       
+      
+        The constraints 'null' and 'not null' are inconsistent
+        The constraints 'null' and 'not null' are inconsistent
+        
+      
       
         Argument at index {0} doesn't match
         Argument pod indeksem {0} nie jest zgodny
@@ -157,6 +172,21 @@
         Argument „{0}” nie jest zgodny
         
       
+      
+        The constraints 'struct' and 'null' are inconsistent
+        The constraints 'struct' and 'null' are inconsistent
+        
+      
+      
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        
+      
+      
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        
+      
       
         All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n    {1}    \nThis branch returns a tuple of length {2} of type\n    {3}    \n
         Wszystkie gałęzie wyrażenia „if” muszą zwracać wartości niejawnie konwertowalne na typ pierwszej gałęzi, która tutaj jest krotką o długości {0} typu\n    {1}    \nTa gałąź zwraca krotkę o długości {2} typu\n    {3}    \n
@@ -177,6 +207,11 @@
         Atrybut zestawu „{0}” odwołuje się do zestawu projektanta „{1}”, którego nie można załadować lub który nie istnieje. Zgłoszony wyjątek: {2} — {3}
         
       
+      
+        {0} for F# {1}
+        {0} for F# {1}
+        
+      
       
         additional type-directed conversions
         dodatkowe konwersje ukierunkowane na typ
@@ -332,6 +367,11 @@
         opcjonalna międzyoperacyjność dopuszczająca wartość null
         
       
+      
+        nullness checking
+        nullness checking
+        
+      
       
         open type declaration
         deklaracja typu otwartego
@@ -647,6 +687,11 @@
         Drukowanie wywnioskowanych interfejsów wszystkich plików kompilacji do skojarzonych plików sygnatur
         
       
+      
+        Enable nullness declarations and checks
+        Enable nullness declarations and checks
+        
+      
       
         Clear the package manager results cache
         Wyczyść pamięć podręczną wyników menedżera pakietów
@@ -957,6 +1002,11 @@
         Atrybutów nie można stosować do rozszerzeń typu.
         
       
+      
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        
+      
       
         This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.
         To wyrażenie rekordu kopiowania i aktualizacji zmienia wszystkie pola typu rekordu „{0}”. Zamiast tego rozważ użycie składni konstrukcji rekordu.
@@ -1102,6 +1152,11 @@
         Wartość elementu „{0}” nie jest funkcją i nie obsługuje notacji indeksowej.
         
       
+      
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        
+      
       
         The syntax 'expr1[expr2]' is ambiguous when used as an argument. See https://aka.ms/fsharp-index-notation. If you intend indexing or slicing then you must use 'expr1.[expr2]' in argument position. If calling a function with multiple curried arguments, add a space between them, e.g. 'someFunction expr1 [expr2]'.
         Składnia wyrażenia „expr1[expr2]” jest niejednoznaczna, gdy jest używana jako argument. Zobacz https://aka.ms/fsharp-index-notation. Jeśli zamierzasz indeksować lub fragmentować, to w pozycji argumentu musi być użyte wyrażenie „expr1.[expr2]”. Jeśli wywołujesz funkcję z wieloma argumentami typu curried, dodaj spację między nimi, np. „someFunction expr1 [expr2]”.
@@ -1167,6 +1222,11 @@
         To wyrażenie bezwzględnie konwertuje typ "{0}" na typ "{1}". Zobacz https://aka.ms/fsharp-implicit-convs.
         
       
+      
+        The type '{0}' does not support a nullness qualitification.
+        The type '{0}' does not support a nullness qualitification.
+        
+      
       
         SynType.Or is not permitted in this declaration
         Element SynType.Or nie jest dozwolony w tej deklaracji
diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf
index 79c864480fd..f7a8891e84d 100644
--- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf
+++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf
@@ -92,6 +92,11 @@
         O uso de 'incr' da biblioteca F# foi preterido. Consulte https://aka.ms/fsharp-refcell-ops. Por exemplo, altere a célula 'incr cell' para 'cell.Value <- cell.Value + 1'.
         
       
+      
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        
+      
       
         If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance let bindings are not allowed.
         Se um tipo usar os atributos [<Sealed>] e [<AbstractClass>], isso significará que ele é estático. Associações de permissão de instância não são permitidas.
@@ -117,6 +122,11 @@
         Sobrecargas disponíveis:\n{0}
         
       
+      
+        The constraints 'delegate' and 'comparison' are inconsistent
+        The constraints 'delegate' and 'comparison' are inconsistent
+        
+      
       
         A generic construct requires that a generic type parameter be known as a struct or reference type. Consider adding a type annotation.
         Um constructo genérico exige que um parâmetro de tipo genérico seja conhecido como um tipo de referência ou struct. Considere adicionar uma anotação de tipo.
@@ -147,6 +157,11 @@
         Parâmetro de tipo conhecido: {0}
         
       
+      
+        The constraints 'null' and 'not null' are inconsistent
+        The constraints 'null' and 'not null' are inconsistent
+        
+      
       
         Argument at index {0} doesn't match
         O argumento no índice {0} não corresponde
@@ -157,6 +172,21 @@
         O argumento '{0}' não corresponde
         
       
+      
+        The constraints 'struct' and 'null' are inconsistent
+        The constraints 'struct' and 'null' are inconsistent
+        
+      
+      
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        
+      
+      
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        
+      
       
         All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n    {1}    \nThis branch returns a tuple of length {2} of type\n    {3}    \n
         Todas as ramificações de uma expressão 'if' devem retornar valores implicitamente conversíveis ao tipo da primeira ramificação, que aqui é uma tupla de comprimento {0} do tipo\n    {1}    \nEsta ramificação retorna uma tupla de comprimento {2} do tipo\n    {3}    \n
@@ -177,6 +207,11 @@
         O atributo de assembly '{0}' refere-se a um assembly de designer '{1}' que não pode ser carregado ou que não existe. A exceção relatada foi {2} – {3}
         
       
+      
+        {0} for F# {1}
+        {0} for F# {1}
+        
+      
       
         additional type-directed conversions
         conversões direcionadas por tipos adicionais
@@ -332,6 +367,11 @@
         interoperabilidade opcional anulável
         
       
+      
+        nullness checking
+        nullness checking
+        
+      
       
         open type declaration
         declaração de tipo aberto
@@ -647,6 +687,11 @@
         Imprimir as interfaces inferidas de todos os arquivos de compilação para os arquivos de assinatura associados
         
       
+      
+        Enable nullness declarations and checks
+        Enable nullness declarations and checks
+        
+      
       
         Clear the package manager results cache
         Limpar o cache de resultados do gerenciador de pacotes
@@ -957,6 +1002,11 @@
         Os atributos não podem ser aplicados às extensões de tipo.
         
       
+      
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        
+      
       
         This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.
         Essa expressão de registro copiar e atualizar altera todos os campos do tipo de registro '{0}'. Considere usar a sintaxe de construção de registro em vez disso.
@@ -1102,6 +1152,11 @@
         O valor '{0}' não é uma função e não dá suporte à notação de índice.
         
       
+      
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        
+      
       
         The syntax 'expr1[expr2]' is ambiguous when used as an argument. See https://aka.ms/fsharp-index-notation. If you intend indexing or slicing then you must use 'expr1.[expr2]' in argument position. If calling a function with multiple curried arguments, add a space between them, e.g. 'someFunction expr1 [expr2]'.
         A sintaxe '[expr1][expr2]' é ambígua quando usada como um argumento. Consulte https://aka.ms/fsharp-index-notation. Se você pretende indexar ou colocar em fatias, deve usar '(expr1).[expr2]' na posição do argumento. Se chamar uma função com vários argumentos na forma curried, adicione um espaço entre eles, por exemplo, 'someFunction [expr1] [expr2]'.
@@ -1167,6 +1222,11 @@
         Essa expressão converte implicitamente o tipo '{0}' ao tipo '{1}'. Consulte https://aka.ms/fsharp-implicit-convs.
         
       
+      
+        The type '{0}' does not support a nullness qualitification.
+        The type '{0}' does not support a nullness qualitification.
+        
+      
       
         SynType.Or is not permitted in this declaration
         SynType.Or não é permitido nesta declaração
diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf
index 831c7891388..4e75473322b 100644
--- a/src/Compiler/xlf/FSComp.txt.ru.xlf
+++ b/src/Compiler/xlf/FSComp.txt.ru.xlf
@@ -92,6 +92,11 @@
         Использование "incr" из библиотеки F# является нерекомендуемым. См. https://aka.ms/fsharp-refcell-ops. Например, замените "incr cell" на "cell.Value <- cell.Value + 1".
         
       
+      
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        
+      
       
         If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance let bindings are not allowed.
         Если тип использует оба атрибута [<Sealed>] и [<AbstractClass>], это означает, что он статический. Привязки экземпляра let не разрешены.
@@ -117,6 +122,11 @@
         Доступные перегрузки:\n{0}
         
       
+      
+        The constraints 'delegate' and 'comparison' are inconsistent
+        The constraints 'delegate' and 'comparison' are inconsistent
+        
+      
       
         A generic construct requires that a generic type parameter be known as a struct or reference type. Consider adding a type annotation.
         В универсальной конструкции требуется использовать параметр универсального типа, известный как структура или ссылочный тип. Рекомендуется добавить заметку с типом.
@@ -147,6 +157,11 @@
         Известный параметр типа: {0}
         
       
+      
+        The constraints 'null' and 'not null' are inconsistent
+        The constraints 'null' and 'not null' are inconsistent
+        
+      
       
         Argument at index {0} doesn't match
         Аргумент в индексе {0} не соответствует
@@ -157,6 +172,21 @@
         Аргумент "{0}" не соответствует
         
       
+      
+        The constraints 'struct' and 'null' are inconsistent
+        The constraints 'struct' and 'null' are inconsistent
+        
+      
+      
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        
+      
+      
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        
+      
       
         All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n    {1}    \nThis branch returns a tuple of length {2} of type\n    {3}    \n
         Все ветви выражения "if" должны возвращать значения, поддерживающие неявное преобразование в тип первой ветви, которым здесь является кортеж длиной {0} типа\n    {1}    \nЭта ветвь возвращает кортеж длиной {2} типа\n    {3}    \n
@@ -177,6 +207,11 @@
         Атрибут сборки "{0}" ссылается на сборку конструктора "{1}", которая не может быть загружена или не существует. Получено исключение: {2} — {3}
         
       
+      
+        {0} for F# {1}
+        {0} for F# {1}
+        
+      
       
         additional type-directed conversions
         дополнительные преобразования на основе типа
@@ -332,6 +367,11 @@
         необязательное взаимодействие, допускающее значение NULL
         
       
+      
+        nullness checking
+        nullness checking
+        
+      
       
         open type declaration
         объявление открытого типа
@@ -647,6 +687,11 @@
         Печать определяемых интерфейсов всех файлов компиляции в связанные файлы подписей
         
       
+      
+        Enable nullness declarations and checks
+        Enable nullness declarations and checks
+        
+      
       
         Clear the package manager results cache
         Очистка кэша результатов диспетчера пакетов
@@ -957,6 +1002,11 @@
         Атрибуты не могут быть применены к расширениям типа.
         
       
+      
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        
+      
       
         This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.
         Это выражение записи копирования и обновления изменяет все поля типа записи "{0}". Вместо этого можно использовать синтаксис конструкции записи.
@@ -1102,6 +1152,11 @@
         Значение {0} не является функцией и не поддерживает нотацию индекса.
         
       
+      
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        
+      
       
         The syntax 'expr1[expr2]' is ambiguous when used as an argument. See https://aka.ms/fsharp-index-notation. If you intend indexing or slicing then you must use 'expr1.[expr2]' in argument position. If calling a function with multiple curried arguments, add a space between them, e.g. 'someFunction expr1 [expr2]'.
         Синтаксис "expr1[expr2]" неоднозначен при использовании в качестве аргумента. См. https://aka.ms/fsharp-index-notation. Если вы намереваетесь индексировать или разрезать, необходимо использовать "expr1.[expr2]" в позиции аргумента. При вызове функции с несколькими каррированными аргументами добавьте пробел между ними, например "someFunction expr1 [expr2]".
@@ -1167,6 +1222,11 @@
         Это выражение неявно преобразует тип "{0}" в тип "{1}". См. сведения на странице https://aka.ms/fsharp-implicit-convs.
         
       
+      
+        The type '{0}' does not support a nullness qualitification.
+        The type '{0}' does not support a nullness qualitification.
+        
+      
       
         SynType.Or is not permitted in this declaration
         SynType.Or не допускается в этом объявлении
diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf
index 9e455903428..4d8e47af9a0 100644
--- a/src/Compiler/xlf/FSComp.txt.tr.xlf
+++ b/src/Compiler/xlf/FSComp.txt.tr.xlf
@@ -92,6 +92,11 @@
         F# kitaplığından gelen 'incr' kullanımı kullanım dışı. https://aka.ms/fsharp-refcell-ops’a bakın. Örneğin, lütfen 'incr cell' ifadesini 'cell.Value <- cell.Value + 1' olarak değiştirin.
         
       
+      
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        
+      
       
         If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance let bindings are not allowed.
         Bir tür, hem [<Sealed>] hem de [< AbstractClass>] özniteliklerini kullanıyorsa bu statik olduğu anlamına gelir. Örnek let bağlamalarına izin verilmez.
@@ -117,6 +122,11 @@
         Kullanılabilir aşırı yüklemeler:\n{0}
         
       
+      
+        The constraints 'delegate' and 'comparison' are inconsistent
+        The constraints 'delegate' and 'comparison' are inconsistent
+        
+      
       
         A generic construct requires that a generic type parameter be known as a struct or reference type. Consider adding a type annotation.
         Genel yapı, genel bir tür parametresinin yapı veya başvuru türü olarak bilinmesini gerektirir. Tür ek açıklaması eklemeyi düşünün.
@@ -147,6 +157,11 @@
         Bilinen tür parametresi: {0}
         
       
+      
+        The constraints 'null' and 'not null' are inconsistent
+        The constraints 'null' and 'not null' are inconsistent
+        
+      
       
         Argument at index {0} doesn't match
         {0} dizinindeki bağımsız değişken eşleşmiyor
@@ -157,6 +172,21 @@
         '{0}' bağımsız değişkeni eşleşmiyor
         
       
+      
+        The constraints 'struct' and 'null' are inconsistent
+        The constraints 'struct' and 'null' are inconsistent
+        
+      
+      
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        
+      
+      
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        
+      
       
         All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n    {1}    \nThis branch returns a tuple of length {2} of type\n    {3}    \n
         Bir 'if' ifadesinin tüm dalları, örtük olarak ilk dalın türüne dönüştürülebilir değerler döndürmelidir. Burada ilk dal {0} uzunluğunda türü\n    {1} olan bir demet    \nBu dal {2} uzunluğunda türü\n    {3}    \nolan bir demet döndürüyor
@@ -177,6 +207,11 @@
         '{0}' bütünleştirilmiş kod özniteliği, yüklenemeyen veya mevcut olmayan '{1}' tasarımcı bütünleştirilmiş koduna başvuruyor. Bildirilen özel durum: {2} - {3}
         
       
+      
+        {0} for F# {1}
+        {0} for F# {1}
+        
+      
       
         additional type-directed conversions
         ek tür ile yönlendirilen dönüştürmeler
@@ -332,6 +367,11 @@
         null atanabilir isteğe bağlı birlikte çalışma
         
       
+      
+        nullness checking
+        nullness checking
+        
+      
       
         open type declaration
         açık tür bildirimi
@@ -647,6 +687,11 @@
         Tüm derleme dosyalarının çıkarsanan arabirimlerini ilişkili imza dosyalarına yazdır
         
       
+      
+        Enable nullness declarations and checks
+        Enable nullness declarations and checks
+        
+      
       
         Clear the package manager results cache
         Paket yöneticisi sonuçları önbelleğini temizle
@@ -957,6 +1002,11 @@
         Öznitelikler tür uzantılarına uygulanamaz.
         
       
+      
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        
+      
       
         This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.
         Bu kopyalama ve güncelleştirme kayıt ifadesi, '{0}' kayıt türündeki tüm alanları değiştirir. Bunun yerine kayıt oluşturma söz dizimini kullanmayı deneyin.
@@ -1102,6 +1152,11 @@
         “{0}” değeri bir işlev değildir ve dizin gösterimini desteklemez.
         
       
+      
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        
+      
       
         The syntax 'expr1[expr2]' is ambiguous when used as an argument. See https://aka.ms/fsharp-index-notation. If you intend indexing or slicing then you must use 'expr1.[expr2]' in argument position. If calling a function with multiple curried arguments, add a space between them, e.g. 'someFunction expr1 [expr2]'.
         Söz dizimi “expr1[expr2]” artık dizin oluşturma için ayrılmıştır ve bağımsız değişken olarak kullanıldığında belirsizdir. https://aka.ms/fsharp-index-notation'a bakın. Dizin oluşturmayı veya dilimlemeyi düşünüyorsanız, bağımsız değişken konumunda “expr1.[expr2]” kullanmalısınız. Birden çok curry bağımsız değişkenli bir işlev çağırıyorsanız, aralarına bir boşluk ekleyin, örn. “someFunction expr1 [expr2]”.
@@ -1167,6 +1222,11 @@
         Bu ifade '{0}' türünü örtülü olarak '{1}' türüne dönüştürür. https://aka.ms/fsharp-implicit-convs adresine bakın.
         
       
+      
+        The type '{0}' does not support a nullness qualitification.
+        The type '{0}' does not support a nullness qualitification.
+        
+      
       
         SynType.Or is not permitted in this declaration
         Bu bildirimde SynType.Or'a izin verilmiyor
diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
index 8338b0da896..6a537df75ee 100644
--- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
+++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
@@ -92,6 +92,11 @@
         已弃用 F# 库中的“incr”。请参阅 https://aka.ms/fsharp-refcell-ops。 例如,请将“incr cell”更改为“cell.Value <- cell.Value + 1”。
         
       
+      
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        
+      
       
         If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance let bindings are not allowed.
         如果类型同时使用 [<Sealed>] 和 [<AbstractClass>] 属性,则表示它是静态的。不允许使用实例允许绑定。
@@ -117,6 +122,11 @@
         可用重载:\n{0}
         
       
+      
+        The constraints 'delegate' and 'comparison' are inconsistent
+        The constraints 'delegate' and 'comparison' are inconsistent
+        
+      
       
         A generic construct requires that a generic type parameter be known as a struct or reference type. Consider adding a type annotation.
         泛型构造要求泛型类型参数被视为结构或引用类型。请考虑添加类型注释。
@@ -147,6 +157,11 @@
         已知类型参数: {0}
         
       
+      
+        The constraints 'null' and 'not null' are inconsistent
+        The constraints 'null' and 'not null' are inconsistent
+        
+      
       
         Argument at index {0} doesn't match
         索引 {0} 处的参数不匹配
@@ -157,6 +172,21 @@
         参数 "{0}" 不匹配
         
       
+      
+        The constraints 'struct' and 'null' are inconsistent
+        The constraints 'struct' and 'null' are inconsistent
+        
+      
+      
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        
+      
+      
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        
+      
       
         All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n    {1}    \nThis branch returns a tuple of length {2} of type\n    {3}    \n
         “if” 表达式的所有分支必须返回可隐式转换为第一个分支类型的值,这是一个长度为 {0} 的类型的元组\n    {1}    \n此分支会返回长度为 {2} 的类型的元组\n    {3}    \n
@@ -177,6 +207,11 @@
         程序集属性“{0}”引用了无法加载或不存在的设计器程序集“{1}”。报告的异常是: {2} - {3}
         
       
+      
+        {0} for F# {1}
+        {0} for F# {1}
+        
+      
       
         additional type-directed conversions
         附加类型定向转换
@@ -332,6 +367,11 @@
         可以为 null 的可选互操作
         
       
+      
+        nullness checking
+        nullness checking
+        
+      
       
         open type declaration
         开放类型声明
@@ -647,6 +687,11 @@
         将所有编译文件的推断接口打印到关联的签名文件
         
       
+      
+        Enable nullness declarations and checks
+        Enable nullness declarations and checks
+        
+      
       
         Clear the package manager results cache
         清除包管理器结果缓存
@@ -957,6 +1002,11 @@
         属性不可应用于类型扩展。
         
       
+      
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        
+      
       
         This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.
         此复制和更新记录表达式更改记录类型“{0}”的所有字段。请考虑改用记录构造语法。
@@ -1102,6 +1152,11 @@
         值 '{0}' 不是函数,不支持索引表示法。
         
       
+      
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        
+      
       
         The syntax 'expr1[expr2]' is ambiguous when used as an argument. See https://aka.ms/fsharp-index-notation. If you intend indexing or slicing then you must use 'expr1.[expr2]' in argument position. If calling a function with multiple curried arguments, add a space between them, e.g. 'someFunction expr1 [expr2]'.
         语法“expr1[expr2]”用作参数时不明确。请参阅 https://aka.ms/fsharp-index-notation。如果要索引或切片,则必须在参数位置使用“expr1.[expr2]”。如果使用多个扩充参数调用函数,请在它们之间添加空格,例如“someFunction expr1 [expr2]”。
@@ -1167,6 +1222,11 @@
         此表达式将类型“{0}”隐式转换为类型“{1}”。请参阅 https://aka.ms/fsharp-implicit-convs。
         
       
+      
+        The type '{0}' does not support a nullness qualitification.
+        The type '{0}' does not support a nullness qualitification.
+        
+      
       
         SynType.Or is not permitted in this declaration
         此声明中不允许使用 SynType.Or
diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
index 5fbdf0f332c..7fd1a1098fe 100644
--- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
+++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
@@ -92,6 +92,11 @@
         透過 F# 程式庫使用 'incr' 的方式已淘汰。請參閱 https://aka.ms/fsharp-refcell-ops。舉例來說,請將 'incr cell' 變更為 'cell.Value <- cell.Value + 1'。
         
       
+      
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        Nullness warning. The 'DefaultValue' attribute is used but the type (or one of its fields if a struct) is non-nullable.
+        
+      
       
         If a type uses both [<Sealed>] and [<AbstractClass>] attributes, it means it is static. Instance let bindings are not allowed.
         如果類型同時使用 [<Sealed>] 和 [<AbstractClass>] 屬性,表示其為靜態。不允許執行個體 'let' 繫結。
@@ -117,6 +122,11 @@
         可用的多載:\n{0}
         
       
+      
+        The constraints 'delegate' and 'comparison' are inconsistent
+        The constraints 'delegate' and 'comparison' are inconsistent
+        
+      
       
         A generic construct requires that a generic type parameter be known as a struct or reference type. Consider adding a type annotation.
         泛型建構要求泛型型別參數必須指定為結構或參考型別。請考慮新增型別註解。
@@ -147,6 +157,11 @@
         已知的型別參數: {0}
         
       
+      
+        The constraints 'null' and 'not null' are inconsistent
+        The constraints 'null' and 'not null' are inconsistent
+        
+      
       
         Argument at index {0} doesn't match
         位於索引 {0} 的引數不相符
@@ -157,6 +172,21 @@
         引數 '{0}' 不相符
         
       
+      
+        The constraints 'struct' and 'null' are inconsistent
+        The constraints 'struct' and 'null' are inconsistent
+        
+      
+      
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        The type '{0}' has 'null' as an extra value but a constraint does not permit this
+        
+      
+      
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        The type '{0}' has 'null' as a true representation value but a constraint does not permit this
+        
+      
       
         All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is a tuple of length {0} of type\n    {1}    \nThis branch returns a tuple of length {2} of type\n    {3}    \n
         'if' 運算式的所有分支都傳回可隱含轉換為第一個分支的類型的值,這是類型為\n    {1}    \n的元組長度 {0}此分支傳回的是類型為\n    {3}    \n的元組長度 {2}
@@ -177,6 +207,11 @@
         無法載入組件屬性 '{0}' 參考的設計工具組件 '{1}' 或其不存在。回報的例外狀況: {2} - {3}
         
       
+      
+        {0} for F# {1}
+        {0} for F# {1}
+        
+      
       
         additional type-directed conversions
         其他類型導向轉換
@@ -332,6 +367,11 @@
         可為 Null 的選擇性 Interop
         
       
+      
+        nullness checking
+        nullness checking
+        
+      
       
         open type declaration
         開放式類型宣告
@@ -647,6 +687,11 @@
         將所有編譯檔案的推斷介面列印至相關聯的簽章檔案
         
       
+      
+        Enable nullness declarations and checks
+        Enable nullness declarations and checks
+        
+      
       
         Clear the package manager results cache
         清除封裝管理員結果快取
@@ -957,6 +1002,11 @@
         屬性無法套用到類型延伸模組。
         
       
+      
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        Nullness warning. The default constructor of a struct type is required but one of the fields of struct type is non-nullable.
+        
+      
       
         This copy-and-update record expression changes all fields of record type '{0}'. Consider using the record construction syntax instead.
         此複製和更新記錄運算式將變更記錄類型為 '{0}' 的所有欄位。請考慮改用記錄建構語法。
@@ -1102,6 +1152,11 @@
         值 '{0}' 並非函式,不支援索引標記法。
         
       
+      
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored.
+        
+      
       
         The syntax 'expr1[expr2]' is ambiguous when used as an argument. See https://aka.ms/fsharp-index-notation. If you intend indexing or slicing then you must use 'expr1.[expr2]' in argument position. If calling a function with multiple curried arguments, add a space between them, e.g. 'someFunction expr1 [expr2]'.
         語法 'expr1[expr2]' 用作引數時不明確。請參閱 https://aka.ms/fsharp-index-notation。如果您要編製索引或切割,則必須在引數位置使用 'expr1.[expr2]'。如果要呼叫具有多個調用引數的函式,請在它們之間新增空格,例如 'someFunction expr1 [expr2]'。
@@ -1167,6 +1222,11 @@
         此運算式將類型 '{0}' 隱含轉換為類型 '{1}'。請參閱 https://aka.ms/fsharp-implicit-convs。
         
       
+      
+        The type '{0}' does not support a nullness qualitification.
+        The type '{0}' does not support a nullness qualitification.
+        
+      
       
         SynType.Or is not permitted in this declaration
         此宣告中不允許 SynType.Or
diff --git a/src/Compiler/xlf/FSStrings.cs.xlf b/src/Compiler/xlf/FSStrings.cs.xlf
index 7ebb3c64f7c..3b399e8d46e 100644
--- a/src/Compiler/xlf/FSStrings.cs.xlf
+++ b/src/Compiler/xlf/FSStrings.cs.xlf
@@ -2,6 +2,26 @@
 
   
     
+      
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        
+      
+      
+        Nullness warning: The type '{0}' does not support nullness.
+        Nullness warning: The type '{0}' does not support nullness.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        
+      
       
         Type mismatch. Expecting a tuple of length {0} of type\n    {1}    \nbut given a tuple of length {2} of type\n    {3}    {4}\n
         Neshoda typů Očekává se řazená kolekce členů o délce {0} typu\n    {1}    \nale odevzdala se řazená kolekce členů o délce {2} typu\n    {3}{4}\n
@@ -42,6 +62,16 @@
         interpolovaný řetězec (část)
         
       
+      
+        keyword '__notnull'
+        keyword '__notnull'
+        
+      
+      
+        keyword '__withnull'
+        keyword '__withnull'
+        
+      
       
         . See also {0}.
         . Viz taky {0}.
diff --git a/src/Compiler/xlf/FSStrings.de.xlf b/src/Compiler/xlf/FSStrings.de.xlf
index 69473239ef4..bd68a3675e3 100644
--- a/src/Compiler/xlf/FSStrings.de.xlf
+++ b/src/Compiler/xlf/FSStrings.de.xlf
@@ -2,6 +2,26 @@
 
   
     
+      
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        
+      
+      
+        Nullness warning: The type '{0}' does not support nullness.
+        Nullness warning: The type '{0}' does not support nullness.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        
+      
       
         Type mismatch. Expecting a tuple of length {0} of type\n    {1}    \nbut given a tuple of length {2} of type\n    {3}    {4}\n
         Typenkonflikt. Es wurde ein Tupel der Länge {0} des Typs\n    {1}    \nerwartet, aber ein Tupel der Länge {2} des Typs\n    {3}{4}\n angegeben.
@@ -42,6 +62,16 @@
         Interpolierte Zeichenfolge (Teil)
         
       
+      
+        keyword '__notnull'
+        keyword '__notnull'
+        
+      
+      
+        keyword '__withnull'
+        keyword '__withnull'
+        
+      
       
         . See also {0}.
         . Siehe auch "{0}".
diff --git a/src/Compiler/xlf/FSStrings.es.xlf b/src/Compiler/xlf/FSStrings.es.xlf
index 2b06bc30aa3..2470385a990 100644
--- a/src/Compiler/xlf/FSStrings.es.xlf
+++ b/src/Compiler/xlf/FSStrings.es.xlf
@@ -2,6 +2,26 @@
 
   
     
+      
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        
+      
+      
+        Nullness warning: The type '{0}' does not support nullness.
+        Nullness warning: The type '{0}' does not support nullness.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        
+      
       
         Type mismatch. Expecting a tuple of length {0} of type\n    {1}    \nbut given a tuple of length {2} of type\n    {3}    {4}\n
         Error de coincidencia de tipos. Se espera una tupla de longitud {0} de tipo\n    {1}    \nperero se ha proporcionado una tupla de longitud {2} de tipo\n    {3}{4}\n
@@ -42,6 +62,16 @@
         cadena interpolada (parte)
         
       
+      
+        keyword '__notnull'
+        keyword '__notnull'
+        
+      
+      
+        keyword '__withnull'
+        keyword '__withnull'
+        
+      
       
         . See also {0}.
         . Vea también {0}.
diff --git a/src/Compiler/xlf/FSStrings.fr.xlf b/src/Compiler/xlf/FSStrings.fr.xlf
index cce4c1059b0..ddc17e5c270 100644
--- a/src/Compiler/xlf/FSStrings.fr.xlf
+++ b/src/Compiler/xlf/FSStrings.fr.xlf
@@ -2,6 +2,26 @@
 
   
     
+      
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        
+      
+      
+        Nullness warning: The type '{0}' does not support nullness.
+        Nullness warning: The type '{0}' does not support nullness.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        
+      
       
         Type mismatch. Expecting a tuple of length {0} of type\n    {1}    \nbut given a tuple of length {2} of type\n    {3}    {4}\n
         Incompatibilité de type. Tuple de longueur attendu {0} de type\n {1} \nmais tuple de longueur {2} de type\n    {3}{4}\n
@@ -42,6 +62,16 @@
         chaîne interpolée (partie)
         
       
+      
+        keyword '__notnull'
+        keyword '__notnull'
+        
+      
+      
+        keyword '__withnull'
+        keyword '__withnull'
+        
+      
       
         . See also {0}.
         . Voir aussi {0}.
diff --git a/src/Compiler/xlf/FSStrings.it.xlf b/src/Compiler/xlf/FSStrings.it.xlf
index f2f77ac5fee..e4845cfd12b 100644
--- a/src/Compiler/xlf/FSStrings.it.xlf
+++ b/src/Compiler/xlf/FSStrings.it.xlf
@@ -2,6 +2,26 @@
 
   
     
+      
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        
+      
+      
+        Nullness warning: The type '{0}' does not support nullness.
+        Nullness warning: The type '{0}' does not support nullness.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        
+      
       
         Type mismatch. Expecting a tuple of length {0} of type\n    {1}    \nbut given a tuple of length {2} of type\n    {3}    {4}\n
         Tipo non corrispondente. È prevista una tupla di lunghezza {0} di tipo\n   {1}    \n, ma è stata specificata una tupla di lunghezza {2} di tipo\n    {3}{4}\n
@@ -42,6 +62,16 @@
         stringa interpolata (parte)
         
       
+      
+        keyword '__notnull'
+        keyword '__notnull'
+        
+      
+      
+        keyword '__withnull'
+        keyword '__withnull'
+        
+      
       
         . See also {0}.
         . Vedere anche {0}.
diff --git a/src/Compiler/xlf/FSStrings.ja.xlf b/src/Compiler/xlf/FSStrings.ja.xlf
index fec45d4eb05..5f49b8eb56b 100644
--- a/src/Compiler/xlf/FSStrings.ja.xlf
+++ b/src/Compiler/xlf/FSStrings.ja.xlf
@@ -2,6 +2,26 @@
 
   
     
+      
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        
+      
+      
+        Nullness warning: The type '{0}' does not support nullness.
+        Nullness warning: The type '{0}' does not support nullness.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        
+      
       
         Type mismatch. Expecting a tuple of length {0} of type\n    {1}    \nbut given a tuple of length {2} of type\n    {3}    {4}\n
         型が一致しません。型の長さ {0} のタプルが必要です\n    {1}    \nただし、型の長さ {2} のタプルが指定された場合\n    {3}{4}\n
@@ -42,6 +62,16 @@
         補間された文字列 (部分)
         
       
+      
+        keyword '__notnull'
+        keyword '__notnull'
+        
+      
+      
+        keyword '__withnull'
+        keyword '__withnull'
+        
+      
       
         . See also {0}.
         。{0} も参照してください。
diff --git a/src/Compiler/xlf/FSStrings.ko.xlf b/src/Compiler/xlf/FSStrings.ko.xlf
index c16c466ebc4..54a71c5fe12 100644
--- a/src/Compiler/xlf/FSStrings.ko.xlf
+++ b/src/Compiler/xlf/FSStrings.ko.xlf
@@ -2,6 +2,26 @@
 
   
     
+      
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        
+      
+      
+        Nullness warning: The type '{0}' does not support nullness.
+        Nullness warning: The type '{0}' does not support nullness.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        
+      
       
         Type mismatch. Expecting a tuple of length {0} of type\n    {1}    \nbut given a tuple of length {2} of type\n    {3}    {4}\n
         유형 불일치. 형식이 \n    {1}이고 길이가 {0}인 튜플이 필요합니다.    \n그러나 형식이 \n    {3}이고 길이가 {2}인 튜플이 제공되었습니다.{4}\n
@@ -42,6 +62,16 @@
         보간 문자열(부분)
         
       
+      
+        keyword '__notnull'
+        keyword '__notnull'
+        
+      
+      
+        keyword '__withnull'
+        keyword '__withnull'
+        
+      
       
         . See also {0}.
         {0}도 참조하세요.
diff --git a/src/Compiler/xlf/FSStrings.pl.xlf b/src/Compiler/xlf/FSStrings.pl.xlf
index 385be4e2a2a..6845eae60ce 100644
--- a/src/Compiler/xlf/FSStrings.pl.xlf
+++ b/src/Compiler/xlf/FSStrings.pl.xlf
@@ -2,6 +2,26 @@
 
   
     
+      
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        
+      
+      
+        Nullness warning: The type '{0}' does not support nullness.
+        Nullness warning: The type '{0}' does not support nullness.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        
+      
       
         Type mismatch. Expecting a tuple of length {0} of type\n    {1}    \nbut given a tuple of length {2} of type\n    {3}    {4}\n
         Niezgodność. Oczekiwano krotki o długości {0} typu\n    {1}    \nale otrzymano krotkę o długości {2} typu\n    {3}{4}\n
@@ -42,6 +62,16 @@
         ciąg interpolowany (część)
         
       
+      
+        keyword '__notnull'
+        keyword '__notnull'
+        
+      
+      
+        keyword '__withnull'
+        keyword '__withnull'
+        
+      
       
         . See also {0}.
         . Zobacz też {0}.
diff --git a/src/Compiler/xlf/FSStrings.pt-BR.xlf b/src/Compiler/xlf/FSStrings.pt-BR.xlf
index 8fa1e1ee33d..f31a0882f77 100644
--- a/src/Compiler/xlf/FSStrings.pt-BR.xlf
+++ b/src/Compiler/xlf/FSStrings.pt-BR.xlf
@@ -2,6 +2,26 @@
 
   
     
+      
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        
+      
+      
+        Nullness warning: The type '{0}' does not support nullness.
+        Nullness warning: The type '{0}' does not support nullness.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        
+      
       
         Type mismatch. Expecting a tuple of length {0} of type\n    {1}    \nbut given a tuple of length {2} of type\n    {3}    {4}\n
         Tipo incompatível. Esperando uma tupla de comprimento {0} do tipo\n    {1}    \nmas recebeu uma tupla de comprimento {2} do tipo\n    {3}{4}\n
@@ -42,6 +62,16 @@
         cadeia de caracteres interpolada (parte)
         
       
+      
+        keyword '__notnull'
+        keyword '__notnull'
+        
+      
+      
+        keyword '__withnull'
+        keyword '__withnull'
+        
+      
       
         . See also {0}.
         . Consulte também {0}.
diff --git a/src/Compiler/xlf/FSStrings.ru.xlf b/src/Compiler/xlf/FSStrings.ru.xlf
index 27b87f0c6b0..54fb0c052fd 100644
--- a/src/Compiler/xlf/FSStrings.ru.xlf
+++ b/src/Compiler/xlf/FSStrings.ru.xlf
@@ -2,6 +2,26 @@
 
   
     
+      
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        
+      
+      
+        Nullness warning: The type '{0}' does not support nullness.
+        Nullness warning: The type '{0}' does not support nullness.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        
+      
       
         Type mismatch. Expecting a tuple of length {0} of type\n    {1}    \nbut given a tuple of length {2} of type\n    {3}    {4}\n
         Несоответствие типов. Ожидается кортеж длиной {0} типа\n    {1},    \nно предоставлен кортеж длиной {2} типа\n    {3}{4}\n
@@ -42,6 +62,16 @@
         интерполированная строка (часть)
         
       
+      
+        keyword '__notnull'
+        keyword '__notnull'
+        
+      
+      
+        keyword '__withnull'
+        keyword '__withnull'
+        
+      
       
         . See also {0}.
         . См. также {0}.
diff --git a/src/Compiler/xlf/FSStrings.tr.xlf b/src/Compiler/xlf/FSStrings.tr.xlf
index fe1b018c46e..43d48a8ea73 100644
--- a/src/Compiler/xlf/FSStrings.tr.xlf
+++ b/src/Compiler/xlf/FSStrings.tr.xlf
@@ -2,6 +2,26 @@
 
   
     
+      
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        
+      
+      
+        Nullness warning: The type '{0}' does not support nullness.
+        Nullness warning: The type '{0}' does not support nullness.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        
+      
       
         Type mismatch. Expecting a tuple of length {0} of type\n    {1}    \nbut given a tuple of length {2} of type\n    {3}    {4}\n
         Tür uyuşmazlığı. {0} uzunluğunda türü\n {1} \nolan bir demet bekleniyordu ancak {2} uzunluğunda türü\n    {3}{4}\nolan bir demet verildi
@@ -42,6 +62,16 @@
         düz metin arasına kod eklenmiş dize (parça)
         
       
+      
+        keyword '__notnull'
+        keyword '__notnull'
+        
+      
+      
+        keyword '__withnull'
+        keyword '__withnull'
+        
+      
       
         . See also {0}.
         . Ayrıca bkz. {0}.
diff --git a/src/Compiler/xlf/FSStrings.zh-Hans.xlf b/src/Compiler/xlf/FSStrings.zh-Hans.xlf
index 89cac4d4d7a..4bd02158e64 100644
--- a/src/Compiler/xlf/FSStrings.zh-Hans.xlf
+++ b/src/Compiler/xlf/FSStrings.zh-Hans.xlf
@@ -2,6 +2,26 @@
 
   
     
+      
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        
+      
+      
+        Nullness warning: The type '{0}' does not support nullness.
+        Nullness warning: The type '{0}' does not support nullness.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        
+      
       
         Type mismatch. Expecting a tuple of length {0} of type\n    {1}    \nbut given a tuple of length {2} of type\n    {3}    {4}\n
         类型不匹配。应为长度为 {0} 的类型的元组\n    {1}    \n但提供了长度为 {2} 的类型的元组\n    {3}{4}\n
@@ -42,6 +62,16 @@
         内插字符串(部分)
         
       
+      
+        keyword '__notnull'
+        keyword '__notnull'
+        
+      
+      
+        keyword '__withnull'
+        keyword '__withnull'
+        
+      
       
         . See also {0}.
         。请参见 {0}。
diff --git a/src/Compiler/xlf/FSStrings.zh-Hant.xlf b/src/Compiler/xlf/FSStrings.zh-Hant.xlf
index 4e60f07d0a8..ba3f25a9471 100644
--- a/src/Compiler/xlf/FSStrings.zh-Hant.xlf
+++ b/src/Compiler/xlf/FSStrings.zh-Hant.xlf
@@ -2,6 +2,26 @@
 
   
     
+      
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        Nullness warning: The type '{0}' supports a null value but a constraint requires the type to be non-null.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have equivalent nullability '{2}' and '{3}'.
+        
+      
+      
+        Nullness warning: The type '{0}' does not support nullness.
+        Nullness warning: The type '{0}' does not support nullness.
+        
+      
+      
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        Nullness warning: The types '{0}' and '{1}' do not have compatible nullability '{2}' and '{3}'.
+        
+      
       
         Type mismatch. Expecting a tuple of length {0} of type\n    {1}    \nbut given a tuple of length {2} of type\n    {3}    {4}\n
         類型不符。必須是類型為\n    {1}    \n 的元組長度 {0},但提供的是類型為\n    {3}{4}\n 的元組長度 {2}
@@ -42,6 +62,16 @@
         插補字串 (部分)
         
       
+      
+        keyword '__notnull'
+        keyword '__notnull'
+        
+      
+      
+        keyword '__withnull'
+        keyword '__withnull'
+        
+      
       
         . See also {0}.
         。請參閱 {0}。
diff --git a/src/FSharp.Build/FSharp.Build.fsproj b/src/FSharp.Build/FSharp.Build.fsproj
index ff97e84dad5..ea0465cc612 100644
--- a/src/FSharp.Build/FSharp.Build.fsproj
+++ b/src/FSharp.Build/FSharp.Build.fsproj
@@ -9,8 +9,9 @@
     FSharp.Build
     $(NoWarn);75 
     true
+    true
     $(DefineConstants);LOCALIZATION_FSBUILD
-    NU1701;FS0075
+    $(NoWarn);NU1701;FS0075
     true
     7.0  
     Debug;Release;Proto  
diff --git a/src/FSharp.Build/FSharpCommandLineBuilder.fs b/src/FSharp.Build/FSharpCommandLineBuilder.fs
index bf86d185edc..ae783fbcb80 100644
--- a/src/FSharp.Build/FSharpCommandLineBuilder.fs
+++ b/src/FSharp.Build/FSharpCommandLineBuilder.fs
@@ -15,7 +15,7 @@ do ()
 // Shim to match nullness checking library support in preview
 []
 module Utils =
-
+#if NO_CHECKNULLS
     /// Match on the nullness of an argument.
     let inline (|Null|NonNull|) (x: 'T) : Choice =
         match x with
@@ -24,7 +24,12 @@ module Utils =
 
     /// Indicates that a type may be null. 'MaybeNull' used internally in the F# compiler as unchecked
     /// replacement for 'string?' for example for future FS-1060.
-    type 'T MaybeNull when 'T: null and 'T: not struct = 'T
+    type MaybeNull<'T when 'T : null> = 'T
+#else
+    /// Indicates that a type may be null. 'MaybeNull' used internally in the F# compiler as unchecked
+    /// replacement for 'string?' for example for future FS-1060.
+    type MaybeNull<'T when 'T : __notnull and 'T : not struct> = 'T __withnull
+#endif
 
 type FSharpCommandLineBuilder() =
 
diff --git a/src/FSharp.Build/Fsc.fs b/src/FSharp.Build/Fsc.fs
index af7e16d13cc..5c44fdb8991 100644
--- a/src/FSharp.Build/Fsc.fs
+++ b/src/FSharp.Build/Fsc.fs
@@ -223,16 +223,13 @@ type public Fsc() as this =
         // checksumAlgorithm
         builder.AppendSwitchIfNotNull(
             "--checksumalgorithm:",
-            let ToUpperInvariant (s: string) =
-                if s = null then
-                    null
-                else
-                    s.ToUpperInvariant()
-
-            match ToUpperInvariant(checksumAlgorithm) with
-            | "SHA1" -> "Sha1"
-            | "SHA256" -> "Sha256"
-            | _ -> null
+            match checksumAlgorithm with
+            | Null -> null
+            | NonNull checksumAlgorithm ->
+                match checksumAlgorithm.ToUpperInvariant() with
+                | "SHA1" -> "Sha1"
+                | "SHA256" -> "Sha256"
+                | _ -> null
         )
 
         // Resources
diff --git a/src/FSharp.Build/SubstituteText.fs b/src/FSharp.Build/SubstituteText.fs
index 2e4950a62b3..97e8758ce55 100644
--- a/src/FSharp.Build/SubstituteText.fs
+++ b/src/FSharp.Build/SubstituteText.fs
@@ -24,7 +24,7 @@ type SubstituteText() =
     override _.Execute() =
         copiedFiles.Clear()
 
-        if not (isNull embeddedResources) then
+        if not (isNull (box embeddedResources)) then // this check can't fail, the type is non-nullable
             for item in embeddedResources do
                 // Update ITaskItem metadata to point to new location
                 let sourcePath = item.GetMetadata("FullPath")
diff --git a/src/FSharp.Core/FSharp.Core.fsproj b/src/FSharp.Core/FSharp.Core.fsproj
index 396618bb65d..54736d35ae9 100644
--- a/src/FSharp.Core/FSharp.Core.fsproj
+++ b/src/FSharp.Core/FSharp.Core.fsproj
@@ -28,7 +28,6 @@
     
     true
     true
-
     true
     FSharp.Core
     $(FSCorePackageVersion)
diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs
index c3f9d35cdcb..d675f714ad9 100644
--- a/src/FSharp.Core/array.fs
+++ b/src/FSharp.Core/array.fs
@@ -831,12 +831,21 @@ module Array =
 
             count
 
+#if BUILDING_WITH_LKG || NO_NULLCHECKING_LIB_SUPPORT
         let private createMask<'a>
             (f: 'a -> bool)
             (src: 'a array)
             (maskArrayOut: byref)
             (leftoverMaskOut: byref)
             =
+#else
+        let private createMask<'a>
+            (f: 'a -> bool)
+            (src: array<'a>)
+            (maskArrayOut: byref __withnull>)
+            (leftoverMaskOut: byref)
+            =
+#endif
             let maskArrayLength = src.Length / 0x20
 
             // null when there are less than 32 items in src array.
@@ -1026,7 +1035,11 @@ module Array =
 
             dstIdx
 
+#if BUILDING_WITH_LKG || NO_NULLCHECKING_LIB_SUPPORT
         let private filterViaMask (maskArray: uint32 array) (leftoverMask: uint32) (count: int) (src: _ array) =
+#else
+        let private filterViaMask (maskArray: uint32 array __withnull) (leftoverMask: uint32) (count: int) (src: _ array) =
+#endif
             let dst = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count
 
             let mutable dstIdx = 0
diff --git a/src/FSharp.Core/async.fs b/src/FSharp.Core/async.fs
index 2cfed9bc670..ce32e7ea3aa 100644
--- a/src/FSharp.Core/async.fs
+++ b/src/FSharp.Core/async.fs
@@ -868,7 +868,11 @@ module AsyncPrimitives =
 
     ///   - Initial cancellation check
     ///   - Call syncCtxt.Post with exception protection. THis may fail as it is arbitrary user code
+#if BUILDING_WITH_LKG || NO_NULLCHECKING_LIB_SUPPORT
     let CreateSwitchToAsync (syncCtxt: SynchronizationContext) =
+#else
+    let CreateSwitchToAsync (syncCtxt: SynchronizationContext __withnull) =
+#endif
         MakeAsyncWithCancelCheck(fun ctxt -> ctxt.PostWithTrampoline syncCtxt ctxt.cont)
 
     ///   - Initial cancellation check
diff --git a/src/FSharp.Core/local.fs b/src/FSharp.Core/local.fs
index 6a3b44f5475..effc8e43426 100644
--- a/src/FSharp.Core/local.fs
+++ b/src/FSharp.Core/local.fs
@@ -1086,7 +1086,11 @@ module internal Array =
         if array.Length > 1 then 
             Array.Sort<_>(array, fastComparerForArraySort())
 
+#if BUILDING_WITH_LKG || NO_NULLCHECKING_LIB_SUPPORT
     let stableSortWithKeysAndComparer (cFast:IComparer<'Key>) (c:IComparer<'Key>) (array:'T array) (keys: 'Key array)  =
+#else
+    let stableSortWithKeysAndComparer (cFast:IComparer<'Key> __withnull) (c:IComparer<'Key>) (array:array<'T>) (keys:array<'Key>)  =
+#endif
         // 'places' is an array or integers storing the permutation performed by the sort        
         let len = array.Length
         let places = zeroCreateUnchecked len
diff --git a/src/FSharp.Core/option.fs b/src/FSharp.Core/option.fs
index 46da0ec13a8..f872738b6e2 100644
--- a/src/FSharp.Core/option.fs
+++ b/src/FSharp.Core/option.fs
@@ -152,6 +152,7 @@ module Option =
         else
             None
 
+#if BUILDING_WITH_LKG || NO_NULLCHECKING_LIB_SUPPORT
     []
     let inline ofObj value =
         match value with
@@ -163,6 +164,19 @@ module Option =
         match value with
         | None -> null
         | Some x -> x
+#else
+    []
+    let inline ofObj (value: 'T __withnull) : 'T option when 'T: not struct and 'T : __notnull = 
+        match value with
+        | null -> None
+        | _ -> Some value
+
+    []
+    let inline toObj (value: 'T option) : 'T __withnull when 'T: not struct (* and 'T : __notnull *)  =
+        match value with
+        | None -> null
+        | Some x -> x
+#endif
 
 module ValueOption =
 
@@ -315,6 +329,7 @@ module ValueOption =
         else
             ValueNone
 
+#if BUILDING_WITH_LKG || NO_NULLCHECKING_LIB_SUPPORT
     []
     let ofObj value =
         match value with
@@ -326,3 +341,16 @@ module ValueOption =
         match value with
         | ValueNone -> null
         | ValueSome x -> x
+#else
+    []
+    let ofObj (value: 'T __withnull) : 'T voption when 'T: not struct and 'T : __notnull  = 
+        match value with
+        | null -> ValueNone
+        | _ -> ValueSome value
+
+    []
+    let toObj (value : 'T voption) : 'T __withnull when 'T: not struct (* and 'T : __notnull *) = 
+        match value with
+        | ValueNone -> null
+        | ValueSome x -> x
+#endif
diff --git a/src/FSharp.Core/option.fsi b/src/FSharp.Core/option.fsi
index ba65f231932..fe8b5ac044f 100644
--- a/src/FSharp.Core/option.fsi
+++ b/src/FSharp.Core/option.fsi
@@ -440,7 +440,12 @@ module Option =
     /// 
     /// 
     []
-    val inline ofObj: value: 'T -> 'T option when 'T: null
+#if BUILDING_WITH_LKG || NO_NULLCHECKING_LIB_SUPPORT
+    val inline ofObj: value: 'T -> 'T option  when 'T : null
+#else
+    // TODO NULLNESS: assess this change - is it a breaking change?
+    val inline ofObj: value: 'T __withnull -> 'T option  when 'T : __notnull and 'T : not struct
+#endif
 
     /// Convert an option to a potentially null value.
     ///
@@ -455,7 +460,12 @@ module Option =
     /// 
     /// 
     []
-    val inline toObj: value: 'T option -> 'T when 'T: null
+#if BUILDING_WITH_LKG || NO_NULLCHECKING_LIB_SUPPORT
+    val inline toObj: value: 'T option -> 'T when 'T : null
+#else
+    // TODO NULLNESS: assess this change - is it a breaking change?
+    val inline toObj: value: 'T option -> 'T __withnull when 'T : not struct (* and 'T : __notnull *)
+#endif
 
 /// Contains operations for working with value options.
 ///
@@ -888,7 +898,12 @@ module ValueOption =
     /// 
     /// 
     []
-    val ofObj: value: 'T -> 'T voption when 'T: null
+#if BUILDING_WITH_LKG || NO_NULLCHECKING_LIB_SUPPORT
+    val ofObj: value: 'T -> 'T voption  when 'T : null
+#else
+    // TODO NULLNESS: assess this change - is it a breaking change?
+    val ofObj: value: 'T __withnull -> 'T voption  when 'T : not struct and 'T : __notnull
+#endif
 
     /// Convert an option to a potentially null value.
     ///
@@ -903,4 +918,9 @@ module ValueOption =
     /// 
     /// 
     []
-    val toObj: value: 'T voption -> 'T when 'T: null
+#if BUILDING_WITH_LKG || NO_NULLCHECKING_LIB_SUPPORT
+    val toObj: value: 'T voption -> 'T when 'T : null
+#else
+    // TODO NULLNESS: assess this change - is it a breaking change?
+    val toObj: value: 'T voption -> 'T __withnull when 'T : not struct (* and 'T : __notnull *)
+#endif
diff --git a/src/FSharp.Core/prim-types.fs b/src/FSharp.Core/prim-types.fs
index e05a55fa47b..5475ac66ea3 100644
--- a/src/FSharp.Core/prim-types.fs
+++ b/src/FSharp.Core/prim-types.fs
@@ -9,6 +9,8 @@
 #nowarn "69" // Interface implementations should normally be given on the initial declaration of a type. Interface implementations in augmentations may lead to accessing static bindings before they are initialized, though only if the interface implementation is invoked during initialization of the static data, and in turn access the static data. You may remove this warning using #nowarn "69" if you have checked this is not the case.
 #nowarn "77" // Member constraints with the name 'Exp' are given special status by the F# compiler...
 #nowarn "3218" // mismatch of parameter name for 'fst' and 'snd'
+#nowarn "3244" // no nullness checking
+#nowarn "3245" // no nullness checking
 
 namespace Microsoft.FSharp.Core
 
@@ -4222,8 +4224,8 @@ namespace Microsoft.FSharp.Core
             | _ -> None
 
         []
-        let inline isNull (value : 'T) = 
-            match value with 
+        let inline isNull (value : 'T when 'T : null) = 
+            match box value with 
             | null -> true 
             | _ -> false
 
@@ -4233,6 +4235,62 @@ namespace Microsoft.FSharp.Core
             | null -> false 
             | _ -> true
 
+#if !BUILDING_WITH_LKG && !NO_NULLCHECKING_LIB_SUPPORT
+
+        []
+        let inline isNullV (value : Nullable<'T>) = not value.HasValue
+
+        []
+        let inline nonNull (value : 'T __withnull when 'T : __notnull and 'T : not struct) = 
+            match box value with 
+            | null -> raise (NullReferenceException()) 
+            | _ -> (# "" value : 'T #)
+
+        []
+        let inline nonNullV (value : Nullable<'T>) = 
+            if value.HasValue then 
+                value.Value
+            else 
+                raise (NullReferenceException())
+
+        []
+        let inline (|Null|NonNull|) (value : 'T __withnull when 'T : __notnull and 'T : not struct) = 
+            match value with 
+            | null -> Null () 
+            | _ -> NonNull (# "" value : 'T #)
+
+        []
+        let inline (|NullV|NonNullV|) (value : Nullable<'T>) = 
+            if value.HasValue then NonNullV value.Value
+            else NullV ()
+
+        []
+        let inline (|NonNullQuick|) (value : 'T __withnull when 'T : __notnull and 'T : not struct) =
+            match box value with 
+            | null -> raise (NullReferenceException()) 
+            | _ -> (# "" value : 'T #)
+
+        []
+        let inline (|NonNullQuickV|) (value : Nullable<'T>) =
+            if value.HasValue then value.Value
+            else raise (NullReferenceException()) 
+
+        []
+        let inline withNull (value : 'T when 'T : __notnull and 'T : not struct) = (# "" value : 'T __withnull #)
+
+        []
+        let inline withNullV (value : 'T) : Nullable<'T> = Nullable<'T>(value)
+
+        []
+        let inline nullV<'T when 'T : struct and 'T : (new : unit -> 'T) and 'T :> ValueType>  = Nullable<'T>()
+
+        []
+        let inline nullArgCheck (argumentName:string) (value: 'T __withnull when 'T : __notnull and 'T : not struct) = 
+            match value with 
+            | null -> raise (new ArgumentNullException(argumentName))        
+            | _ ->  (# "" value : 'T #)
+#endif
+
         []
         let inline raise (exn: exn) =
             (# "throw" exn : 'T #)
@@ -4325,6 +4383,16 @@ namespace Microsoft.FSharp.Core
         []
         let defaultValueArg arg defaultValue = match arg with ValueNone -> defaultValue | ValueSome v -> v
 
+#if !BUILDING_WITH_LKG && !NO_NULLCHECKING_LIB_SUPPORT
+        []
+        let inline defaultIfNull defaultValue (arg: 'T __withnull when 'T : __notnull and 'T : not struct) = 
+            match arg with null -> defaultValue | _ -> (# "" arg : 'T #)
+        
+        []
+        let inline defaultIfNullV defaultValue (arg: Nullable<'T>) = 
+            if arg.HasValue then arg.Value else defaultValue
+#endif
+
         []
         let inline (~-) (n: ^T) : ^T = 
              UnaryNegationDynamic<(^T), (^T)> n
diff --git a/src/FSharp.Core/prim-types.fsi b/src/FSharp.Core/prim-types.fsi
index bcbfa77e320..b547b26a7d2 100644
--- a/src/FSharp.Core/prim-types.fsi
+++ b/src/FSharp.Core/prim-types.fsi
@@ -1343,7 +1343,11 @@ namespace Microsoft.FSharp.Core
         val inline FastGenericComparer<'T> : System.Collections.Generic.IComparer<'T> when 'T: comparison 
 
         /// Make an F# comparer object for the given type, where it can be null if System.Collections.Generic.Comparer<'T>.Default
-        val internal FastGenericComparerCanBeNull<'T> : System.Collections.Generic.IComparer<'T> when 'T: comparison 
+#if BUILDING_WITH_LKG || NO_NULLCHECKING_LIB_SUPPORT
+        val internal FastGenericComparerCanBeNull<'T>  : System.Collections.Generic.IComparer<'T> when 'T : comparison 
+#else
+        val internal FastGenericComparerCanBeNull<'T>  : System.Collections.Generic.IComparer<'T> __withnull when 'T : comparison 
+#endif
 
         /// Make an F# hash/equality object for the given type
         val inline FastGenericEqualityComparer<'T> : System.Collections.Generic.IEqualityComparer<'T> when 'T: equality
@@ -3098,6 +3102,24 @@ namespace Microsoft.FSharp.Core
         /// 
         val inline (<|||): func: ('T1 -> 'T2 -> 'T3 -> 'U) -> arg1: 'T1 * arg2: 'T2 * arg3: 'T3 -> 'U
 
+#if !BUILDING_WITH_LKG && !NO_NULLCHECKING_LIB_SUPPORT
+        /// Used to specify a default value for a nullable reference argument in the implementation of a function
+        /// The default value of the argument.
+        /// A nullable value representing the argument.
+        /// The argument value. If it is null, the defaultValue is returned.
+        []
+        []
+        val inline defaultIfNull : defaultValue:'T -> arg:'T __withnull -> 'T when 'T : __notnull and 'T : not struct 
+
+        /// Used to specify a default value for an nullable value argument in the implementation of a function
+        /// The default value of the argument.
+        /// A nullable value representing the argument.
+        /// The argument value. If it is null, the defaultValue is returned.
+        []
+        []
+        val inline defaultIfNullV : defaultValue:'T -> arg:Nullable<'T> -> 'T 
+#endif
+
         /// Used to specify a default value for an optional argument in the implementation of a function
         ///
         /// An option representing the argument.
@@ -3395,13 +3417,92 @@ namespace Microsoft.FSharp.Core
         []
         val inline isNull: value: 'T -> bool when 'T: null
         
+#if !BUILDING_WITH_LKG && !NO_NULLCHECKING_LIB_SUPPORT
+        /// Determines whether the given value is null.
+        /// The value to check.
+        /// A choice indicating whether the value is null or not-null.
+        []
+        []
+        val inline (|Null|NonNull|) : value: 'T __withnull -> Choice  when 'T : __notnull and 'T : not struct
+        
+        /// Determines whether the given value is null.
+        /// In a future revision of nullness support this may be unified with 'Null|NonNull'.
+        /// The value to check.
+        /// A choice indicating whether the value is null or not-null.
+        []
+        []
+        val inline (|NullV|NonNullV|) : value: Nullable<'T> -> Choice 
+        
+        /// When used in a pattern checks the given value is not null.
+        /// The value to check.
+        /// The non-null value.
+        []
+        []
+        val inline (|NonNullQuick|) : value: 'T __withnull -> 'T when 'T : __notnull and 'T : not struct
+        
+        /// When used in a pattern checks the given value is not null.
+        /// In a future revision of nullness support this may be unified with 'NonNullQuick'.
+        /// The value to check.
+        /// The non-null value.
+        []
+        []
+        val inline (|NonNullQuickV|) : value: Nullable<'T> -> 'T 
+        
+        /// Determines whether the given value is null.
+        /// In a future revision of nullness support this may be unified with 'isNull'.
+        /// The value to check.
+        /// True when value is null, false otherwise.
+        []
+        []
+        val inline isNullV : value:Nullable<'T> -> bool
+#endif
+
         /// Determines whether the given value is not null.
         ///
         /// The value to check.
         ///
         /// True when value is not null, false otherwise.
         []
-        val inline internal isNotNull: value: 'T -> bool when 'T: null
+        val inline internal isNotNull: value:'T -> bool when 'T : null
+
+#if !BUILDING_WITH_LKG && !NO_NULLCHECKING_LIB_SUPPORT
+        /// Get the null value for a value type.
+        /// In a future revision of nullness support this may be unified with 'null'.
+        /// The null value for a value type.
+        []
+        []
+        val inline nullV<'T when 'T : struct and 'T : (new : unit -> 'T) and 'T :> ValueType> :  Nullable<'T>
+
+        /// Asserts that the value is non-null.
+        /// The value to check.
+        /// The value when it is not null. If the value is null an exception is raised.
+        []
+        []
+        val inline nonNull : value: 'T __withnull -> 'T when 'T : __notnull and 'T : not struct
+
+        /// Asserts that the value is non-null.
+        /// In a future revision of nullness support this may be unified with 'nonNull'.
+        /// The value to check.
+        /// True when value is null, false otherwise.
+        []
+        []
+        val inline nonNullV : value:Nullable<'T> -> 'T 
+
+        /// Asserts that the value is non-null.
+        /// The value to check.
+        /// True when value is null, false otherwise.
+        []
+        []
+        val inline withNull : value:'T -> 'T __withnull when 'T : __notnull and 'T : not struct
+
+        /// Asserts that the value is non-null.
+        /// In a future revision of nullness support this may be unified with 'withNull'.
+        /// The value to check.
+        /// True when value is null, false otherwise.
+        []
+        []
+        val inline withNullV : value:'T -> Nullable<'T> 
+#endif
 
         /// Throw a  exception.
         ///
@@ -3467,6 +3568,17 @@ namespace Microsoft.FSharp.Core
         []
         val inline nullArg: argumentName: string -> 'T 
 
+#if !BUILDING_WITH_LKG && !NO_NULLCHECKING_LIB_SUPPORT
+        /// Throw a System.ArgumentNullException if the given value is null exception
+        /// 
+        /// The argument name.
+        /// 
+        /// The result value.
+        []
+        []
+        val inline nullArgCheck : argumentName:string -> 'T __withnull -> 'T when 'T : __notnull and 'T : not struct
+#endif
+
         /// Throw a  exception
         ///
         /// The exception message.
@@ -3641,7 +3753,7 @@ namespace Microsoft.FSharp.Core
         /// otherwise raise an exception. Calls .
         ///
         /// The exit code to use.
-        /// 
+        ///
         /// Never returns.
         /// 
         /// 
diff --git a/src/fsi/console.fs b/src/fsi/console.fs
index 4e68d2aea0b..a56b3f4e4f7 100644
--- a/src/fsi/console.fs
+++ b/src/fsi/console.fs
@@ -7,6 +7,18 @@ open System.Text
 open System.Collections.Generic
 open FSharp.Compiler.DiagnosticsLogger
 
+[]
+module internal ConsoleHelpers =
+
+#if NO_CHECKNULLS
+  type MaybeNull<'T when 'T : null> = 'T
+
+  // Shim to match nullness checking library support in preview
+  let inline (|Null|NonNull|) (x: 'T) : Choice = match x with null -> Null | v -> NonNull v
+#else
+  type MaybeNull<'T when 'T : __notnull> = 'T __withnull
+#endif
+
 type internal Style =
     | Prompt
     | Out
@@ -29,24 +41,20 @@ type internal History() =
         list.Clear()
         current <- -1
 
-    member _.Add line =
-        match line with
-        | null
+    member _.Add (line: string MaybeNull) = 
+        match line with 
+        | Null
         | "" -> ()
-        | _ -> list.Add(line)
+        | NonNull line -> list.Add(line)
 
-    member _.AddLast line =
-        match line with
-        | null
+    member _.AddLast (line: string MaybeNull) =  
+        match line with 
+        | Null
         | "" -> ()
-        | _ ->
+        | NonNull line ->
             list.Add(line)
             current <- list.Count
 
-    // Dead code
-    // member x.First() = current <- 0; x.Current
-    // member x.Last() = current <- list.Count - 1; x.Current
-
     member x.Previous() =
         if (list.Count > 0) then
             current <- ((current - 1) + list.Count) % list.Count
diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/BindingExpressions/in05.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/BindingExpressions/in05.fs
index 583f27de4e8..0c386d1676d 100644
--- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/BindingExpressions/in05.fs
+++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/BindingExpressions/in05.fs
@@ -4,9 +4,9 @@
 // I'm adding these cases to make sure we do not accidentally change the behavior from version to version
 // Eventually, we will deprecated them - and the specs will be updated.
 //The type 'int' does not match the type 'unit'$
-//Type mismatch\. Expecting a.    ''a -> 'b'    .but given a.    ''a -> unit'    .The type 'int' does not match the type 'unit'$
 //The result of this expression has type 'bool' and is implicitly ignored\. Consider using 'ignore' to discard this value explicitly, e\.g\. 'expr \|> ignore', or 'let' to bind the result to a name, e\.g\. 'let result = expr'.$
-module E
+
+module E = 
     let a = 3 in
         a + 1 |> ignore
         a + 1 |> ignore
diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeEqualsMissingTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeEqualsMissingTests.fs
index 3dacfbfde6e..e62cdf6822c 100644
--- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeEqualsMissingTests.fs
+++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeEqualsMissingTests.fs
@@ -6,16 +6,16 @@ open Xunit
 open FSharp.Test
 open FSharp.Compiler.Diagnostics
 
+// Disabled this test, see https://github.com/dotnet/fsharp/commit/98dc1b09fa1ff15176998dc2d28c5b5c8d0f80b1#r43542750
+//module ``Type definition missing equals`` =
 
-module ``Type definition missing equals`` =
-
-    []
-    let ``Missing equals in DU``() =
-        CompilerAssert.TypeCheckSingleError
-            """
-type X | A | B
-            """
-            FSharpDiagnosticSeverity.Error
-            3360 
-            (2, 8, 2, 9)
-            "Unexpected token in type definition. Expected '=' after the type 'X'."
+//     []
+//     let ``Missing equals in DU``() =
+//         CompilerAssert.TypeCheckSingleError
+//             """
+// type X | A | B
+//             """
+//             FSharpDiagnosticSeverity.Error
+//             3360 
+//             (2, 8, 2, 9)
+//             "Unexpected token in type definition. Expected '=' after the type 'X'."
diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl
index 0faf9512f36..b067bd9ef51 100644
--- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl
+++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl
@@ -11341,4 +11341,35 @@ FSharp.Compiler.Xml.XmlDoc: System.String GetXmlText()
 FSharp.Compiler.Xml.XmlDoc: System.String[] GetElaboratedXmlLines()
 FSharp.Compiler.Xml.XmlDoc: System.String[] UnprocessedLines
 FSharp.Compiler.Xml.XmlDoc: System.String[] get_UnprocessedLines()
-FSharp.Compiler.Xml.XmlDoc: Void .ctor(System.String[], FSharp.Compiler.Text.Range)
\ No newline at end of file
+FSharp.Compiler.Xml.XmlDoc: Void .ctor(System.String[], FSharp.Compiler.Text.Range)
+FSharp.Compiler.Symbols.FSharpType: Boolean HasNullAnnotation
+FSharp.Compiler.Symbols.FSharpType: Boolean IsNullAmbivalent
+FSharp.Compiler.Symbols.FSharpType: Boolean get_HasNullAnnotation()
+FSharp.Compiler.Symbols.FSharpType: Boolean get_IsNullAmbivalent()
+FSharp.Compiler.Syntax.SynType+StaticConstantNull: FSharp.Compiler.Text.Range get_range()
+FSharp.Compiler.Syntax.SynType+StaticConstantNull: FSharp.Compiler.Text.Range range
+FSharp.Compiler.Syntax.SynType+Tags: Int32 StaticConstantNull
+FSharp.Compiler.Syntax.SynType+Tags: Int32 WithNull
+FSharp.Compiler.Syntax.SynType+WithNull: Boolean ambivalent
+FSharp.Compiler.Syntax.SynType+WithNull: Boolean get_ambivalent()
+FSharp.Compiler.Syntax.SynType+WithNull: FSharp.Compiler.Syntax.SynType get_innerType()
+FSharp.Compiler.Syntax.SynType+WithNull: FSharp.Compiler.Syntax.SynType innerType
+FSharp.Compiler.Syntax.SynType+WithNull: FSharp.Compiler.Text.Range get_range()
+FSharp.Compiler.Syntax.SynType+WithNull: FSharp.Compiler.Text.Range range
+FSharp.Compiler.Syntax.SynType: Boolean IsStaticConstantNull
+FSharp.Compiler.Syntax.SynType: Boolean IsWithNull
+FSharp.Compiler.Syntax.SynType: Boolean get_IsStaticConstantNull()
+FSharp.Compiler.Syntax.SynType: Boolean get_IsWithNull()
+FSharp.Compiler.Syntax.SynType: FSharp.Compiler.Syntax.SynType NewStaticConstantNull(FSharp.Compiler.Text.Range)
+FSharp.Compiler.Syntax.SynType: FSharp.Compiler.Syntax.SynType NewWithNull(FSharp.Compiler.Syntax.SynType, Boolean, FSharp.Compiler.Text.Range)
+FSharp.Compiler.Syntax.SynType: FSharp.Compiler.Syntax.SynType+StaticConstantNull
+FSharp.Compiler.Syntax.SynType: FSharp.Compiler.Syntax.SynType+WithNull
+FSharp.Compiler.Syntax.SynTypeConstraint+Tags: Int32 WhereTyparNotSupportsNull
+FSharp.Compiler.Syntax.SynTypeConstraint+WhereTyparNotSupportsNull: FSharp.Compiler.Syntax.SynTypar genericName
+FSharp.Compiler.Syntax.SynTypeConstraint+WhereTyparNotSupportsNull: FSharp.Compiler.Syntax.SynTypar get_genericName()
+FSharp.Compiler.Syntax.SynTypeConstraint+WhereTyparNotSupportsNull: FSharp.Compiler.Text.Range get_range()
+FSharp.Compiler.Syntax.SynTypeConstraint+WhereTyparNotSupportsNull: FSharp.Compiler.Text.Range range
+FSharp.Compiler.Syntax.SynTypeConstraint: Boolean IsWhereTyparNotSupportsNull
+FSharp.Compiler.Syntax.SynTypeConstraint: Boolean get_IsWhereTyparNotSupportsNull()
+FSharp.Compiler.Syntax.SynTypeConstraint: FSharp.Compiler.Syntax.SynTypeConstraint NewWhereTyparNotSupportsNull(FSharp.Compiler.Syntax.SynTypar, FSharp.Compiler.Text.Range)
+FSharp.Compiler.Syntax.SynTypeConstraint: FSharp.Compiler.Syntax.SynTypeConstraint+WhereTyparNotSupportsNull
diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Core/BigIntType.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Core/BigIntType.fs
index caa7e84591c..8e0a6eb44f1 100644
--- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Core/BigIntType.fs
+++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Core/BigIntType.fs
@@ -93,7 +93,7 @@ type BigIntType() =
         Assert.True(z4.Equals(z4))
 
         // Null
-        Assert.False(a.Equals(null))
+        Assert.False(a.Equals(null:obj)) // TODO NULLNESS - this type annoation was needed to resolve overloading, even with --checknulls off
 
         Assert.True(0I.GetHashCode() = (BigInteger()).GetHashCode())
     
diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Core/OptionModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Core/OptionModule.fs
index cecad68f233..7b0ff69b420 100644
--- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Core/OptionModule.fs
+++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Core/OptionModule.fs
@@ -131,7 +131,7 @@ type OptionModule() =
     member this.OfToObj() =
         Assert.True( Option.toObj (Some "3") = "3")
         Assert.True( Option.toObj (Some "") = "")
-        Assert.True( Option.toObj (Some null) = null)
+        Assert.True( Option.toObj (Some null) = null) // TODO NULLNESS: this type annotation should not be needed 
         Assert.True( Option.toObj None = null)     
      
         Assert.True( Option.ofObj "3" = Some "3")
@@ -369,7 +369,7 @@ type ValueOptionTests() =
     member this.OfToObj() =
         Assert.True(ValueOption.toObj (ValueSome "3") = "3")
         Assert.True(ValueOption.toObj (ValueSome "") = "")
-        Assert.True(ValueOption.toObj (ValueSome null) = null)
+        Assert.True(ValueOption.toObj (ValueSome null) = null)  // TODO NULLNESS: this type annotation should not be needed
         Assert.True(ValueOption.toObj ValueNone = null)     
      
         Assert.True(ValueOption.ofObj "3" = ValueSome "3")
diff --git a/tests/fsharp/Compiler/Regressions/NullableOptionalRegressionTests.fs b/tests/fsharp/Compiler/Regressions/NullableOptionalRegressionTests.fs
index c4009ee5233..341955aa7c8 100644
--- a/tests/fsharp/Compiler/Regressions/NullableOptionalRegressionTests.fs
+++ b/tests/fsharp/Compiler/Regressions/NullableOptionalRegressionTests.fs
@@ -8,7 +8,8 @@ open FSharp.Test.Compiler
 []
 module NullableOptionalRegressionTests =
 
-    []
+    //Disabled, see RFC for nullable
+    //[]
     let ``Should compile with generic overloaded nullable methods``() =
         Fsx """
 open System
diff --git a/tests/fsharp/tests.fs b/tests/fsharp/tests.fs
index e29ae44733b..bfdb893c2dc 100644
--- a/tests/fsharp/tests.fs
+++ b/tests/fsharp/tests.fs
@@ -36,8 +36,69 @@ let testConfig = getTestsDirectory >> testConfig
 []
 module CoreTests =
 
+
 #if !NETCOREAPP
-// This test stays in FsharpSuite for a later migration phases, it uses hardcoded #r to a C# compiled cslib.dll inside
+    []
+    let ``subtype-langversion-preview-checknulls`` () =
+        let cfg = testConfig "core/subtype"
+
+        use testOkFile = fileguard cfg "test.ok"
+
+        fsc cfg "%s -o:test-checknulls.exe -g --langversion:preview --checknulls" cfg.fsc_flags ["test.fsx"]
+
+        exec cfg ("." ++ "test-checknulls.exe") ""
+
+        testOkFile.CheckExists()
+
+    []
+    let ``subtype-langversion-preview-no-checknulls`` () =
+        let cfg = testConfig "core/subtype"
+
+        use testOkFile = fileguard cfg "test.ok"
+
+        fsc cfg "%s -o:test-no-checknulls.exe -g --langversion:preview --checknulls-" cfg.fsc_flags ["test.fsx"]
+
+        exec cfg ("." ++ "test-no-checknulls.exe") ""
+
+        testOkFile.CheckExists()
+
+    []
+    let ``subtype-langversion-46`` () =
+        let cfg = testConfig "core/subtype"
+
+        use testOkFile = fileguard cfg "test.ok"
+
+        fsc cfg "%s -o:test-langversion-46.exe -g --langversion:4.6" cfg.fsc_flags ["test.fsx"]
+
+        exec cfg ("." ++ "test-langversion-46.exe") ""
+
+        testOkFile.CheckExists()
+
+    []
+    let nullness_no_checknulls () =
+        let cfg = testConfig "core/nullness"
+
+        use testOkFile = fileguard cfg "test.ok"
+
+        fsc cfg "%s -o:test-no-checknulls.exe -g --langversion:preview --define:NO_CHECKNULLS" cfg.fsc_flags ["test.fsx"]
+
+        exec cfg ("." ++ "test-no-checknulls.exe") ""
+
+        testOkFile.CheckExists()
+
+    []
+    let nullness_checknulls () =
+        let cfg = testConfig "core/nullness"
+
+        use testOkFile = fileguard cfg "test.ok"
+
+        fsc cfg "%s -o:test-checknulls.exe -g --checknulls --langversion:preview" cfg.fsc_flags ["test.fsx"]
+
+        exec cfg ("." ++ "test-checknulls.exe") ""
+
+        testOkFile.CheckExists()
+
+    // This test stays in FsharpSuite for a later migration phases, it uses hardcoded #r to a C# compiled cslib.dll inside
     []
     let ``quotes-FSC-FSC_DEBUG`` () = singleTestBuildAndRun "core/quotes" FSC_DEBUG
 
@@ -1025,6 +1086,31 @@ module CoreTests =
     []
     let ``libtest-FSC_NETFX_TEST_ROUNDTRIP_AS_DLL`` () = singleTestBuildAndRun "core/libtest" FSC_NETFX_TEST_ROUNDTRIP_AS_DLL
 
+    []
+    let ``libtest-langversion-preview-checknulls`` () =
+        let cfg = testConfig "core/libtest"
+
+        use testOkFile = fileguard cfg "test.ok"
+
+        fsc cfg "%s -o:test-checknulls.exe -g --langversion:preview --checknulls" cfg.fsc_flags ["test.fsx"]
+
+        exec cfg ("." ++ "test-checknulls.exe") ""
+
+        testOkFile.CheckExists()
+
+ 
+    []
+    let ``libtest-langversion-46`` () =
+        let cfg = testConfig "core/libtest"
+
+        use testOkFile = fileguard cfg "test.ok"
+
+        fsc cfg "%s -o:test-langversion-46.exe -g --langversion:4.6" cfg.fsc_flags ["test.fsx"]
+
+        exec cfg ("." ++ "test-langversion-46.exe") ""
+
+        testOkFile.CheckExists()
+
     []
     let ``no-warn-2003-tests`` () =
         // see https://github.com/dotnet/fsharp/issues/3139
diff --git a/tests/fsharp/typecheck/overloads/neg_known_return_type_and_known_type_arguments.fsx b/tests/fsharp/typecheck/overloads/neg_known_return_type_and_known_type_arguments.fsx
index 947735d0e84..72bfdad9c21 100644
--- a/tests/fsharp/typecheck/overloads/neg_known_return_type_and_known_type_arguments.fsx
+++ b/tests/fsharp/typecheck/overloads/neg_known_return_type_and_known_type_arguments.fsx
@@ -13,7 +13,7 @@ type Plus =
     inherit Default1
     static member inline ``+`` (x: 'Plus             , y: 'Plus             ,             _mthd: Default2) = (^Plus :  (static member (<|>) : _*_ -> _) x, y) : ^Plus
     static member inline ``+`` (x: 'Plus             , y: 'Plus             , []_mthd: Default1) = x + y : ^Plus
-    static member inline ``+`` (_: ^t when ^t: null and ^t: struct, _: ^t   , []_mthd: Default1) = id
+
     
     static member inline Invoke (x: 'Plus) (y: 'Plus) : 'Plus =
         let inline call (mthd : ^M, input1 : ^I, input2 : ^I) = ((^M or ^I) : (static member ``+`` : _*_*_ -> _) input1, input2, mthd)
@@ -49,9 +49,9 @@ type Zero =
   inherit Default1
   static member inline Zero (_: 't                             , _: Default3) = (^t : (static member Empty : ^t) ()) : 't
   static member inline Zero (_: 't                             , _: Default2) = FromInt32.Invoke 0             : 't
-  static member inline Zero (_: ^t when ^t: null and ^t: struct, _: Default2) = id
+
   static member inline Zero (_: 't                             , _: Default1) = LanguagePrimitives.GenericZero : 't
-  static member inline Zero (_: ^t when ^t: null and ^t: struct, _: Default1) = id
+
   static member        Zero (_: System.TimeSpan                , _: Zero    ) = System.TimeSpan ()
   static member        Zero (_: list<'a>                       , _: Zero    ) = []   :   list<'a>
   static member        Zero (_: option<'a>                     , _: Zero    ) = None : option<'a>
diff --git a/tests/fsharp/typecheck/sigs/neg04.bsl b/tests/fsharp/typecheck/sigs/neg04.bsl
index 25a4a425582..57638b360fc 100644
--- a/tests/fsharp/typecheck/sigs/neg04.bsl
+++ b/tests/fsharp/typecheck/sigs/neg04.bsl
@@ -30,7 +30,7 @@ neg04.fs(47,30,47,51): typecheck error FS0001: Type mismatch. Expecting a
     'seq<'a> -> 'n'    
 but given a
     ''o list -> 'p'    
-The type 'seq<'a>' does not match the type ''n list'
+The type ''n list' does not match the type 'seq<'a>'
 
 neg04.fs(47,49,47,51): typecheck error FS0784: This numeric literal requires that a module 'NumericLiteralN' defining functions FromZero, FromOne, FromInt32, FromInt64 and FromString be in scope
 
@@ -38,7 +38,7 @@ neg04.fs(47,30,47,51): typecheck error FS0001: Type mismatch. Expecting a
     'seq<'a> -> 'n'    
 but given a
     ''o list -> 'p'    
-The type 'seq<'a>' does not match the type ''n list'
+The type ''n list' does not match the type 'seq<'a>'
 
 neg04.fs(61,25,61,40): typecheck error FS0001: This expression was expected to have type
     'ClassType1'    
diff --git a/tests/fsharp/typecheck/sigs/neg20.bsl b/tests/fsharp/typecheck/sigs/neg20.bsl
index 88f483967e4..095ec90fd47 100644
--- a/tests/fsharp/typecheck/sigs/neg20.bsl
+++ b/tests/fsharp/typecheck/sigs/neg20.bsl
@@ -90,19 +90,19 @@ neg20.fs(108,12,108,16): typecheck error FS0001: Type mismatch. Expecting a
     'B * B -> 'a'    
 but given a
     'A * A -> Data'    
-The type 'B' does not match the type 'A'
+The type 'A' does not match the type 'B'
 
 neg20.fs(109,12,109,16): typecheck error FS0001: Type mismatch. Expecting a
     'A * B -> 'a'    
 but given a
     'A * A -> Data'    
-The type 'B' does not match the type 'A'
+The type 'A' does not match the type 'B'
 
 neg20.fs(110,12,110,16): typecheck error FS0001: Type mismatch. Expecting a
     'B * A -> 'a'    
 but given a
     'A * A -> Data'    
-The type 'B' does not match the type 'A'
+The type 'A' does not match the type 'B'
 
 neg20.fs(128,19,128,22): typecheck error FS0001: This expression was expected to have type
     'string'    
diff --git a/vsintegration/src/FSharp.VS.FSI/fsiBasis.fs b/vsintegration/src/FSharp.VS.FSI/fsiBasis.fs
index 8e4d3974761..771b9c0c979 100644
--- a/vsintegration/src/FSharp.VS.FSI/fsiBasis.fs
+++ b/vsintegration/src/FSharp.VS.FSI/fsiBasis.fs
@@ -38,11 +38,12 @@ module internal Guids =
     let guidFSharpProjectPkgString      = "91A04A73-4F2C-4E7C-AD38-C1A68E7DA05C" // FSI-LINKAGE-POINT: when packaged in project system
 
     []
-    /// "35A5E6B8-4012-41fc-A652-2CDC56D74E9F"
+
+    // "35A5E6B8-4012-41fc-A652-2CDC56D74E9F"
     let guidFsiLanguageService          = "35A5E6B8-4012-41fc-A652-2CDC56D74E9F"        // The FSI lang service
 
     []
-    /// "dee22b65-9761-4a26-8fb2-759b971d6dfc"
+    // "dee22b65-9761-4a26-8fb2-759b971d6dfc"
     let guidFsiSessionToolWindow        = "dee22b65-9761-4a26-8fb2-759b971d6dfc"
 
     // FSI Package command set