From 0bfe450d1b671b0d9373af417639f82539a60f85 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 15 Aug 2024 16:54:21 +0200 Subject: [PATCH 01/10] See what happens if I just publish a ValSpec for ToString for DUs, without generating a binding for it --- .../Checking/AugmentWithHashCompare.fs | 32 ++++++++++++++----- .../Nullness/NullableReferenceTypesTests.fs | 22 +++++++++++++ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/Compiler/Checking/AugmentWithHashCompare.fs b/src/Compiler/Checking/AugmentWithHashCompare.fs index c64b0001a29..e8c67506c30 100644 --- a/src/Compiler/Checking/AugmentWithHashCompare.fs +++ b/src/Compiler/Checking/AugmentWithHashCompare.fs @@ -81,6 +81,9 @@ let mkGetHashCodeSlotSig (g: TcGlobals) = let mkEqualsSlotSig (g: TcGlobals) = TSlotSig("Equals", g.obj_ty_noNulls, [], [], [ [ TSlotParam(Some("obj"), g.obj_ty_withNulls, false, false, false, []) ] ], Some g.bool_ty) +let mkToStringSlotSig (g: TcGlobals) = + TSlotSig("ToString", g.obj_ty_noNulls, [], [], [ [] ], Some g.string_ty) + //------------------------------------------------------------------------- // Helpers associated with code-generation of comparison/hash augmentations //------------------------------------------------------------------------- @@ -112,6 +115,9 @@ let mkEqualsWithComparerTyExact g ty = let mkHashTy g ty = mkFunTy g (mkThisTy g ty) (mkFunTy g g.unit_ty g.int_ty) +let mkToStringTy g ty = + mkFunTy g (mkThisTy g ty) (mkFunTy g g.unit_ty g.string_ty) + let mkHashWithComparerTy g ty = mkFunTy g (mkThisTy g ty) (mkFunTy g g.IEqualityComparer_ty g.int_ty) @@ -1683,14 +1689,22 @@ let MakeValsForUnionAugmentation g (tcref: TyconRef) = let vis = tcref.TypeReprAccessibility let tps = tcref.Typars m - tcref.UnionCasesAsList - |> List.map (fun uc -> - // Unlike other generated items, the 'IsABC' propeties are visible, not considered compiler-generated - let v = - mkImpliedValSpec g uc.Range tcref tmty vis None ("get_Is" + uc.CompiledName) (tps +-> (mkIsCaseTy g tmty)) unitArg true + let isTesters = + + tcref.UnionCasesAsList + |> List.map (fun uc -> + // Unlike other generated items, the 'IsABC' propeties are visible, not considered compiler-generated + let v = + mkImpliedValSpec g uc.Range tcref tmty vis None ("get_Is" + uc.CompiledName) (tps +-> (mkIsCaseTy g tmty)) unitArg true + + g.AddValGeneratedAttributes v m + v) - g.AddValGeneratedAttributes v m - v) + let toString = + mkValSpec g tcref tmty vis (Some(mkToStringSlotSig g)) "ToString" (tps +-> (mkToStringTy g tmty)) unitArg false + + + [ yield toString;yield! isTesters] let MakeBindingsForUnionAugmentation g (tycon: Tycon) (vals: ValRef list) = let tcref = mkLocalTyconRef tycon @@ -1700,7 +1714,9 @@ let MakeBindingsForUnionAugmentation g (tycon: Tycon) (vals: ValRef list) = let thisv, thise = mkThisVar g m ty let unitv, _ = mkCompGenLocal m "unitArg" g.unit_ty - (tcref.UnionCasesAsRefList, vals) + let _ToStringVal,isVals = List.headAndTail vals + + (tcref.UnionCasesAsRefList, isVals) ||> List.map2 (fun ucr v -> let isdata = mkUnionCaseTest g (thise, ucr, tinst, m) let expr = mkLambdas g m tps [ thisv; unitv ] (isdata, g.bool_ty) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs index c355a6a244f..b173fac2cbe 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs @@ -8,6 +8,7 @@ let withNullnessOptions cu = |> withCheckNulls |> withWarnOn 3261 |> withWarnOn 3262 + |> withNoWarn 52 //The value has been copied to ensure the original.. |> withOptions ["--warnaserror+"] let typeCheckWithStrictNullness cu = @@ -299,6 +300,27 @@ let processBool (b:bool) : string = |> typeCheckWithStrictNullness |> shouldSucceed +[] +[] +[] +[] +[] +[] +[] +let ``Generated ToString() methods are not nullable`` (myTypeDef) = + FSharp $"""module MyLibrary + +type MyCustomType = {myTypeDef} + +let onlyWantNotNullString(x:string) = () + +let processBool (x:MyCustomType) = + onlyWantNotNullString(x.ToString()) +""" + |> asLibrary + |> typeCheckWithStrictNullness + |> shouldSucceed + [] let ``Printing a nullable string should pass`` () = FSharp """module MyLibrary From 43444007ecc7c268f5a94a649886eb1419196856 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 16 Aug 2024 10:16:39 +0200 Subject: [PATCH 02/10] DUs with custom ToString , also via extensions --- .../Checking/AugmentWithHashCompare.fs | 7 ++++--- .../Nullness/NullableReferenceTypesTests.fs | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Checking/AugmentWithHashCompare.fs b/src/Compiler/Checking/AugmentWithHashCompare.fs index e8c67506c30..9d106a7e5be 100644 --- a/src/Compiler/Checking/AugmentWithHashCompare.fs +++ b/src/Compiler/Checking/AugmentWithHashCompare.fs @@ -1700,11 +1700,12 @@ let MakeValsForUnionAugmentation g (tcref: TyconRef) = g.AddValGeneratedAttributes v m v) - let toString = + let toStringSpec() = mkValSpec g tcref tmty vis (Some(mkToStringSlotSig g)) "ToString" (tps +-> (mkToStringTy g tmty)) unitArg false - - [ yield toString;yield! isTesters] + [ if tcref.HasOverride g "ToString" [] |> not then + yield toStringSpec() + yield! isTesters] let MakeBindingsForUnionAugmentation g (tycon: Tycon) (vals: ValRef list) = let tcref = mkLocalTyconRef tycon diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs index b173fac2cbe..03b1273dd8a 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs @@ -300,8 +300,29 @@ let processBool (b:bool) : string = |> typeCheckWithStrictNullness |> shouldSucceed +[] +let duWithExtensionProvidedToString = """A | B +module Functions = + let printMyType (x:MyCustomType) : string = "xxx" +type MyCustomType with + override x.ToString() : string = WhatEver.print x + """ + +[] +let duWithExtensionProvidedNullableToString = """A | B +module Functions = + let printMyType (x:MyCustomType) : string = "xxx" +type MyCustomType with + override x.ToString() = null + """ + + [] [] +[] +[] +[] +[] [] [] [] From a2319e3adaaee872e71f29929a762e4fa6e064a5 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 16 Aug 2024 12:23:39 +0200 Subject: [PATCH 03/10] Publish valspec at the end of impl/sig file processing --- .../Checking/AugmentWithHashCompare.fs | 37 +++++++++---------- .../Checking/AugmentWithHashCompare.fsi | 2 + src/Compiler/Checking/CheckDeclarations.fs | 21 ++++++++++- .../Nullness/NullableReferenceTypesTests.fs | 3 +- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/Compiler/Checking/AugmentWithHashCompare.fs b/src/Compiler/Checking/AugmentWithHashCompare.fs index 9d106a7e5be..57f60e6b2c5 100644 --- a/src/Compiler/Checking/AugmentWithHashCompare.fs +++ b/src/Compiler/Checking/AugmentWithHashCompare.fs @@ -1689,23 +1689,24 @@ let MakeValsForUnionAugmentation g (tcref: TyconRef) = let vis = tcref.TypeReprAccessibility let tps = tcref.Typars m - let isTesters = - - tcref.UnionCasesAsList - |> List.map (fun uc -> - // Unlike other generated items, the 'IsABC' propeties are visible, not considered compiler-generated - let v = - mkImpliedValSpec g uc.Range tcref tmty vis None ("get_Is" + uc.CompiledName) (tps +-> (mkIsCaseTy g tmty)) unitArg true - - g.AddValGeneratedAttributes v m - v) - - let toStringSpec() = + tcref.UnionCasesAsList + |> List.map (fun uc -> + // Unlike other generated items, the 'IsABC' propeties are visible, not considered compiler-generated + let v = + mkImpliedValSpec g uc.Range tcref tmty vis None ("get_Is" + uc.CompiledName) (tps +-> (mkIsCaseTy g tmty)) unitArg true + + g.AddValGeneratedAttributes v m + v) + +let MakeValForNonNullableToStringOverride (g:TcGlobals) (tcref: TyconRef) = + if g.checkNullness && not (tcref.HasOverride g "ToString" []) && (tcref.IsRecordTycon || tcref.IsUnionTycon) then + let _, tmty = mkMinimalTy g tcref + let tps = tcref.Typars tcref.Range + let vis = tcref.TypeReprAccessibility mkValSpec g tcref tmty vis (Some(mkToStringSlotSig g)) "ToString" (tps +-> (mkToStringTy g tmty)) unitArg false - - [ if tcref.HasOverride g "ToString" [] |> not then - yield toStringSpec() - yield! isTesters] + |> List.singleton + else + [] let MakeBindingsForUnionAugmentation g (tycon: Tycon) (vals: ValRef list) = let tcref = mkLocalTyconRef tycon @@ -1715,9 +1716,7 @@ let MakeBindingsForUnionAugmentation g (tycon: Tycon) (vals: ValRef list) = let thisv, thise = mkThisVar g m ty let unitv, _ = mkCompGenLocal m "unitArg" g.unit_ty - let _ToStringVal,isVals = List.headAndTail vals - - (tcref.UnionCasesAsRefList, isVals) + (tcref.UnionCasesAsRefList, vals) ||> List.map2 (fun ucr v -> let isdata = mkUnionCaseTest g (thise, ucr, tinst, m) let expr = mkLambdas g m tps [ thisv; unitv ] (isdata, g.bool_ty) diff --git a/src/Compiler/Checking/AugmentWithHashCompare.fsi b/src/Compiler/Checking/AugmentWithHashCompare.fsi index 4a7d74311b1..578341abe0a 100644 --- a/src/Compiler/Checking/AugmentWithHashCompare.fsi +++ b/src/Compiler/Checking/AugmentWithHashCompare.fsi @@ -44,4 +44,6 @@ val TypeDefinitelyHasEquality: TcGlobals -> TType -> bool val MakeValsForUnionAugmentation: TcGlobals -> TyconRef -> Val list +val MakeValForNonNullableToStringOverride: TcGlobals -> TyconRef -> Val list + val MakeBindingsForUnionAugmentation: TcGlobals -> Tycon -> ValRef list -> Binding list diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index b1ed720c973..fd6969b0ba2 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -5699,6 +5699,19 @@ let MakeInitialEnv env = let moduleTyAcc = ref (Construct.NewEmptyModuleOrNamespaceType (Namespace false)) { env with eModuleOrNamespaceTypeAccumulator = moduleTyAcc }, moduleTyAcc +let ChangeFsharpTypesToNonNullToString (g:TcGlobals) checkForErrors moduleOrNsContents cenv env m = + if g.checkNullness then + conditionallySuppressErrorReporting (checkForErrors()) (fun () -> + try + moduleOrNsContents |> IterTyconsOfModuleOrNamespaceType (fun tycon -> + if tycon.IsRecordTycon || tycon.IsUnionTycon then + let tcref = mkLocalTyconRef tycon + AugmentTypeDefinitions.MakeValForNonNullableToStringOverride g tcref + |> List.iter (fun v -> PublishValueDefnMaybeInclCompilerGenerated cenv env true ModuleOrMemberBinding v)) + + with RecoverableException exn -> + errorRecovery exn m) + /// Check an entire implementation file /// Typecheck, then close the inference scope and then check the file meets its signature (if any) let CheckOneImplFile @@ -5774,12 +5787,15 @@ let CheckOneImplFile conditionallySuppressErrorReporting (checkForErrors()) (fun () -> ApplyDefaults cenv g denvAtEnd m moduleContents extraAttribs) + ChangeFsharpTypesToNonNullToString g checkForErrors implFileTypePriorToSig cenv env m + // Check completion of all classes defined across this file. // NOTE: this is not a great technique if inner signatures are permitted to hide // virtual dispatch slots. conditionallySuppressErrorReporting (checkForErrors()) (fun () -> try implFileTypePriorToSig |> IterTyconsOfModuleOrNamespaceType (fun tycon -> + //AugmentTypeDefinitions.MakeValForNonNullableToStringOverride g FinalTypeDefinitionChecksAtEndOfInferenceScope (cenv.infoReader, envAtEnd.NameEnv, cenv.tcSink, true, denvAtEnd, tycon)) with RecoverableException exn -> @@ -5882,12 +5898,13 @@ let CheckOneSigFile (g, amap, thisCcu, checkForErrors, conditionalDefines, tcSin tcComputationExpression=TcComputationExpression) let envinner, moduleTyAcc = MakeInitialEnv tcEnv - + let m = sigFile.QualifiedName.Range let specs = [ for x in sigFile.Contents -> SynModuleSigDecl.NamespaceFragment x ] - let! tcEnv = TcSignatureElements cenv ParentNone sigFile.QualifiedName.Range envinner PreXmlDoc.Empty None specs + let! tcEnv = TcSignatureElements cenv ParentNone m envinner PreXmlDoc.Empty None specs let sigFileType = moduleTyAcc.Value + ChangeFsharpTypesToNonNullToString g checkForErrors sigFileType cenv tcEnv m if not (checkForErrors()) then try sigFileType |> IterTyconsOfModuleOrNamespaceType (fun tycon -> diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs index 03b1273dd8a..e48aeaf5b27 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs @@ -9,6 +9,7 @@ let withNullnessOptions cu = |> withWarnOn 3261 |> withWarnOn 3262 |> withNoWarn 52 //The value has been copied to ensure the original.. + |> withNoWarn 60 // Override implementations in augmentations are now deprecated... |> withOptions ["--warnaserror+"] let typeCheckWithStrictNullness cu = @@ -305,7 +306,7 @@ let duWithExtensionProvidedToString = """A | B module Functions = let printMyType (x:MyCustomType) : string = "xxx" type MyCustomType with - override x.ToString() : string = WhatEver.print x + override x.ToString() : string = "hi" """ [] From ee3a20185dafbf148942ef24c044b6a4bb4ab9a4 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 22 Aug 2024 13:05:58 +0200 Subject: [PATCH 04/10] ToString on F#-controled types treated as returning a non-nullable string --- src/Compiler/Checking/AccessibilityLogic.fs | 3 +- src/Compiler/Checking/AttributeChecking.fs | 6 +- .../Checking/AugmentWithHashCompare.fs | 26 ++-- .../Checking/AugmentWithHashCompare.fsi | 2 +- src/Compiler/Checking/CheckDeclarations.fs | 38 +++--- src/Compiler/Checking/ConstraintSolver.fs | 2 + .../Checking/Expressions/CheckExpressions.fs | 2 + src/Compiler/Checking/InfoReader.fs | 3 +- src/Compiler/Checking/MethodCalls.fs | 23 +++- src/Compiler/Checking/MethodOverrides.fs | 6 + src/Compiler/Checking/NameResolution.fs | 3 +- src/Compiler/Checking/NicePrint.fs | 8 +- src/Compiler/Checking/infos.fs | 114 +++++++++++++----- src/Compiler/Checking/infos.fsi | 3 + src/Compiler/FSComp.txt | 1 + src/Compiler/Symbols/SymbolHelpers.fs | 3 +- src/Compiler/xlf/FSComp.txt.cs.xlf | 5 + src/Compiler/xlf/FSComp.txt.de.xlf | 5 + src/Compiler/xlf/FSComp.txt.es.xlf | 5 + src/Compiler/xlf/FSComp.txt.fr.xlf | 5 + src/Compiler/xlf/FSComp.txt.it.xlf | 5 + src/Compiler/xlf/FSComp.txt.ja.xlf | 5 + src/Compiler/xlf/FSComp.txt.ko.xlf | 5 + src/Compiler/xlf/FSComp.txt.pl.xlf | 5 + src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 5 + src/Compiler/xlf/FSComp.txt.ru.xlf | 5 + src/Compiler/xlf/FSComp.txt.tr.xlf | 5 + src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 5 + src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 5 + .../EmittedIL/Nullness/AnonRecords.fs | 4 +- .../Nullness/AnonRecords.fs.il.net472.bsl | 24 ++++ .../Nullness/AnonRecords.fs.il.netcore.bsl | 24 ++++ .../EmittedIL/Nullness/Records.fs | 4 +- .../Nullness/Records.fs.il.net472.bsl | 10 ++ .../Nullness/Records.fs.il.netcore.bsl | 11 ++ .../EmittedIL/Nullness/ReferenceDU.fs | 2 + .../Nullness/ReferenceDU.fs.il.net472.bsl | 10 ++ .../Nullness/ReferenceDU.fs.il.netcore.bsl | 10 ++ .../EmittedIL/Nullness/StructDU.fs | 8 ++ .../Nullness/StructDU.fs.il.net472.bsl | 96 +++++++-------- .../Nullness/StructDU.fs.il.netcore.bsl | 40 ++++-- .../Nullness/NullableReferenceTypesTests.fs | 41 ++++--- 42 files changed, 442 insertions(+), 150 deletions(-) diff --git a/src/Compiler/Checking/AccessibilityLogic.fs b/src/Compiler/Checking/AccessibilityLogic.fs index 052befa7a0c..b8acf9c9202 100644 --- a/src/Compiler/Checking/AccessibilityLogic.fs +++ b/src/Compiler/Checking/AccessibilityLogic.fs @@ -351,9 +351,10 @@ let CheckILFieldInfoAccessible g amap m ad finfo = /// when calling x.SomeMethod() we need to use 'adTyp' do verify that type of x is accessible from C /// and 'ad' to determine accessibility of SomeMethod. /// I.e when calling x.Public() and x.Protected() -in both cases first check should succeed and second - should fail in the latter one. -let IsTypeAndMethInfoAccessible amap m accessDomainTy ad = function +let rec IsTypeAndMethInfoAccessible amap m accessDomainTy ad = function | ILMeth (g, x, _) -> IsILMethInfoAccessible g amap m accessDomainTy ad x | FSMeth (_, _, vref, _) -> IsValAccessible ad vref + | MethInfoWithModifiedReturnType(mi,_) -> IsTypeAndMethInfoAccessible amap m accessDomainTy ad mi | DefaultStructCtor(g, ty) -> IsTypeAccessible g amap m ad ty #if !NO_TYPEPROVIDERS | ProvidedMeth(amap, tpmb, _, m) as etmi -> diff --git a/src/Compiler/Checking/AttributeChecking.fs b/src/Compiler/Checking/AttributeChecking.fs index 1dd832ebb3e..5d449193979 100644 --- a/src/Compiler/Checking/AttributeChecking.fs +++ b/src/Compiler/Checking/AttributeChecking.fs @@ -152,10 +152,11 @@ let GetAttribInfosOfEntity g amap m (tcref:TyconRef) = tcref.Attribs |> List.map (fun a -> FSAttribInfo (g, a)) -let GetAttribInfosOfMethod amap m minfo = +let rec GetAttribInfosOfMethod amap m minfo = match minfo with | ILMeth (g, ilminfo, _) -> ilminfo.RawMetadata.CustomAttrs |> AttribInfosOfIL g amap ilminfo.MetadataScope m | FSMeth (g, _, vref, _) -> vref.Attribs |> AttribInfosOfFS g + | MethInfoWithModifiedReturnType(mi,_) -> GetAttribInfosOfMethod amap m mi | DefaultStructCtor _ -> [] #if !NO_TYPEPROVIDERS // TODO: provided attributes @@ -186,11 +187,12 @@ let GetAttribInfosOfEvent amap m einfo = /// Analyze three cases for attributes declared on methods: IL-declared attributes, F#-declared attributes and /// provided attributes. -let BindMethInfoAttributes m minfo f1 f2 f3 = +let rec BindMethInfoAttributes m minfo f1 f2 f3 = ignore m; ignore f3 match minfo with | ILMeth (_, x, _) -> f1 x.RawMetadata.CustomAttrs | FSMeth (_, _, vref, _) -> f2 vref.Attribs + | MethInfoWithModifiedReturnType(mi,_) -> BindMethInfoAttributes m mi f1 f2 f3 | DefaultStructCtor _ -> f2 [] #if !NO_TYPEPROVIDERS | ProvidedMeth (_, mi, _, _) -> f3 (mi.PApply((fun st -> (st :> IProvidedCustomAttributeProvider)), m)) diff --git a/src/Compiler/Checking/AugmentWithHashCompare.fs b/src/Compiler/Checking/AugmentWithHashCompare.fs index 57f60e6b2c5..a6b2f5fda5b 100644 --- a/src/Compiler/Checking/AugmentWithHashCompare.fs +++ b/src/Compiler/Checking/AugmentWithHashCompare.fs @@ -81,8 +81,8 @@ let mkGetHashCodeSlotSig (g: TcGlobals) = let mkEqualsSlotSig (g: TcGlobals) = TSlotSig("Equals", g.obj_ty_noNulls, [], [], [ [ TSlotParam(Some("obj"), g.obj_ty_withNulls, false, false, false, []) ] ], Some g.bool_ty) -let mkToStringSlotSig (g: TcGlobals) = - TSlotSig("ToString", g.obj_ty_noNulls, [], [], [ [] ], Some g.string_ty) +//let mkToStringSlotSig (g: TcGlobals) = +// TSlotSig("ToString", g.obj_ty_noNulls, [], [], [ [] ], Some g.string_ty) //------------------------------------------------------------------------- // Helpers associated with code-generation of comparison/hash augmentations @@ -115,8 +115,8 @@ let mkEqualsWithComparerTyExact g ty = let mkHashTy g ty = mkFunTy g (mkThisTy g ty) (mkFunTy g g.unit_ty g.int_ty) -let mkToStringTy g ty = - mkFunTy g (mkThisTy g ty) (mkFunTy g g.unit_ty g.string_ty) +//let mkToStringTy g ty = +// mkFunTy g (mkThisTy g ty) (mkFunTy g g.unit_ty g.string_ty) let mkHashWithComparerTy g ty = mkFunTy g (mkThisTy g ty) (mkFunTy g g.IEqualityComparer_ty g.int_ty) @@ -1698,15 +1698,15 @@ let MakeValsForUnionAugmentation g (tcref: TyconRef) = g.AddValGeneratedAttributes v m v) -let MakeValForNonNullableToStringOverride (g:TcGlobals) (tcref: TyconRef) = - if g.checkNullness && not (tcref.HasOverride g "ToString" []) && (tcref.IsRecordTycon || tcref.IsUnionTycon) then - let _, tmty = mkMinimalTy g tcref - let tps = tcref.Typars tcref.Range - let vis = tcref.TypeReprAccessibility - mkValSpec g tcref tmty vis (Some(mkToStringSlotSig g)) "ToString" (tps +-> (mkToStringTy g tmty)) unitArg false - |> List.singleton - else - [] +//let MakeValForNonNullableToStringOverride (g:TcGlobals) (tcref: TyconRef) = +// if g.checkNullness && not (tcref.HasOverride g "ToString" []) && (tcref.IsRecordTycon || tcref.IsUnionTycon) then +// let _, tmty = mkMinimalTy g tcref +// let tps = tcref.Typars tcref.Range +// let vis = tcref.TypeReprAccessibility +// mkValSpec g tcref tmty vis (Some(mkToStringSlotSig g)) "ToString" (tps +-> (mkToStringTy g tmty)) unitArg false +// |> List.singleton +// else +// [] let MakeBindingsForUnionAugmentation g (tycon: Tycon) (vals: ValRef list) = let tcref = mkLocalTyconRef tycon diff --git a/src/Compiler/Checking/AugmentWithHashCompare.fsi b/src/Compiler/Checking/AugmentWithHashCompare.fsi index 578341abe0a..4551c84b9a7 100644 --- a/src/Compiler/Checking/AugmentWithHashCompare.fsi +++ b/src/Compiler/Checking/AugmentWithHashCompare.fsi @@ -44,6 +44,6 @@ val TypeDefinitelyHasEquality: TcGlobals -> TType -> bool val MakeValsForUnionAugmentation: TcGlobals -> TyconRef -> Val list -val MakeValForNonNullableToStringOverride: TcGlobals -> TyconRef -> Val list +//val MakeValForNonNullableToStringOverride: TcGlobals -> TyconRef -> Val list val MakeBindingsForUnionAugmentation: TcGlobals -> Tycon -> ValRef list -> Binding list diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index fd6969b0ba2..8b70759574b 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -4521,6 +4521,18 @@ module TcDeclarations = //------------------------------------------------------------------------- + //let ChangeFsharpTypesToNonNullToString (g:TcGlobals) (tycons:Entity list) cenv env m = + // if g.checkNullness then + // try + // tycons |> List.iter (fun tycon -> + // if tycon.IsRecordTycon || tycon.IsUnionTycon then + // let tcref = mkLocalTyconRef tycon + // AugmentTypeDefinitions.MakeValForNonNullableToStringOverride g tcref + // |> List.iter (fun v -> PublishValueDefnMaybeInclCompilerGenerated cenv env true ModuleOrMemberBinding v)) + + // with RecoverableException exn -> + // errorRecovery exn m + /// Bind a collection of mutually recursive definitions in an implementation file let TcMutRecDefinitions (cenv: cenv) envInitial parent typeNames tpenv m scopem mutRecNSInfo (mutRecDefns: MutRecDefnsInitialData) isMutRec = @@ -4659,6 +4671,8 @@ module TcDeclarations = // Check for cyclic structs and inheritance all over again, since we may have added some fields to the struct when generating the implicit construction syntax EstablishTypeDefinitionCores.TcTyconDefnCore_CheckForCyclicStructsAndInheritance cenv tycons + //ChangeFsharpTypesToNonNullToString g tycons cenv envFinal m + withExtraBindings, envFinal @@ -4741,6 +4755,7 @@ module TcDeclarations = let envForTycon = MakeInnerEnvForTyconRef envForTycon tcref (declKind = ExtrinsicExtensionBinding) let vals, env = TcTyconMemberSpecs cenv envForTycon (TyconContainerInfo(innerParent, tcref, declaredTyconTypars, NoSafeInitInfo)) declKind tpenv members + //ChangeFsharpTypesToNonNullToString g [tcref.Deref] cenv envForTycon m if not(cenv.g.langVersion.SupportsFeature(LanguageFeature.CSharpExtensionAttributeNotRequired)) then vals, env else @@ -5699,19 +5714,6 @@ let MakeInitialEnv env = let moduleTyAcc = ref (Construct.NewEmptyModuleOrNamespaceType (Namespace false)) { env with eModuleOrNamespaceTypeAccumulator = moduleTyAcc }, moduleTyAcc -let ChangeFsharpTypesToNonNullToString (g:TcGlobals) checkForErrors moduleOrNsContents cenv env m = - if g.checkNullness then - conditionallySuppressErrorReporting (checkForErrors()) (fun () -> - try - moduleOrNsContents |> IterTyconsOfModuleOrNamespaceType (fun tycon -> - if tycon.IsRecordTycon || tycon.IsUnionTycon then - let tcref = mkLocalTyconRef tycon - AugmentTypeDefinitions.MakeValForNonNullableToStringOverride g tcref - |> List.iter (fun v -> PublishValueDefnMaybeInclCompilerGenerated cenv env true ModuleOrMemberBinding v)) - - with RecoverableException exn -> - errorRecovery exn m) - /// Check an entire implementation file /// Typecheck, then close the inference scope and then check the file meets its signature (if any) let CheckOneImplFile @@ -5787,15 +5789,12 @@ let CheckOneImplFile conditionallySuppressErrorReporting (checkForErrors()) (fun () -> ApplyDefaults cenv g denvAtEnd m moduleContents extraAttribs) - ChangeFsharpTypesToNonNullToString g checkForErrors implFileTypePriorToSig cenv env m - // Check completion of all classes defined across this file. // NOTE: this is not a great technique if inner signatures are permitted to hide // virtual dispatch slots. conditionallySuppressErrorReporting (checkForErrors()) (fun () -> try - implFileTypePriorToSig |> IterTyconsOfModuleOrNamespaceType (fun tycon -> - //AugmentTypeDefinitions.MakeValForNonNullableToStringOverride g + implFileTypePriorToSig |> IterTyconsOfModuleOrNamespaceType (fun tycon -> FinalTypeDefinitionChecksAtEndOfInferenceScope (cenv.infoReader, envAtEnd.NameEnv, cenv.tcSink, true, denvAtEnd, tycon)) with RecoverableException exn -> @@ -5902,9 +5901,8 @@ let CheckOneSigFile (g, amap, thisCcu, checkForErrors, conditionalDefines, tcSin let specs = [ for x in sigFile.Contents -> SynModuleSigDecl.NamespaceFragment x ] let! tcEnv = TcSignatureElements cenv ParentNone m envinner PreXmlDoc.Empty None specs - let sigFileType = moduleTyAcc.Value - - ChangeFsharpTypesToNonNullToString g checkForErrors sigFileType cenv tcEnv m + let sigFileType = moduleTyAcc.Value + if not (checkForErrors()) then try sigFileType |> IterTyconsOfModuleOrNamespaceType (fun tycon -> diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index 7371a8ece4d..600fce6368c 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -2132,6 +2132,8 @@ and MemberConstraintSolutionOfMethInfo css m minfo minst staticTyOpt = | FSMeth(_, ty, vref, _) -> FSMethSln(ty, vref, minst, staticTyOpt) + | MethInfoWithModifiedReturnType(mi,_) -> MemberConstraintSolutionOfMethInfo css m mi minst staticTyOpt + | MethInfo.DefaultStructCtor _ -> error(InternalError("the default struct constructor was the unexpected solution to a trait constraint", m)) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 8789e92797c..5c26ba3b868 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -11676,6 +11676,8 @@ and ApplyAbstractSlotInference (cenv: cenv) (envinner: TcEnv) (_: Val option) (a let declaredTypars = (if typarsFromAbsSlotAreRigid then typarsFromAbsSlot else declaredTypars) + let retTyFromAbsSlot = retTyFromAbsSlot |> changeWithNullReqTyToVariable g + let absSlotTy = mkMethodTy g argTysFromAbsSlot retTyFromAbsSlot UnifyTypes cenv envinner m argsAndRetTy absSlotTy diff --git a/src/Compiler/Checking/InfoReader.fs b/src/Compiler/Checking/InfoReader.fs index 26b7d25c843..aaa13019622 100644 --- a/src/Compiler/Checking/InfoReader.fs +++ b/src/Compiler/Checking/InfoReader.fs @@ -1191,7 +1191,7 @@ let GetXmlDocSigOfUnionCaseRef (ucref: UnionCaseRef) = ucref.UnionCase.XmlDocSig <- XmlDocSigOfUnionCase [tcref.CompiledRepresentationForNamedType.FullName; ucref.CaseName] Some (ccuFileName, ucref.UnionCase.XmlDocSig) -let GetXmlDocSigOfMethInfo (infoReader: InfoReader) m (minfo: MethInfo) = +let rec GetXmlDocSigOfMethInfo (infoReader: InfoReader) m (minfo: MethInfo) = let amap = infoReader.amap match minfo with | FSMeth (g, _, vref, _) -> @@ -1219,6 +1219,7 @@ let GetXmlDocSigOfMethInfo (infoReader: InfoReader) m (minfo: MethInfo) = Some (ccuFileName, "M:"+actualTypeName+"."+normalizedName+genericArity+XmlDocArgsEnc g (formalTypars, fmtps) args) + | MethInfoWithModifiedReturnType(mi,_) -> GetXmlDocSigOfMethInfo infoReader m mi | DefaultStructCtor(g, ty) -> match tryTcrefOfAppTy g ty with | ValueSome tcref -> diff --git a/src/Compiler/Checking/MethodCalls.fs b/src/Compiler/Checking/MethodCalls.fs index 878c5099573..0ed430f1d74 100644 --- a/src/Compiler/Checking/MethodCalls.fs +++ b/src/Compiler/Checking/MethodCalls.fs @@ -523,6 +523,18 @@ type CalledMeth<'T> staticTyOpt: TType option) = let g = infoReader.g + + let minfo = + match callerObjArgTys,minfo with + | objTy :: [], ILMeth _ when + g.checkNullness + && minfo.DisplayName = "ToString" + && (isAnonRecdTy g objTy || isRecdTy g objTy || isUnionTy g objTy) + && ( typeEquiv g g.obj_ty_noNulls minfo.ApparentEnclosingAppType + || typeEquiv g g.system_Value_ty minfo.ApparentEnclosingAppType) -> + MethInfoWithModifiedReturnType(minfo, g.string_ty) + | _ -> minfo + let methodRetTy = if minfo.IsConstructor then minfo.ApparentEnclosingType else minfo.GetFSharpReturnType(infoReader.amap, m, calledTyArgs) let fullCurriedCalledArgs = MakeCalledArgs infoReader.amap m minfo calledTyArgs @@ -1039,7 +1051,7 @@ let BuildFSharpMethodCall g m (ty, vref: ValRef) valUseFlags minst args = /// Make a call to a method info. Used by the optimizer and code generator to build /// calls to the type-directed solutions to member constraints. -let MakeMethInfoCall (amap: ImportMap) m (minfo: MethInfo) minst args staticTyOpt = +let rec MakeMethInfoCall (amap: ImportMap) m (minfo: MethInfo) minst args staticTyOpt = let g = amap.g let ccallInfo = ComputeConstrainedCallInfo g amap m staticTyOpt args minfo let valUseFlags = @@ -1059,6 +1071,8 @@ let MakeMethInfoCall (amap: ImportMap) m (minfo: MethInfo) minst args staticTyOp | FSMeth(g, ty, vref, _) -> BuildFSharpMethodCall g m (ty, vref) valUseFlags minst args |> fst + | MethInfoWithModifiedReturnType(mi,_) -> MakeMethInfoCall amap m mi minst args staticTyOpt + | DefaultStructCtor(_, ty) -> mkDefault (m, ty) @@ -1108,7 +1122,7 @@ let TryImportProvidedMethodBaseAsLibraryIntrinsic (amap: Import.ImportMap, m: ra // minst: the instantiation to apply for a generic method // objArgs: the 'this' argument, if any // args: the arguments, if any -let BuildMethodCall tcVal g amap isMutable m isProp minfo valUseFlags minst objArgs args staticTyOpt = +let rec BuildMethodCall tcVal g amap isMutable m isProp minfo valUseFlags minst objArgs args staticTyOpt = let direct = IsBaseCall objArgs TakeObjAddrForMethodCall g amap minfo isMutable m staticTyOpt objArgs (fun ccallInfo objArgs -> @@ -1181,6 +1195,11 @@ let BuildMethodCall tcVal g amap isMutable m isProp minfo valUseFlags minst objA let vExpr, vExprTy = tcVal vref valUseFlags (minfo.DeclaringTypeInst @ minst) m BuildFSharpMethodApp g m vref vExpr vExprTy allArgs + | MethInfoWithModifiedReturnType(mi,retTy) -> + let expr, exprTy = BuildMethodCall tcVal g amap isMutable m isProp mi valUseFlags minst objArgs args staticTyOpt + let expr = mkCoerceExpr(expr, retTy, m, exprTy) + expr, retTy + // Build a 'call' to a struct default constructor | DefaultStructCtor (g, ty) -> if g.langFeatureNullness && g.checkNullness then diff --git a/src/Compiler/Checking/MethodOverrides.fs b/src/Compiler/Checking/MethodOverrides.fs index acec41cd479..5635b0d965b 100644 --- a/src/Compiler/Checking/MethodOverrides.fs +++ b/src/Compiler/Checking/MethodOverrides.fs @@ -148,6 +148,12 @@ module DispatchSlotChecking = let _, _, argInfos, retTy, _ = GetTypeOfMemberInMemberForm g overrideBy let nm = overrideBy.LogicalName + if g.checkNullness && nm = "ToString" && (argInfos |> List.sumBy _.Length) = 0 && retTy.IsSome then + let returnsString = typeEquiv g retTy.Value g.string_ty + let retTyNullness = (nullnessOfTy g retTy.Value).TryEvaluate() + if returnsString && retTyNullness = ValueSome(NullnessInfo.WithNull) then + warning(Error(FSComp.SR.tcNullableToStringOverride(), overrideBy.Range)) + let argTys = argInfos |> List.mapSquared fst let memberMethodTypars, memberToParentInst, argTys, retTy = diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index f1945c80928..0caa889f8f4 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -678,13 +678,14 @@ let IntrinsicMethInfosOfType (infoReader: InfoReader) optFilter ad allowMultiInt let minfos = minfos |> ExcludeHiddenOfMethInfos g amap m minfos -let TrySelectExtensionMethInfoOfILExtMem m amap apparentTy (actualParent, minfo, pri) = +let rec TrySelectExtensionMethInfoOfILExtMem m amap apparentTy (actualParent, minfo, pri) = match minfo with | ILMeth(_,ilminfo,_) -> MethInfo.CreateILExtensionMeth (amap, m, apparentTy, actualParent, Some pri, ilminfo.RawMetadata) |> Some // F#-defined IL-style extension methods are not seen as extension methods in F# code | FSMeth(g,_,vref,_) -> FSMeth(g, apparentTy, vref, Some pri) |> Some + | MethInfoWithModifiedReturnType(mi,_) -> TrySelectExtensionMethInfoOfILExtMem m amap apparentTy (actualParent, mi, pri) #if !NO_TYPEPROVIDERS // // Provided extension methods are not yet supported | ProvidedMeth(amap,providedMeth,_,m) -> diff --git a/src/Compiler/Checking/NicePrint.fs b/src/Compiler/Checking/NicePrint.fs index f954e38b6fd..0fd772bc7fc 100644 --- a/src/Compiler/Checking/NicePrint.fs +++ b/src/Compiler/Checking/NicePrint.fs @@ -1734,7 +1734,7 @@ module InfoMemberPrinting = // // For C# extension members: // ApparentContainer.Method(argName1: argType1, ..., argNameN: argTypeN) : retType - let prettyLayoutOfMethInfoFreeStyle (infoReader: InfoReader) m denv typarInst methInfo = + let rec prettyLayoutOfMethInfoFreeStyle (infoReader: InfoReader) m denv typarInst methInfo = let amap = infoReader.amap match methInfo with @@ -1745,6 +1745,12 @@ module InfoMemberPrinting = | FSMeth(_, _, vref, _) -> let prettyTyparInst, resL = PrintTastMemberOrVals.prettyLayoutOfValOrMember { denv with showMemberContainers=true } infoReader typarInst vref prettyTyparInst, resL + | MethInfoWithModifiedReturnType(ILMeth(_, ilminfo, _) as wrappedInfo,retTy) -> + let prettyTyparInst, prettyMethInfo, minst = prettifyILMethInfo amap m wrappedInfo typarInst ilminfo + let prettyMethInfo = MethInfoWithModifiedReturnType(prettyMethInfo,retTy) + let resL = layoutMethInfoCSharpStyle amap m denv prettyMethInfo minst + prettyTyparInst, resL + | MethInfoWithModifiedReturnType(mi,_) -> prettyLayoutOfMethInfoFreeStyle infoReader m denv typarInst mi | ILMeth(_, ilminfo, _) -> let prettyTyparInst, prettyMethInfo, minst = prettifyILMethInfo amap m methInfo typarInst ilminfo let resL = layoutMethInfoCSharpStyle amap m denv prettyMethInfo minst diff --git a/src/Compiler/Checking/infos.fs b/src/Compiler/Checking/infos.fs index 74dc1c437fd..eec961e78dc 100644 --- a/src/Compiler/Checking/infos.fs +++ b/src/Compiler/Checking/infos.fs @@ -668,6 +668,9 @@ type MethInfo = /// Describes a use of a method backed by Abstract IL # metadata | ILMeth of tcGlobals: TcGlobals * ilMethInfo: ILMethInfo * extensionMethodPriority: ExtensionMethodPriority option + /// A pseudo-method used by F# typechecker to treat Object.ToString() of known types as returning regular string, not `string?` as in the BCL + | MethInfoWithModifiedReturnType of original:MethInfo * modifiedReturnType: TType + /// Describes a use of a pseudo-method corresponding to the default constructor for a .NET struct type | DefaultStructCtor of tcGlobals: TcGlobals * structTy: TType @@ -684,6 +687,7 @@ type MethInfo = match x with | ILMeth(_, ilminfo, _) -> ilminfo.ApparentEnclosingType | FSMeth(_, ty, _, _) -> ty + | MethInfoWithModifiedReturnType(mi, _) -> mi.ApparentEnclosingType | DefaultStructCtor(_, ty) -> ty #if !NO_TYPEPROVIDERS | ProvidedMeth(amap, mi, _, m) -> @@ -704,6 +708,7 @@ type MethInfo = match x with | ILMeth(_, ilminfo, _) when x.IsExtensionMember -> ilminfo.DeclaringTyconRef | FSMeth(_, _, vref, _) when x.IsExtensionMember && vref.HasDeclaringEntity -> vref.DeclaringEntity + | MethInfoWithModifiedReturnType(mi, _) -> mi.DeclaringTyconRef | _ -> x.ApparentEnclosingTyconRef /// Get the information about provided static parameters, if any @@ -711,6 +716,7 @@ type MethInfo = match x with | ILMeth _ -> None | FSMeth _ -> None + | MethInfoWithModifiedReturnType _ -> None #if !NO_TYPEPROVIDERS | ProvidedMeth (_, mb, _, m) -> let staticParams = mb.PApplyWithProvider((fun (mb, provider) -> mb.GetStaticParametersForMethod provider), range=m) @@ -729,6 +735,7 @@ type MethInfo = #if !NO_TYPEPROVIDERS | ProvidedMeth(_, _, pri, _) -> pri #endif + | MethInfoWithModifiedReturnType(mi, _) -> mi.ExtensionMemberPriorityOption | DefaultStructCtor _ -> None /// Get the extension method priority of the method. If it is not an extension method @@ -742,6 +749,7 @@ type MethInfo = | ILMeth(_, y, _) -> y.DeclaringTyconRef.DisplayNameWithStaticParametersAndUnderscoreTypars + "::" + y.ILName | FSMeth(_, AbbrevOrAppTy(tcref, _), vref, _) -> tcref.DisplayNameWithStaticParametersAndUnderscoreTypars + "::" + vref.LogicalName | FSMeth(_, _, vref, _) -> "??::" + vref.LogicalName + | MethInfoWithModifiedReturnType(mi, returnTy) -> mi.DebuggerDisplayName + $"({returnTy.DebugText})" #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi, _, m) -> "ProvidedMeth: " + mi.PUntaint((fun mi -> mi.Name), m) #endif @@ -752,6 +760,7 @@ type MethInfo = match x with | ILMeth(_, y, _) -> y.ILName | FSMeth(_, _, vref, _) -> vref.LogicalName + | MethInfoWithModifiedReturnType(mi, _) -> mi.LogicalName #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi, _, m) -> mi.PUntaint((fun mi -> mi.Name), m) #endif @@ -791,6 +800,7 @@ type MethInfo = match x with | ILMeth(g, _, _) -> g | FSMeth(g, _, _, _) -> g + | MethInfoWithModifiedReturnType(mi, _) -> mi.TcGlobals | DefaultStructCtor (g, _) -> g #if !NO_TYPEPROVIDERS | ProvidedMeth(amap, _, _, _) -> amap.g @@ -806,6 +816,7 @@ type MethInfo = let ty = x.ApparentEnclosingAppType let _, memberMethodTypars, _, _ = AnalyzeTypeOfMemberVal x.IsCSharpStyleExtensionMember g (ty, vref) memberMethodTypars + | MethInfoWithModifiedReturnType(mi, _) -> mi.FormalMethodTypars | DefaultStructCtor _ -> [] #if !NO_TYPEPROVIDERS | ProvidedMeth _ -> [] // There will already have been an error if there are generic parameters here. @@ -821,6 +832,7 @@ type MethInfo = match x with | ILMeth _ -> XmlDoc.Empty | FSMeth(_, _, vref, _) -> vref.XmlDoc + | MethInfoWithModifiedReturnType(mi, _) -> mi.XmlDoc | DefaultStructCtor _ -> XmlDoc.Empty #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi, _, m)-> @@ -832,6 +844,7 @@ type MethInfo = member x.ArbitraryValRef = match x with | FSMeth(_g, _, vref, _) -> Some vref + | MethInfoWithModifiedReturnType(mi, _) -> mi.ArbitraryValRef | _ -> None /// Get a list of argument-number counts, one count for each set of curried arguments. @@ -841,6 +854,7 @@ type MethInfo = match x with | ILMeth(_, ilminfo, _) -> [ilminfo.NumParams] | FSMeth(g, _, vref, _) -> GetArgInfosOfMember x.IsCSharpStyleExtensionMember g vref |> List.map List.length + | MethInfoWithModifiedReturnType(mi, _) -> mi.NumArgs | DefaultStructCtor _ -> [0] #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi, _, m) -> [mi.PUntaint((fun mi -> mi.GetParameters().Length), m)] // Why is this a list? Answer: because the method might be curried @@ -862,6 +876,7 @@ type MethInfo = match x with | ILMeth(_, ilmeth, _) -> ilmeth.IsInstance | FSMeth(_, _, vref, _) -> vref.IsInstanceMember || x.IsCSharpStyleExtensionMember + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsInstance | DefaultStructCtor _ -> false #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi, _, m) -> mi.PUntaint((fun mi -> not mi.IsConstructor && not mi.IsStatic), m) @@ -875,6 +890,7 @@ type MethInfo = match x with | ILMeth(_, ilmeth, _) -> ilmeth.IsProtectedAccessibility | FSMeth _ -> false + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsProtectedAccessibility | DefaultStructCtor _ -> false #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi, _, m) -> mi.PUntaint((fun mi -> mi.IsFamily), m) @@ -884,6 +900,7 @@ type MethInfo = match x with | ILMeth(_, ilmeth, _) -> ilmeth.IsVirtual | FSMeth(_, _, vref, _) -> vref.IsVirtualMember + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsVirtual | DefaultStructCtor _ -> false #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi, _, m) -> mi.PUntaint((fun mi -> mi.IsVirtual), m) @@ -893,6 +910,7 @@ type MethInfo = match x with | ILMeth(_, ilmeth, _) -> ilmeth.IsConstructor | FSMeth(_g, _, vref, _) -> (vref.MemberInfo.Value.MemberFlags.MemberKind = SynMemberKind.Constructor) + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsConstructor | DefaultStructCtor _ -> true #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi, _, m) -> mi.PUntaint((fun mi -> mi.IsConstructor), m) @@ -905,6 +923,7 @@ type MethInfo = match vref.TryDeref with | ValueSome x -> x.IsClassConstructor | _ -> false + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsClassConstructor | DefaultStructCtor _ -> false #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi, _, m) -> mi.PUntaint((fun mi -> mi.IsConstructor && mi.IsStatic), m) // Note: these are never public anyway @@ -914,6 +933,7 @@ type MethInfo = match x with | ILMeth(_g, ilmeth, _) -> ilmeth.IsVirtual | FSMeth(_, _, vref, _) -> vref.MemberInfo.Value.MemberFlags.IsDispatchSlot + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsDispatchSlot | DefaultStructCtor _ -> false #if !NO_TYPEPROVIDERS | ProvidedMeth _ -> x.IsVirtual // Note: follow same implementation as ILMeth @@ -925,6 +945,7 @@ type MethInfo = match x with | ILMeth(_, ilmeth, _) -> ilmeth.IsFinal | FSMeth(_g, _, _vref, _) -> false + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsFinal | DefaultStructCtor _ -> true #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi, _, m) -> mi.PUntaint((fun mi -> mi.IsFinal), m) @@ -940,6 +961,7 @@ type MethInfo = match minfo with | ILMeth(_, ilmeth, _) -> ilmeth.IsAbstract | FSMeth(g, _, vref, _) -> isInterfaceTy g minfo.ApparentEnclosingType || vref.IsDispatchSlotMember + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsAbstract | DefaultStructCtor _ -> false #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi, _, m) -> mi.PUntaint((fun mi -> mi.IsAbstract), m) @@ -950,6 +972,7 @@ type MethInfo = (match x with | ILMeth(_, x, _) -> x.IsNewSlot || (isInterfaceTy x.TcGlobals x.ApparentEnclosingType && not x.IsFinal) | FSMeth(_, _, vref, _) -> vref.IsDispatchSlotMember + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsNewSlot #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi, _, m) -> mi.PUntaint((fun mi -> mi.IsHideBySig), m) // REVIEW: Check this is correct #endif @@ -959,6 +982,7 @@ type MethInfo = member x.IsILMethod = match x with | ILMeth _ -> true + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsILMethod | _ -> false /// Check if this method is an explicit implementation of an interface member @@ -966,6 +990,7 @@ type MethInfo = match x with | ILMeth _ -> false | FSMeth(g, _, vref, _) -> vref.IsFSharpExplicitInterfaceImplementation g + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsFSharpExplicitInterfaceImplementation | DefaultStructCtor _ -> false #if !NO_TYPEPROVIDERS | ProvidedMeth _ -> false @@ -976,6 +1001,7 @@ type MethInfo = match x with | ILMeth _ -> false | FSMeth(_, _, vref, _) -> vref.IsDefiniteFSharpOverrideMember + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsDefiniteFSharpOverride | DefaultStructCtor _ -> false #if !NO_TYPEPROVIDERS | ProvidedMeth _ -> false @@ -984,6 +1010,7 @@ type MethInfo = member x.ImplementedSlotSignatures = match x with | FSMeth(_, _, vref, _) -> vref.ImplementedSlotSignatures + | MethInfoWithModifiedReturnType(mi, _) -> mi.ImplementedSlotSignatures | _ -> failwith "not supported" /// Indicates if this is an extension member. @@ -991,6 +1018,7 @@ type MethInfo = match x with | FSMeth (_, _, vref, pri) -> pri.IsSome || vref.IsExtensionMember | ILMeth (_, _, Some _) -> true + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsExtensionMember | _ -> false /// Indicates if this is an extension member (e.g. on a struct) that takes a byref arg @@ -1002,12 +1030,16 @@ type MethInfo = /// Indicates if this is an F# extension member. member x.IsFSharpStyleExtensionMember = - match x with FSMeth (_, _, vref, _) -> vref.IsExtensionMember | _ -> false + match x with + | FSMeth (_, _, vref, _) -> vref.IsExtensionMember + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsFSharpStyleExtensionMember + | _ -> false /// Indicates if this is an C#-style extension member. member x.IsCSharpStyleExtensionMember = match x with | FSMeth (_, _, vref, Some _) -> not vref.IsExtensionMember + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsCSharpStyleExtensionMember | ILMeth (_, _, Some _) -> true | _ -> false @@ -1029,6 +1061,7 @@ type MethInfo = member x.IsFSharpEventPropertyMethod = match x with | FSMeth(g, _, vref, _) -> vref.IsFSharpEventProperty g + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsFSharpEventPropertyMethod #if !NO_TYPEPROVIDERS | ProvidedMeth _ -> false #endif @@ -1060,12 +1093,14 @@ type MethInfo = | ILMeth (g, ilMethInfo, _) -> ilMethInfo.IsReadOnly g || x.IsOnReadOnlyType | FSMeth _ -> false // F# defined methods not supported yet. Must be a language feature. + | MethInfoWithModifiedReturnType(mi, _) -> mi.IsReadOnly | _ -> false /// Indicates, wheter this method has `IsExternalInit` modreq. member x.HasExternalInit = match x with | ILMeth (_, ilMethInfo, _) -> HasExternalInit ilMethInfo.ILMethodRef + | MethInfoWithModifiedReturnType(mi, _) -> mi.HasExternalInit | _ -> false /// Indicates if this method is an extension member that is read-only. @@ -1099,6 +1134,8 @@ type MethInfo = match x1, x2 with | ILMeth(_, x1, _), ILMeth(_, x2, _) -> (x1.RawMetadata === x2.RawMetadata) | FSMeth(g, _, vref1, _), FSMeth(_, _, vref2, _) -> valRefEq g vref1 vref2 + | mi1, MethInfoWithModifiedReturnType(mi2, _) + | MethInfoWithModifiedReturnType(mi1, _), mi2 -> MethInfo.MethInfosUseIdenticalDefinitions mi1 mi2 | DefaultStructCtor _, DefaultStructCtor _ -> tyconRefEq x1.TcGlobals x1.DeclaringTyconRef x2.DeclaringTyconRef #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi1, _, _), ProvidedMeth(_, mi2, _, _) -> ProvidedMethodBase.TaintedEquals (mi1, mi2) @@ -1110,6 +1147,7 @@ type MethInfo = match x with | ILMeth(_, x1, _) -> hash x1.RawMetadata.Name | FSMeth(_, _, vref, _) -> hash vref.LogicalName + | MethInfoWithModifiedReturnType(mi,_) -> mi.ComputeHashCode() | DefaultStructCtor(_, _ty) -> 34892 // "ty" doesn't support hashing. We could use "hash (tcrefOfAppTy g ty).CompiledName" or // something but we don't have a "g" parameter here yet. But this hash need only be very approximate anyway #if !NO_TYPEPROVIDERS @@ -1124,6 +1162,7 @@ type MethInfo = | ILMethInfo(_, IlType ty, md, _) -> MethInfo.CreateILMeth(amap, m, instType inst ty.ToType, md) | ILMethInfo(_, CSharpStyleExtension(declaringTyconRef,ty), md, _) -> MethInfo.CreateILExtensionMeth(amap, m, instType inst ty, declaringTyconRef, pri, md) | FSMeth(g, ty, vref, pri) -> FSMeth(g, instType inst ty, vref, pri) + | MethInfoWithModifiedReturnType(mi, retTy) -> MethInfoWithModifiedReturnType(mi.Instantiate(amap, m, inst), retTy) | DefaultStructCtor(g, ty) -> DefaultStructCtor(g, instType inst ty) #if !NO_TYPEPROVIDERS | ProvidedMeth _ -> @@ -1142,6 +1181,7 @@ type MethInfo = let inst = GetInstantiationForMemberVal g x.IsCSharpStyleExtensionMember (ty, vref, minst) let _, _, retTy, _ = AnalyzeTypeOfMemberVal x.IsCSharpStyleExtensionMember g (ty, vref) retTy |> Option.map (instType inst) + | MethInfoWithModifiedReturnType(_,retTy) -> Some retTy | DefaultStructCtor _ -> None #if !NO_TYPEPROVIDERS | ProvidedMeth(amap, mi, _, m) -> @@ -1159,6 +1199,7 @@ type MethInfo = | ILMeth (ilMethInfo = ilminfo) -> // A single group of tupled arguments [ ilminfo.ParamMetadata |> List.map (fun x -> x.Name) ] + | MethInfoWithModifiedReturnType(mi,_) -> mi.GetParamNames() #if !NO_TYPEPROVIDERS | ProvidedMeth (_, mi, _, m) -> // A single group of tupled arguments @@ -1177,6 +1218,7 @@ type MethInfo = let paramTypes = ParamNameAndType.FromMember x.IsCSharpStyleExtensionMember g vref let inst = GetInstantiationForMemberVal g x.IsCSharpStyleExtensionMember (ty, vref, minst) paramTypes |> List.mapSquared (fun (ParamNameAndType(_, ty)) -> instType inst ty) + | MethInfoWithModifiedReturnType(mi,_) -> mi.GetParamTypes(amap,m,minst) | DefaultStructCtor _ -> [] #if !NO_TYPEPROVIDERS | ProvidedMeth(amap, mi, _, m) -> @@ -1201,6 +1243,7 @@ type MethInfo = else [ ty ] else [] + | MethInfoWithModifiedReturnType(mi,_) -> mi.GetObjArgTypes(amap, m, minst) | DefaultStructCtor _ -> [] #if !NO_TYPEPROVIDERS | ProvidedMeth(amap, mi, _, m) -> @@ -1212,6 +1255,7 @@ type MethInfo = member x.GetCustomAttrs() = match x with | ILMeth(_, ilMethInfo, _) -> ilMethInfo.RawMetadata.CustomAttrs + | MethInfoWithModifiedReturnType(mi,_) -> mi.GetCustomAttrs() | _ -> ILAttributes.Empty /// Get the parameter attributes of a method info, which get combined with the parameter names and types @@ -1252,6 +1296,7 @@ type MethInfo = | FSMeth(g, _, vref, _) -> GetArgInfosOfMember x.IsCSharpStyleExtensionMember g vref |> List.mapSquared (CrackParamAttribsInfo g) + | MethInfoWithModifiedReturnType(mi,_) -> mi.GetParamAttribs(amap, m) | DefaultStructCtor _ -> [[]] @@ -1294,6 +1339,7 @@ type MethInfo = |> List.mapSquared (map1Of2 (instType methodToParentRenaming) >> MakeSlotParam ) let formalRetTy = Option.map (instType methodToParentRenaming) retTy MakeSlotSig(x.LogicalName, x.ApparentEnclosingType, formalEnclosingTypars, formalMethTypars, formalParams, formalRetTy) + | MethInfoWithModifiedReturnType(mi,_) -> mi.GetSlotSig(amap, m) | DefaultStructCtor _ -> error(InternalError("no slotsig for DefaultStructCtor", m)) | _ -> let g = x.TcGlobals @@ -1346,37 +1392,41 @@ type MethInfo = /// Get the ParamData objects for the parameters of a MethInfo member x.GetParamDatas(amap, m, minst) = - let paramNamesAndTypes = - match x with - | ILMeth(_g, ilminfo, _) -> - [ ilminfo.GetParamNamesAndTypes(amap, m, minst) ] - | FSMeth(g, _, vref, _) -> - let ty = x.ApparentEnclosingAppType - let items = ParamNameAndType.FromMember x.IsCSharpStyleExtensionMember g vref - let inst = GetInstantiationForMemberVal g x.IsCSharpStyleExtensionMember (ty, vref, minst) - items |> ParamNameAndType.InstantiateCurried inst - | DefaultStructCtor _ -> - [[]] -#if !NO_TYPEPROVIDERS - | ProvidedMeth(amap, mi, _, _) -> - // A single set of tupled parameters - [ [for p in mi.PApplyArray((fun mi -> mi.GetParameters()), "GetParameters", m) do - let paramName = - match p.PUntaint((fun p -> p.Name), m) with - | "" -> None - | name -> Some (mkSynId m name) - let paramTy = - match p.PApply((fun p -> p.ParameterType), m) with - | Tainted.Null -> amap.g.unit_ty - | Tainted.NonNull parameterType -> ImportProvidedType amap m parameterType - yield ParamNameAndType(paramName, paramTy) ] ] - -#endif - - let paramAttribs = x.GetParamAttribs(amap, m) - (paramAttribs, paramNamesAndTypes) ||> List.map2 (List.map2 (fun info (ParamNameAndType(nmOpt, pty)) -> - let (ParamAttribs(isParamArrayArg, isInArg, isOutArg, optArgInfo, callerInfo, reflArgInfo)) = info - ParamData(isParamArrayArg, isInArg, isOutArg, optArgInfo, callerInfo, nmOpt, reflArgInfo, pty))) + match x with + | MethInfoWithModifiedReturnType(mi,_) -> mi.GetParamDatas(amap, m, minst) + | _ -> + let paramNamesAndTypes = + match x with + | ILMeth(_g, ilminfo, _) -> + [ ilminfo.GetParamNamesAndTypes(amap, m, minst) ] + | FSMeth(g, _, vref, _) -> + let ty = x.ApparentEnclosingAppType + let items = ParamNameAndType.FromMember x.IsCSharpStyleExtensionMember g vref + let inst = GetInstantiationForMemberVal g x.IsCSharpStyleExtensionMember (ty, vref, minst) + items |> ParamNameAndType.InstantiateCurried inst + | MethInfoWithModifiedReturnType(_mi,_) -> failwith "unreachable" + | DefaultStructCtor _ -> + [[]] +#if !NO_TYPEPROVIDERS + | ProvidedMeth(amap, mi, _, _) -> + // A single set of tupled parameters + [ [for p in mi.PApplyArray((fun mi -> mi.GetParameters()), "GetParameters", m) do + let paramName = + match p.PUntaint((fun p -> p.Name), m) with + | "" -> None + | name -> Some (mkSynId m name) + let paramTy = + match p.PApply((fun p -> p.ParameterType), m) with + | Tainted.Null -> amap.g.unit_ty + | Tainted.NonNull parameterType -> ImportProvidedType amap m parameterType + yield ParamNameAndType(paramName, paramTy) ] ] + +#endif + + let paramAttribs = x.GetParamAttribs(amap, m) + (paramAttribs, paramNamesAndTypes) ||> List.map2 (List.map2 (fun info (ParamNameAndType(nmOpt, pty)) -> + let (ParamAttribs(isParamArrayArg, isInArg, isOutArg, optArgInfo, callerInfo, reflArgInfo)) = info + ParamData(isParamArrayArg, isInArg, isOutArg, optArgInfo, callerInfo, nmOpt, reflArgInfo, pty))) /// Get the ParamData objects for the parameters of a MethInfo member x.HasParamArrayArg(amap, m, minst) = diff --git a/src/Compiler/Checking/infos.fsi b/src/Compiler/Checking/infos.fsi index 05e017ff144..ee693b75648 100644 --- a/src/Compiler/Checking/infos.fsi +++ b/src/Compiler/Checking/infos.fsi @@ -314,6 +314,9 @@ type MethInfo = /// Describes a use of a method backed by Abstract IL # metadata | ILMeth of tcGlobals: TcGlobals * ilMethInfo: ILMethInfo * extensionMethodPriority: ExtensionMethodPriority option + /// A pseudo-method used by F# typechecker to treat Object.ToString() of known types as returning regular string, not `string?` as in the BCL + | MethInfoWithModifiedReturnType of original:MethInfo * modifiedReturnType: TType + /// Describes a use of a pseudo-method corresponding to the default constructor for a .NET struct type | DefaultStructCtor of tcGlobals: TcGlobals * structTy: TType diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index e452d21f100..7418921078d 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1533,6 +1533,7 @@ tcPassingWithoutNullToValueOptionOfObj,"You can create 'ValueSome value' directl tcPassingWithoutNullToNonNullAP,"You can remove this |Null|NonNull| pattern usage." tcPassingWithoutNullToNonNullQuickAP,"You can remove this |NonNullQuick| pattern usage." tcPassingWithoutNullTononNullFunction,"You can remove this `nonNull` assertion." +3263,tcNullableToStringOverride,"With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function." 3268,csNullNotNullConstraintInconsistent,"The constraints 'null' and 'not null' are inconsistent" 3271,tcNullnessCheckingNotEnabled,"The 'nullness checking' language feature is not enabled. This use of a nullness checking construct will be ignored." csTypeHasNullAsTrueValue,"The type '%s' uses 'null' as a representation value but a non-null type is expected" diff --git a/src/Compiler/Symbols/SymbolHelpers.fs b/src/Compiler/Symbols/SymbolHelpers.fs index 0e5d0ff8475..1fedee2968a 100644 --- a/src/Compiler/Symbols/SymbolHelpers.fs +++ b/src/Compiler/Symbols/SymbolHelpers.fs @@ -798,7 +798,7 @@ module internal SymbolHelpers = /// Get the "F1 Keyword" associated with an item, for looking up documentation help indexes on the web let rec GetF1Keyword (g: TcGlobals) item = - let getKeywordForMethInfo (minfo : MethInfo) = + let rec getKeywordForMethInfo (minfo : MethInfo) = match minfo with | FSMeth(_, _, vref, _) -> match vref.TryDeclaringEntity with @@ -813,6 +813,7 @@ module internal SymbolHelpers = if nGenericParams > 0 then "``"+(nGenericParams.ToString()) else "" sprintf "%s.%s%s" typeString minfo.RawMetadata.Name paramString |> Some + | MethInfoWithModifiedReturnType(mi,_) -> getKeywordForMethInfo mi | DefaultStructCtor _ -> None #if !NO_TYPEPROVIDERS | ProvidedMeth _ -> None diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index d00017d2697..51cde20f6d3 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -1467,6 +1467,11 @@ Hodnota {0} není funkce a nepodporuje zápis indexu. + + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + + 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. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 2bf293d961c..23e5afff5f8 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -1467,6 +1467,11 @@ Der Wert "{0}" ist keine Funktion und unterstützt keine Indexnotation. + + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + + 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. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 6923afa068f..3d7d7280ba7 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -1467,6 +1467,11 @@ El valor "{0}" no es una función y no admite la notación de índices. + + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + + 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. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 4207041ad15..23dca25721f 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -1467,6 +1467,11 @@ La valeur « {0} » n’est pas une fonction et ne prend pas en charge la notation d’index. + + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + + 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. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 9c2171b9217..e6a33abcd90 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -1467,6 +1467,11 @@ Questo valore '{0}' non è una funzione e non supporta la notazione degli indici. + + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + + 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. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 3ac536db2b4..896510994bc 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -1467,6 +1467,11 @@ 値 '{0}' は関数ではなく、インデックス表記をサポートしていません。 + + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + + 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. diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 41047438142..2ad1622aba4 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -1467,6 +1467,11 @@ '{0}' 값은 함수가 아니며 인덱스 표기법을 지원하지 않습니다. + + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + + 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. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 9e64dbf33d8..cc5858eeecb 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -1467,6 +1467,11 @@ Wartość elementu „{0}” nie jest funkcją i nie obsługuje notacji indeksowej. + + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + + 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. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 59330e37600..c67ad810b78 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -1467,6 +1467,11 @@ O valor '{0}' não é uma função e não dá suporte à notação de índice. + + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + + 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. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 71995741697..f61f566816d 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -1467,6 +1467,11 @@ Значение {0} не является функцией и не поддерживает нотацию индекса. + + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + + 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. diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 4d6a7456a03..68f2493931d 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -1467,6 +1467,11 @@ “{0}” değeri bir işlev değildir ve dizin gösterimini desteklemez. + + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + + 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. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 6d942aa6526..b0f0a5d2dc4 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -1467,6 +1467,11 @@ 值 '{0}' 不是函数,不支持索引表示法。 + + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + + 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. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index fa8f6a00187..fe7bb1a3b0d 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -1467,6 +1467,11 @@ 值 '{0}' 並非函式,不支援索引標記法。 + + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function. + + 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. diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/AnonRecords.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/AnonRecords.fs index 1178f3e704e..0e1cbf437ca 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/AnonRecords.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/AnonRecords.fs @@ -11,4 +11,6 @@ let giveMeB () = {| A = justInt; B = justInt; C = justInt|} let giveMeC () = - {| A = maybeString; B = maybeString; C = maybeString|} \ No newline at end of file + {| A = maybeString; B = maybeString; C = maybeString|} + +let threeHappyStrings () : string array = [|giveMeA().ToString();giveMeB().ToString();giveMeC().ToString()|] \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/AnonRecords.fs.il.net472.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/AnonRecords.fs.il.net472.bsl index 9e4cf639944..6390383dbd8 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/AnonRecords.fs.il.net472.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/AnonRecords.fs.il.net472.bsl @@ -101,6 +101,30 @@ IL_0014: ret } + .method public static string[] threeHappyStrings() cil managed + { + + .maxstack 8 + IL_0000: ldc.i4.3 + IL_0001: newarr [runtime]System.String + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: call class '<>f__AnonymousType2430756162`3',int32> MyTestModule::giveMeA() + IL_000d: callvirt instance string [runtime]System.Object::ToString() + IL_0012: stelem [runtime]System.String + IL_0017: dup + IL_0018: ldc.i4.1 + IL_0019: call class '<>f__AnonymousType2430756162`3' MyTestModule::giveMeB() + IL_001e: callvirt instance string [runtime]System.Object::ToString() + IL_0023: stelem [runtime]System.String + IL_0028: dup + IL_0029: ldc.i4.2 + IL_002a: call class '<>f__AnonymousType2430756162`3' MyTestModule::giveMeC() + IL_002f: callvirt instance string [runtime]System.Object::ToString() + IL_0034: stelem [runtime]System.String + IL_0039: ret + } + .method private specialname rtspecialname static void .cctor() cil managed { diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/AnonRecords.fs.il.netcore.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/AnonRecords.fs.il.netcore.bsl index 127fe13bf97..67f941bd5bc 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/AnonRecords.fs.il.netcore.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/AnonRecords.fs.il.netcore.bsl @@ -101,6 +101,30 @@ IL_0014: ret } + .method public static string[] threeHappyStrings() cil managed + { + + .maxstack 8 + IL_0000: ldc.i4.3 + IL_0001: newarr [runtime]System.String + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: call class '<>f__AnonymousType2430756162`3',int32> MyTestModule::giveMeA() + IL_000d: callvirt instance string [runtime]System.Object::ToString() + IL_0012: stelem [runtime]System.String + IL_0017: dup + IL_0018: ldc.i4.1 + IL_0019: call class '<>f__AnonymousType2430756162`3' MyTestModule::giveMeB() + IL_001e: callvirt instance string [runtime]System.Object::ToString() + IL_0023: stelem [runtime]System.String + IL_0028: dup + IL_0029: ldc.i4.2 + IL_002a: call class '<>f__AnonymousType2430756162`3' MyTestModule::giveMeC() + IL_002f: callvirt instance string [runtime]System.Object::ToString() + IL_0034: stelem [runtime]System.String + IL_0039: ret + } + .method private specialname rtspecialname static void .cctor() cil managed { diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/Records.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/Records.fs index 2a2c7a09b7e..0fff3c6c6e0 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/Records.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/Records.fs @@ -22,4 +22,6 @@ let createAnInstance () = GenericNormalField = 42 GenericNullableField = maybeString GenericNotNullField = ""} - \ No newline at end of file + + +let stringOfInst() : string = createAnInstance().ToString() \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/Records.fs.il.net472.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/Records.fs.il.net472.bsl index 14108f77f92..d4c152a8ce3 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/Records.fs.il.net472.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/Records.fs.il.net472.bsl @@ -273,6 +273,16 @@ IL_0020: ret } + .method public static string stringOfInst() cil managed + { + + .maxstack 8 + IL_0000: call class MyTestModule/MyRecord`3 MyTestModule::createAnInstance() + IL_0005: tail. + IL_0007: callvirt instance string [runtime]System.Object::ToString() + IL_000c: ret + } + .property string maybeString() { .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/Records.fs.il.netcore.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/Records.fs.il.netcore.bsl index 0956d43e1f8..5edf4dc0c5a 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/Records.fs.il.netcore.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/Records.fs.il.netcore.bsl @@ -273,6 +273,16 @@ IL_0020: ret } + .method public static string stringOfInst() cil managed + { + + .maxstack 8 + IL_0000: call class MyTestModule/MyRecord`3 MyTestModule::createAnInstance() + IL_0005: tail. + IL_0007: callvirt instance string [runtime]System.Object::ToString() + IL_000c: ret + } + .property string maybeString() { .custom instance void [runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) @@ -289,3 +299,4 @@ + diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/ReferenceDU.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/ReferenceDU.fs index 01c56c32109..95ed41ea247 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/ReferenceDU.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/ReferenceDU.fs @@ -8,6 +8,8 @@ type MyDu = let giveMeLabel () = JustLabel +let giveMeLabelsText() : string = giveMeLabel().ToString() + let createMaybeString (innerValue:string|null) = MaybeString innerValue let processNullableDu (x : (MyDu | null)) : string | null = diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/ReferenceDU.fs.il.net472.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/ReferenceDU.fs.il.net472.bsl index 0a8ff69084c..58897d97d51 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/ReferenceDU.fs.il.net472.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/ReferenceDU.fs.il.net472.bsl @@ -499,6 +499,16 @@ IL_0005: ret } + .method public static string giveMeLabelsText() cil managed + { + + .maxstack 8 + IL_0000: call class MyTestModule/MyDu MyTestModule::giveMeLabel() + IL_0005: tail. + IL_0007: callvirt instance string [runtime]System.Object::ToString() + IL_000c: ret + } + .method public static class MyTestModule/MyDu createMaybeString(string innerValue) cil managed { .param [1] diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/ReferenceDU.fs.il.netcore.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/ReferenceDU.fs.il.netcore.bsl index c946d58421f..0e99fd80dc1 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/ReferenceDU.fs.il.netcore.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/ReferenceDU.fs.il.netcore.bsl @@ -499,6 +499,16 @@ IL_0005: ret } + .method public static string giveMeLabelsText() cil managed + { + + .maxstack 8 + IL_0000: call class MyTestModule/MyDu MyTestModule::giveMeLabel() + IL_0005: tail. + IL_0007: callvirt instance string [runtime]System.Object::ToString() + IL_000c: ret + } + .method public static class MyTestModule/MyDu createMaybeString(string innerValue) cil managed { .param [1] diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs index 2571094f73f..2dee671e426 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs @@ -7,6 +7,14 @@ type MyStructDU = | B of nonNullableString:string // Tricky as the field WILL be null for tags other than B | C of nullableString:(string | null) // The field behind this is always nullable + with override this.ToString() = + match this with + | A -> "A" + | B _ -> "B" + | C _ -> "C" + +let printMyDu(x:MyStructDU) : string = x.ToString() + let getVal x = match x with | C text -> text diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs.il.net472.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs.il.net472.bsl index 83c9e539fa5..8ce83b6f4ca 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs.il.net472.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs.il.net472.bsl @@ -57,8 +57,7 @@ .custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .method public static valuetype MyTestModule/Myassembly - get_A() cil managed + .method public static valuetype MyTestModule/Myassembly get_A() cil managed { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, int32) = ( 01 00 08 00 00 00 00 00 00 00 00 00 ) @@ -69,8 +68,7 @@ IL_0006: ret } - .method public hidebysig instance bool - get_IsA() cil managed + .method public hidebysig instance bool get_IsA() cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -83,8 +81,7 @@ IL_0009: ret } - .method public static valuetype MyTestModule/Myassembly - NewB(string _nonNullableString) cil managed + .method public static valuetype MyTestModule/Myassembly NewB(string _nonNullableString) cil managed { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, int32) = ( 01 00 08 00 00 00 01 00 00 00 00 00 ) @@ -103,8 +100,7 @@ IL_0019: ret } - .method public hidebysig instance bool - get_IsB() cil managed + .method public hidebysig instance bool get_IsB() cil managed { .custom instance void System.Diagnostics.CodeAnalysis.MemberNotNullWhenAttribute::.ctor(bool, string[]) = ( 01 00 01 02 00 00 00 11 6E 6F 6E 4E 75 6C 6C 61 @@ -121,8 +117,7 @@ IL_0009: ret } - .method public static valuetype MyTestModule/Myassembly - NewC(string _nullableString) cil managed + .method public static valuetype MyTestModule/Myassembly NewC(string _nullableString) cil managed { .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, int32) = ( 01 00 08 00 00 00 02 00 00 00 00 00 ) @@ -143,8 +138,7 @@ IL_0019: ret } - .method public hidebysig instance bool - get_IsC() cil managed + .method public hidebysig instance bool get_IsC() cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -157,8 +151,7 @@ IL_0009: ret } - .method assembly specialname rtspecialname - instance void .ctor(int32 _tag) cil managed + .method assembly specialname rtspecialname instance void .ctor(int32 _tag) cil managed { .custom instance void System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute::.ctor(valuetype System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes, class [runtime]System.Type) = ( 01 00 60 06 00 00 17 4D 79 54 65 73 74 4D 6F 64 @@ -173,8 +166,7 @@ IL_0007: ret } - .method public hidebysig instance string - get_nonNullableString() cil managed + .method public hidebysig instance string get_nonNullableString() cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -187,8 +179,7 @@ IL_0006: ret } - .method public hidebysig instance string - get_nullableString() cil managed + .method public hidebysig instance string get_nullableString() cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -201,8 +192,7 @@ IL_0006: ret } - .method public hidebysig instance int32 - get_Tag() cil managed + .method public hidebysig instance int32 get_Tag() cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -213,8 +203,7 @@ IL_0006: ret } - .method assembly hidebysig specialname - instance object __DebugDisplay() cil managed + .method assembly hidebysig specialname instance object __DebugDisplay() cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -229,19 +218,30 @@ IL_001a: ret } - .method public strict virtual instance string - ToString() cil managed + .method public hidebysig virtual instance string ToString() cil managed { - .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .param [0] + .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) - .maxstack 8 - IL_0000: ldstr "%+A" - IL_0005: newobj instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5,class [FSharp.Core]Microsoft.FSharp.Core.Unit,string,string,valuetype MyTestModule/Myassembly>::.ctor(string) - IL_000a: call !!0 [FSharp.Core]Microsoft.FSharp.Core.ExtraTopLevelOperators::PrintFormatToString>(class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4) - IL_000f: ldarg.0 - IL_0010: ldobj MyTestModule/Myassembly - IL_0015: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2::Invoke(!0) - IL_001a: ret + .maxstack 3 + .locals init (valuetype MyTestModule/Myassembly V_0) + IL_0000: ldarg.0 + IL_0001: ldobj MyTestModule/Myassembly + IL_0006: stloc.0 + IL_0007: ldarg.0 + IL_0008: call instance int32 MyTestModule/Myassembly::get_Tag() + IL_000d: switch ( + IL_001e, + IL_0024, + IL_002a) + IL_001e: ldstr "A" + IL_0023: ret + + IL_0024: ldstr "B" + IL_0029: ret + + IL_002a: ldstr "C" + IL_002f: ret } .property instance int32 Tag() @@ -306,6 +306,15 @@ } } + .method public static string printMyDu(valuetype MyTestModule/Myassembly x) cil managed + { + + .maxstack 8 + IL_0000: ldarga.s x + IL_0002: callvirt instance string MyTestModule/Myassembly::ToString() + IL_0007: ret + } + .method public static string getVal(valuetype MyTestModule/Myassembly x) cil managed { .param [0] @@ -396,8 +405,7 @@ IL_0014: ret } - .method public hidebysig specialname instance class [runtime]System.Type - get_Type() cil managed + .method public hidebysig specialname instance class [runtime]System.Type get_Type() cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -408,8 +416,7 @@ IL_0006: ret } - .method public hidebysig specialname instance valuetype System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes - get_MemberType() cil managed + .method public hidebysig specialname instance valuetype System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes get_MemberType() cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -465,8 +472,7 @@ IL_0014: ret } - .method public hidebysig specialname instance string[] - get_Members() cil managed + .method public hidebysig specialname instance string[] get_Members() cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -477,8 +483,7 @@ IL_0006: ret } - .method public hidebysig specialname instance bool - get_ReturnValue() cil managed + .method public hidebysig specialname instance bool get_ReturnValue() cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -510,8 +515,7 @@ .field public uint8[] NullableFlags .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .method public specialname rtspecialname - instance void .ctor(uint8 scalarByteValue) cil managed + .method public specialname rtspecialname instance void .ctor(uint8 scalarByteValue) cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -530,8 +534,7 @@ IL_0016: ret } - .method public specialname rtspecialname - instance void .ctor(uint8[] NullableFlags) cil managed + .method public specialname rtspecialname instance void .ctor(uint8[] NullableFlags) cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -554,8 +557,7 @@ .field public uint8 Flag .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .method public specialname rtspecialname - instance void .ctor(uint8 Flag) cil managed + .method public specialname rtspecialname instance void .ctor(uint8 Flag) cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs.il.netcore.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs.il.netcore.bsl index 9f2e59097fe..8ca2c18433e 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs.il.netcore.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs.il.netcore.bsl @@ -218,18 +218,28 @@ IL_001a: ret } - .method public strict virtual instance string ToString() cil managed + .method public hidebysig virtual instance string ToString() cil managed { - .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - .maxstack 8 - IL_0000: ldstr "%+A" - IL_0005: newobj instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5,class [FSharp.Core]Microsoft.FSharp.Core.Unit,string,string,valuetype MyTestModule/Myassembly>::.ctor(string) - IL_000a: call !!0 [FSharp.Core]Microsoft.FSharp.Core.ExtraTopLevelOperators::PrintFormatToString>(class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4) - IL_000f: ldarg.0 - IL_0010: ldobj MyTestModule/Myassembly - IL_0015: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2::Invoke(!0) - IL_001a: ret + .maxstack 3 + .locals init (valuetype MyTestModule/Myassembly V_0) + IL_0000: ldarg.0 + IL_0001: ldobj MyTestModule/Myassembly + IL_0006: stloc.0 + IL_0007: ldarg.0 + IL_0008: call instance int32 MyTestModule/Myassembly::get_Tag() + IL_000d: switch ( + IL_001e, + IL_0024, + IL_002a) + IL_001e: ldstr "A" + IL_0023: ret + + IL_0024: ldstr "B" + IL_0029: ret + + IL_002a: ldstr "C" + IL_002f: ret } .property instance int32 Tag() @@ -294,6 +304,15 @@ } } + .method public static string printMyDu(valuetype MyTestModule/Myassembly x) cil managed + { + + .maxstack 8 + IL_0000: ldarga.s x + IL_0002: callvirt instance string MyTestModule/Myassembly::ToString() + IL_0007: ret + } + .method public static string getVal(valuetype MyTestModule/Myassembly x) cil managed { .param [0] @@ -334,3 +353,4 @@ + diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs index e48aeaf5b27..597d71036f1 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs @@ -306,30 +306,17 @@ let duWithExtensionProvidedToString = """A | B module Functions = let printMyType (x:MyCustomType) : string = "xxx" type MyCustomType with - override x.ToString() : string = "hi" + override x.ToString() : string = Functions.printMyType x """ [] let duWithExtensionProvidedNullableToString = """A | B -module Functions = - let printMyType (x:MyCustomType) : string = "xxx" + type MyCustomType with override x.ToString() = null """ - -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -let ``Generated ToString() methods are not nullable`` (myTypeDef) = +let toStringCodeSnippet myTypeDef = FSharp $"""module MyLibrary type MyCustomType = {myTypeDef} @@ -341,8 +328,30 @@ let processBool (x:MyCustomType) = """ |> asLibrary |> typeCheckWithStrictNullness + +[] +[] +[] +[] +[ string """)>] +[] +[] +[] +[] +[] +[] +let ``Generated ToString() methods are not nullable`` (myTypeDef) = + toStringCodeSnippet myTypeDef |> shouldSucceed +[] +[] +[] +let ``ToString override warns if it returns nullable`` (myTypeDef) = + toStringCodeSnippet myTypeDef + |> shouldFail + |> withDiagnosticMessage "With nullness checking enabled, overrides of .ToString() method must return a non-nullable string. You can handle potential nulls via the built-in string function." + [] let ``Printing a nullable string should pass`` () = FSharp """module MyLibrary From ffde9b88a5b916f7d3c5aa1c9d21a8c4aa59a660 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 22 Aug 2024 13:06:49 +0200 Subject: [PATCH 05/10] fantomas --- src/Compiler/Checking/infos.fsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Checking/infos.fsi b/src/Compiler/Checking/infos.fsi index ee693b75648..e21d588a497 100644 --- a/src/Compiler/Checking/infos.fsi +++ b/src/Compiler/Checking/infos.fsi @@ -315,7 +315,7 @@ type MethInfo = | ILMeth of tcGlobals: TcGlobals * ilMethInfo: ILMethInfo * extensionMethodPriority: ExtensionMethodPriority option /// A pseudo-method used by F# typechecker to treat Object.ToString() of known types as returning regular string, not `string?` as in the BCL - | MethInfoWithModifiedReturnType of original:MethInfo * modifiedReturnType: TType + | MethInfoWithModifiedReturnType of original: MethInfo * modifiedReturnType: TType /// Describes a use of a pseudo-method corresponding to the default constructor for a .NET struct type | DefaultStructCtor of tcGlobals: TcGlobals * structTy: TType From 74641087fc4e4a6c2779ef2a97dc4b7841544a6e Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 22 Aug 2024 13:32:37 +0200 Subject: [PATCH 06/10] release notes, cleanup --- .../.FSharp.Compiler.Service/9.0.100.md | 1 + .../Checking/AugmentWithHashCompare.fs | 16 ---------------- .../Checking/AugmentWithHashCompare.fsi | 2 -- src/Compiler/Checking/CheckDeclarations.fs | 18 +----------------- .../Checking/Expressions/CheckExpressions.fs | 2 ++ src/Compiler/Checking/MethodCalls.fs | 1 + 6 files changed, 5 insertions(+), 35 deletions(-) diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md index 3c0ce2356f4..eccf6006be9 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md @@ -12,6 +12,7 @@ ### Added * Support for nullable reference types ([PR #15181](https://github.com/dotnet/fsharp/pull/15181)) +* Treat .ToString() on F# types as returning non-nullable string in --checknulls+ context ([PR #17547](https://github.com/dotnet/fsharp/pull/17547)) * Parser: recover on missing union case fields (PR [#17452](https://github.com/dotnet/fsharp/pull/17452)) * Parser: recover on missing union case field types (PR [#17455](https://github.com/dotnet/fsharp/pull/17455)) * Sink: report function domain type ([PR #17470](https://github.com/dotnet/fsharp/pull/17470)) diff --git a/src/Compiler/Checking/AugmentWithHashCompare.fs b/src/Compiler/Checking/AugmentWithHashCompare.fs index a6b2f5fda5b..c64b0001a29 100644 --- a/src/Compiler/Checking/AugmentWithHashCompare.fs +++ b/src/Compiler/Checking/AugmentWithHashCompare.fs @@ -81,9 +81,6 @@ let mkGetHashCodeSlotSig (g: TcGlobals) = let mkEqualsSlotSig (g: TcGlobals) = TSlotSig("Equals", g.obj_ty_noNulls, [], [], [ [ TSlotParam(Some("obj"), g.obj_ty_withNulls, false, false, false, []) ] ], Some g.bool_ty) -//let mkToStringSlotSig (g: TcGlobals) = -// TSlotSig("ToString", g.obj_ty_noNulls, [], [], [ [] ], Some g.string_ty) - //------------------------------------------------------------------------- // Helpers associated with code-generation of comparison/hash augmentations //------------------------------------------------------------------------- @@ -115,9 +112,6 @@ let mkEqualsWithComparerTyExact g ty = let mkHashTy g ty = mkFunTy g (mkThisTy g ty) (mkFunTy g g.unit_ty g.int_ty) -//let mkToStringTy g ty = -// mkFunTy g (mkThisTy g ty) (mkFunTy g g.unit_ty g.string_ty) - let mkHashWithComparerTy g ty = mkFunTy g (mkThisTy g ty) (mkFunTy g g.IEqualityComparer_ty g.int_ty) @@ -1698,16 +1692,6 @@ let MakeValsForUnionAugmentation g (tcref: TyconRef) = g.AddValGeneratedAttributes v m v) -//let MakeValForNonNullableToStringOverride (g:TcGlobals) (tcref: TyconRef) = -// if g.checkNullness && not (tcref.HasOverride g "ToString" []) && (tcref.IsRecordTycon || tcref.IsUnionTycon) then -// let _, tmty = mkMinimalTy g tcref -// let tps = tcref.Typars tcref.Range -// let vis = tcref.TypeReprAccessibility -// mkValSpec g tcref tmty vis (Some(mkToStringSlotSig g)) "ToString" (tps +-> (mkToStringTy g tmty)) unitArg false -// |> List.singleton -// else -// [] - let MakeBindingsForUnionAugmentation g (tycon: Tycon) (vals: ValRef list) = let tcref = mkLocalTyconRef tycon let m = tycon.Range diff --git a/src/Compiler/Checking/AugmentWithHashCompare.fsi b/src/Compiler/Checking/AugmentWithHashCompare.fsi index 4551c84b9a7..4a7d74311b1 100644 --- a/src/Compiler/Checking/AugmentWithHashCompare.fsi +++ b/src/Compiler/Checking/AugmentWithHashCompare.fsi @@ -44,6 +44,4 @@ val TypeDefinitelyHasEquality: TcGlobals -> TType -> bool val MakeValsForUnionAugmentation: TcGlobals -> TyconRef -> Val list -//val MakeValForNonNullableToStringOverride: TcGlobals -> TyconRef -> Val list - val MakeBindingsForUnionAugmentation: TcGlobals -> Tycon -> ValRef list -> Binding list diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index 8b70759574b..fc5716bb949 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -4520,19 +4520,6 @@ module TcDeclarations = core, extra_vals_Inherits_Abstractslots @ extraMembers //------------------------------------------------------------------------- - - //let ChangeFsharpTypesToNonNullToString (g:TcGlobals) (tycons:Entity list) cenv env m = - // if g.checkNullness then - // try - // tycons |> List.iter (fun tycon -> - // if tycon.IsRecordTycon || tycon.IsUnionTycon then - // let tcref = mkLocalTyconRef tycon - // AugmentTypeDefinitions.MakeValForNonNullableToStringOverride g tcref - // |> List.iter (fun v -> PublishValueDefnMaybeInclCompilerGenerated cenv env true ModuleOrMemberBinding v)) - - // with RecoverableException exn -> - // errorRecovery exn m - /// Bind a collection of mutually recursive definitions in an implementation file let TcMutRecDefinitions (cenv: cenv) envInitial parent typeNames tpenv m scopem mutRecNSInfo (mutRecDefns: MutRecDefnsInitialData) isMutRec = @@ -4671,8 +4658,6 @@ module TcDeclarations = // Check for cyclic structs and inheritance all over again, since we may have added some fields to the struct when generating the implicit construction syntax EstablishTypeDefinitionCores.TcTyconDefnCore_CheckForCyclicStructsAndInheritance cenv tycons - //ChangeFsharpTypesToNonNullToString g tycons cenv envFinal m - withExtraBindings, envFinal @@ -4755,7 +4740,6 @@ module TcDeclarations = let envForTycon = MakeInnerEnvForTyconRef envForTycon tcref (declKind = ExtrinsicExtensionBinding) let vals, env = TcTyconMemberSpecs cenv envForTycon (TyconContainerInfo(innerParent, tcref, declaredTyconTypars, NoSafeInitInfo)) declKind tpenv members - //ChangeFsharpTypesToNonNullToString g [tcref.Deref] cenv envForTycon m if not(cenv.g.langVersion.SupportsFeature(LanguageFeature.CSharpExtensionAttributeNotRequired)) then vals, env else @@ -5794,7 +5778,7 @@ let CheckOneImplFile // virtual dispatch slots. conditionallySuppressErrorReporting (checkForErrors()) (fun () -> try - implFileTypePriorToSig |> IterTyconsOfModuleOrNamespaceType (fun tycon -> + implFileTypePriorToSig |> IterTyconsOfModuleOrNamespaceType (fun tycon -> FinalTypeDefinitionChecksAtEndOfInferenceScope (cenv.infoReader, envAtEnd.NameEnv, cenv.tcSink, true, denvAtEnd, tycon)) with RecoverableException exn -> diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 5c26ba3b868..435c69e94cc 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -11676,6 +11676,8 @@ and ApplyAbstractSlotInference (cenv: cenv) (envinner: TcEnv) (_: Val option) (a let declaredTypars = (if typarsFromAbsSlotAreRigid then typarsFromAbsSlot else declaredTypars) + // Overrides can narrow the retTy from nullable to not-null. + // By changing nullness to be variable we do not get in the way of eliminating nullness (=good). let retTyFromAbsSlot = retTyFromAbsSlot |> changeWithNullReqTyToVariable g let absSlotTy = mkMethodTy g argTysFromAbsSlot retTyFromAbsSlot diff --git a/src/Compiler/Checking/MethodCalls.fs b/src/Compiler/Checking/MethodCalls.fs index 0ed430f1d74..ae8432d78b7 100644 --- a/src/Compiler/Checking/MethodCalls.fs +++ b/src/Compiler/Checking/MethodCalls.fs @@ -529,6 +529,7 @@ type CalledMeth<'T> | objTy :: [], ILMeth _ when g.checkNullness && minfo.DisplayName = "ToString" + && minfo.IsNullary && (isAnonRecdTy g objTy || isRecdTy g objTy || isUnionTy g objTy) && ( typeEquiv g g.obj_ty_noNulls minfo.ApparentEnclosingAppType || typeEquiv g g.system_Value_ty minfo.ApparentEnclosingAppType) -> From 889594a7d46ddfa663294be505597b0f80837865 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 22 Aug 2024 15:25:11 +0200 Subject: [PATCH 07/10] Make sure we emit tcNullableToStringOverride even in desktop framework (where BCL methods are ambivalent) --- src/Compiler/TypedTree/TypedTreeOps.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 9eda4a9c2de..71bfe3550f0 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9184,7 +9184,8 @@ let changeWithNullReqTyToVariable g reqTy = match isTyparTy g sty with | false -> match nullnessOfTy g sty with - | Nullness.Known NullnessInfo.WithNull -> + | Nullness.Known NullnessInfo.AmbivalentToNull + | Nullness.Known NullnessInfo.WithNull when g.checkNullness -> reqTy |> replaceNullnessOfTy (NewNullnessVar()) | _ -> reqTy | true -> reqTy From 6b8097cd35c3449380ef35cb991f726e8bba7450 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 22 Aug 2024 17:18:21 +0200 Subject: [PATCH 08/10] handle type.FullName warning --- src/Compiler/AbstractIL/ilnativeres.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/AbstractIL/ilnativeres.fs b/src/Compiler/AbstractIL/ilnativeres.fs index ec6a928fc63..62961613b3f 100644 --- a/src/Compiler/AbstractIL/ilnativeres.fs +++ b/src/Compiler/AbstractIL/ilnativeres.fs @@ -1154,7 +1154,7 @@ type NativeResourceWriter() = "Unknown entry %s" (match e with | null -> "" - | e -> e.GetType().FullName) + | e -> e.GetType().FullName |> string) if id >= 0 then writer.WriteInt32 id From 68c4702c6570b1776727a5656142965c08c0dca8 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 30 Aug 2024 11:26:31 +0200 Subject: [PATCH 09/10] adjust net472 test baseline --- .../EmittedIL/Nullness/StructDU.fs.il.net472.bsl | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs.il.net472.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs.il.net472.bsl index 8ce83b6f4ca..244fc114746 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs.il.net472.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Nullness/StructDU.fs.il.net472.bsl @@ -220,8 +220,6 @@ .method public hidebysig virtual instance string ToString() cil managed { - .param [0] - .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) .maxstack 3 .locals init (valuetype MyTestModule/Myassembly V_0) @@ -386,9 +384,7 @@ .field private class [runtime]System.Type Type@ .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .method public specialname rtspecialname - instance void .ctor(valuetype System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes MemberType, - class [runtime]System.Type Type) cil managed + .method public specialname rtspecialname instance void .ctor(valuetype System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes MemberType, class [runtime]System.Type Type) cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) @@ -453,9 +449,7 @@ .field private string[] Members@ .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) - .method public specialname rtspecialname - instance void .ctor(bool ReturnValue, - string[] Members) cil managed + .method public specialname rtspecialname instance void .ctor(bool ReturnValue, string[] Members) cil managed { .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) From 449fdb811b8715e61852f0e1e1abcfab0e389b03 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 30 Aug 2024 15:36:27 +0200 Subject: [PATCH 10/10] [] on now recursive GetXmlDocSigOfMethInfo --- src/Compiler/Checking/InfoReader.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compiler/Checking/InfoReader.fs b/src/Compiler/Checking/InfoReader.fs index 48fe6bb4276..f4a9f033c64 100644 --- a/src/Compiler/Checking/InfoReader.fs +++ b/src/Compiler/Checking/InfoReader.fs @@ -1191,6 +1191,7 @@ let GetXmlDocSigOfUnionCaseRef (ucref: UnionCaseRef) = ucref.UnionCase.XmlDocSig <- XmlDocSigOfUnionCase [tcref.CompiledRepresentationForNamedType.FullName; ucref.CaseName] Some (ccuFileName, ucref.UnionCase.XmlDocSig) +[] let rec GetXmlDocSigOfMethInfo (infoReader: InfoReader) m (minfo: MethInfo) = let amap = infoReader.amap match minfo with