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 7e871eb33e10..27e8ed7350ac 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md @@ -20,6 +20,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)) @@ -28,6 +29,7 @@ * Allow object expression without overrides. ([Language suggestion #632](https://github.com/fsharp/fslang-suggestions/issues/632), [PR #17387](https://github.com/dotnet/fsharp/pull/17387)) * Enable FSharp 9.0 Language Version ([Issue #17497](https://github.com/dotnet/fsharp/issues/17438)), [PR](https://github.com/dotnet/fsharp/pull/17500))) * Enable LanguageFeature.EnforceAttributeTargets in F# 9.0. ([Issue #17514](https://github.com/dotnet/fsharp/issues/17558), [PR #17516](https://github.com/dotnet/fsharp/pull/17558)) +* Enable consuming generic arguments defined as `allows ref struct` in C# ([Issue #17597](https://github.com/dotnet/fsharp/issues/17597) ### Changed diff --git a/src/Compiler/AbstractIL/il.fs b/src/Compiler/AbstractIL/il.fs index 9b8f659f647d..9ed1822676b6 100644 --- a/src/Compiler/AbstractIL/il.fs +++ b/src/Compiler/AbstractIL/il.fs @@ -1862,9 +1862,10 @@ type ILGenericParameterDef = Name: string Constraints: ILTypes Variance: ILGenericVariance - HasReferenceTypeConstraint: bool + HasReferenceTypeConstraint: bool HasNotNullableValueTypeConstraint: bool HasDefaultConstructorConstraint: bool + HasAllowsRefStruct: bool CustomAttrsStored: ILAttributesStored MetadataIndex: int32 } @@ -3283,6 +3284,7 @@ let mkILSimpleTypar nm = HasReferenceTypeConstraint = false HasNotNullableValueTypeConstraint = false HasDefaultConstructorConstraint = false + HasAllowsRefStruct = false CustomAttrsStored = storeILCustomAttrs emptyILCustomAttrs MetadataIndex = NoMetadataIdx } diff --git a/src/Compiler/AbstractIL/il.fsi b/src/Compiler/AbstractIL/il.fsi index 1e2a6ee3705b..e3ec95a40d7c 100644 --- a/src/Compiler/AbstractIL/il.fsi +++ b/src/Compiler/AbstractIL/il.fsi @@ -1021,6 +1021,9 @@ type ILGenericParameterDef = /// Indicates the type argument must have a public nullary constructor. HasDefaultConstructorConstraint: bool + /// Indicates the type parameter allows ref struct, i.e. an anti constraint. + HasAllowsRefStruct: bool + /// Do not use this CustomAttrsStored: ILAttributesStored diff --git a/src/Compiler/AbstractIL/ilnativeres.fs b/src/Compiler/AbstractIL/ilnativeres.fs index ec6a928fc630..62961613b3fe 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 diff --git a/src/Compiler/AbstractIL/ilread.fs b/src/Compiler/AbstractIL/ilread.fs index 0ff6896f82df..02696c53f0ec 100644 --- a/src/Compiler/AbstractIL/ilread.fs +++ b/src/Compiler/AbstractIL/ilread.fs @@ -2292,6 +2292,7 @@ and seekReadGenericParamsUncached ctxtH (GenericParamsIdx(numTypars, a, b)) = HasReferenceTypeConstraint = (flags &&& 0x0004) <> 0 HasNotNullableValueTypeConstraint = (flags &&& 0x0008) <> 0 HasDefaultConstructorConstraint = (flags &&& 0x0010) <> 0 + HasAllowsRefStruct = (flags &&& 0x0020) <> 0 }) ) diff --git a/src/Compiler/AbstractIL/ilreflect.fs b/src/Compiler/AbstractIL/ilreflect.fs index 8c56aca9be8d..9b0b7eddb9e7 100644 --- a/src/Compiler/AbstractIL/ilreflect.fs +++ b/src/Compiler/AbstractIL/ilreflect.fs @@ -1714,31 +1714,34 @@ let buildGenParamsPass1b cenv emEnv (genArgs: Type array) (gps: ILGenericParamet gp.CustomAttrs |> emitCustomAttrs cenv emEnv (wrapCustomAttr gpB.SetCustomAttribute) - let flags = GenericParameterAttributes.None - let flags = match gp.Variance with - | NonVariant -> flags - | CoVariant -> flags ||| GenericParameterAttributes.Covariant - | ContraVariant -> flags ||| GenericParameterAttributes.Contravariant + | NonVariant -> GenericParameterAttributes.None + | CoVariant -> GenericParameterAttributes.Covariant + | ContraVariant -> GenericParameterAttributes.Contravariant - let flags = - if gp.HasReferenceTypeConstraint then - flags ||| GenericParameterAttributes.ReferenceTypeConstraint - else - flags + let zero = GenericParameterAttributes.None let flags = - if gp.HasNotNullableValueTypeConstraint then - flags ||| GenericParameterAttributes.NotNullableValueTypeConstraint - else - flags - - let flags = - if gp.HasDefaultConstructorConstraint then - flags ||| GenericParameterAttributes.DefaultConstructorConstraint - else - flags + flags + ||| (if gp.HasReferenceTypeConstraint then + GenericParameterAttributes.ReferenceTypeConstraint + else + zero) + ||| (if gp.HasNotNullableValueTypeConstraint then + GenericParameterAttributes.NotNullableValueTypeConstraint + else + zero) + ||| (if gp.HasDefaultConstructorConstraint then + GenericParameterAttributes.DefaultConstructorConstraint + else + zero) + ||| + // GenericParameterAttributes.AllowByRefLike from net9, not present in ns20 + (if gp.HasAllowsRefStruct then + (enum 0x0020) + else + zero) gpB.SetGenericParameterAttributes flags) //---------------------------------------------------------------------------- diff --git a/src/Compiler/AbstractIL/ilwrite.fs b/src/Compiler/AbstractIL/ilwrite.fs index f1112b334f82..832aa1c2810a 100644 --- a/src/Compiler/AbstractIL/ilwrite.fs +++ b/src/Compiler/AbstractIL/ilwrite.fs @@ -2513,7 +2513,8 @@ let rec GetGenericParamAsGenericParamRow cenv _env idx owner gp = | ContraVariant -> 0x0002) ||| (if gp.HasReferenceTypeConstraint then 0x0004 else 0x0000) ||| (if gp.HasNotNullableValueTypeConstraint then 0x0008 else 0x0000) ||| - (if gp.HasDefaultConstructorConstraint then 0x0010 else 0x0000) + (if gp.HasDefaultConstructorConstraint then 0x0010 else 0x0000) ||| + (if gp.HasAllowsRefStruct then 0x0020 else 0x0000) let mdVersionMajor, _ = metadataSchemaVersionSupportedByCLRVersion cenv.desiredMetadataVersion diff --git a/src/Compiler/Checking/AccessibilityLogic.fs b/src/Compiler/Checking/AccessibilityLogic.fs index b7db401463e7..6aba2edcb4b3 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 9cb2ee28de61..a91ace98d4ad 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/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index fddf9085c5fd..8a997f75ebbc 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -4526,7 +4526,6 @@ module TcDeclarations = core, extra_vals_Inherits_Abstractslots @ extraMembers //------------------------------------------------------------------------- - /// 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 = @@ -5888,12 +5887,12 @@ 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 sigFileType = moduleTyAcc.Value + let! tcEnv = TcSignatureElements cenv ParentNone m envinner PreXmlDoc.Empty None specs + 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 d61af2dd0741..196a62c3fecd 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 7499e154d671..caa1e79b0cdb 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -11683,6 +11683,10 @@ 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 UnifyTypes cenv envinner m argsAndRetTy absSlotTy diff --git a/src/Compiler/Checking/InfoReader.fs b/src/Compiler/Checking/InfoReader.fs index b4986de5438c..f4a9f033c646 100644 --- a/src/Compiler/Checking/InfoReader.fs +++ b/src/Compiler/Checking/InfoReader.fs @@ -1191,7 +1191,8 @@ 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 +1220,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 037f36a3db57..723639435498 100644 --- a/src/Compiler/Checking/MethodCalls.fs +++ b/src/Compiler/Checking/MethodCalls.fs @@ -523,6 +523,19 @@ type CalledMeth<'T> staticTyOpt: TType option) = let g = infoReader.g + + let minfo = + match callerObjArgTys,minfo with + | 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) -> + 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 +1052,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 +1072,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 +1123,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 +1196,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 ad12a1cdfe49..b9eaa2c6687e 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 bba120dfab2d..2eb5b14fa02b 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 222205d44481..ccb99f121393 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/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 3fbe64272481..06ef1b9766a4 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -28,6 +28,7 @@ open FSharp.Compiler.TypedTreeBasics open FSharp.Compiler.TypedTreeOps open FSharp.Compiler.TypeHierarchy open FSharp.Compiler.TypeRelations +open Import //-------------------------------------------------------------------------- // NOTES: reraise safety checks @@ -334,7 +335,20 @@ let RecordAnonRecdInfo cenv (anonInfo: AnonRecdTypeInfo) = // approx walk of type //-------------------------------------------------------------------------- -let rec CheckTypeDeep (cenv: cenv) (visitTy, visitTyconRefOpt, visitAppTyOpt, visitTraitSolutionOpt, visitTyparOpt as f) (g: TcGlobals) env isInner ty = +/// Represents the container for nester type instantions, carrying information about the parent (generic type) and data about correspinding generic typar definition. +/// For current use, IlGenericParameterDef was enough. For other future use cases, conversion into F# Typar might be needed. +type TypeInstCtx = + | NoInfo + | IlGenericInst of parent:TyconRef * genericArg:ILGenericParameterDef + | TyparInst of parent:TyconRef + | TopLevelAllowingByRef + + with member x.TyparAllowsRefStruct() = + match x with + | IlGenericInst(_,ilTypar) -> ilTypar.HasAllowsRefStruct + | _ -> false + +let rec CheckTypeDeep (cenv: cenv) (visitTy, visitTyconRefOpt, visitAppTyOpt, visitTraitSolutionOpt, visitTyparOpt as f) (g: TcGlobals) env (typeInstParentOpt:TypeInstCtx) ty = // We iterate the _solved_ constraints as well, to pick up any record of trait constraint solutions // This means we walk _all_ the constraints _everywhere_ in a type, including // those attached to _solved_ type variables. This is used by PostTypeCheckSemanticChecks to detect uses of @@ -366,22 +380,30 @@ let rec CheckTypeDeep (cenv: cenv) (visitTy, visitTyconRefOpt, visitAppTyOpt, vi match ty with | TType_forall (tps, body) -> let env = BindTypars g env tps - CheckTypeDeep cenv f g env isInner body + CheckTypeDeep cenv f g env typeInstParentOpt body tps |> List.iter (fun tp -> tp.Constraints |> List.iter (CheckTypeConstraintDeep cenv f g env)) | TType_measure _ -> () | TType_app (tcref, tinst, _) -> match visitTyconRefOpt with - | Some visitTyconRef -> visitTyconRef isInner tcref + | Some visitTyconRef -> visitTyconRef typeInstParentOpt tcref | None -> () // If it's a 'byref<'T>', don't check 'T as an inner. This allows byref>. // 'byref>' is invalid and gets checked in visitAppTy. - if isByrefTyconRef g tcref then - CheckTypesDeepNoInner cenv f g env tinst + //if isByrefTyconRef g tcref then + // CheckTypesDeepNoInner cenv f g env tinst + + if tcref.CanDeref && tcref.IsILTycon && tinst.Length = tcref.ILTyconRawMetadata.GenericParams.Length then + (tinst,tcref.ILTyconRawMetadata.GenericParams) + ||> List.iter2 (fun ty ilGenericParam -> + let typeInstParent = IlGenericInst(tcref, ilGenericParam) + CheckTypeDeep cenv f g env typeInstParent ty) else - CheckTypesDeep cenv f g env tinst + let parentRef = TyparInst(tcref) + for ty in tinst do + CheckTypeDeep cenv f g env parentRef ty match visitAppTyOpt with | Some visitAppTy -> visitAppTy (tcref, tinst) @@ -398,8 +420,8 @@ let rec CheckTypeDeep (cenv: cenv) (visitTy, visitTyconRefOpt, visitAppTyOpt, vi CheckTypesDeep cenv f g env tys | TType_fun (s, t, _) -> - CheckTypeDeep cenv f g env true s - CheckTypeDeep cenv f g env true t + CheckTypeDeep cenv f g env NoInfo s + CheckTypeDeep cenv f g env NoInfo t | TType_var (tp, _) -> if not tp.IsSolved then @@ -410,20 +432,16 @@ let rec CheckTypeDeep (cenv: cenv) (visitTy, visitTyconRefOpt, visitAppTyOpt, vi and CheckTypesDeep cenv f g env tys = for ty in tys do - CheckTypeDeep cenv f g env true ty - -and CheckTypesDeepNoInner cenv f g env tys = - for ty in tys do - CheckTypeDeep cenv f g env false ty + CheckTypeDeep cenv f g env NoInfo ty and CheckTypeConstraintDeep cenv f g env x = match x with - | TyparConstraint.CoercesTo(ty, _) -> CheckTypeDeep cenv f g env true ty + | TyparConstraint.CoercesTo(ty, _) -> CheckTypeDeep cenv f g env NoInfo ty | TyparConstraint.MayResolveMember(traitInfo, _) -> CheckTraitInfoDeep cenv f g env traitInfo - | TyparConstraint.DefaultsTo(_, ty, _) -> CheckTypeDeep cenv f g env true ty + | TyparConstraint.DefaultsTo(_, ty, _) -> CheckTypeDeep cenv f g env NoInfo ty | TyparConstraint.SimpleChoice(tys, _) -> CheckTypesDeep cenv f g env tys - | TyparConstraint.IsEnum(underlyingTy, _) -> CheckTypeDeep cenv f g env true underlyingTy - | TyparConstraint.IsDelegate(argTys, retTy, _) -> CheckTypeDeep cenv f g env true argTys; CheckTypeDeep cenv f g env true retTy + | TyparConstraint.IsEnum(underlyingTy, _) -> CheckTypeDeep cenv f g env NoInfo underlyingTy + | TyparConstraint.IsDelegate(argTys, retTy, _) -> CheckTypeDeep cenv f g env NoInfo argTys; CheckTypeDeep cenv f g env NoInfo retTy | TyparConstraint.SupportsComparison _ | TyparConstraint.SupportsEquality _ | TyparConstraint.SupportsNull _ @@ -436,18 +454,18 @@ and CheckTypeConstraintDeep cenv f g env x = and CheckTraitInfoDeep cenv (_, _, _, visitTraitSolutionOpt, _ as f) g env traitInfo = CheckTypesDeep cenv f g env traitInfo.SupportTypes CheckTypesDeep cenv f g env traitInfo.CompiledObjectAndArgumentTypes - Option.iter (CheckTypeDeep cenv f g env true ) traitInfo.CompiledReturnType + Option.iter (CheckTypeDeep cenv f g env NoInfo ) traitInfo.CompiledReturnType match visitTraitSolutionOpt, traitInfo.Solution with | Some visitTraitSolution, Some sln -> visitTraitSolution sln | _ -> () /// Check for byref-like types let CheckForByrefLikeType cenv env m ty check = - CheckTypeDeep cenv (ignore, Some (fun _deep tcref -> if isByrefLikeTyconRef cenv.g m tcref then check()), None, None, None) cenv.g env false ty + CheckTypeDeep cenv (ignore, Some (fun ctx tcref -> if (isByrefLikeTyconRef cenv.g m tcref && not(ctx.TyparAllowsRefStruct())) then check()), None, None, None) cenv.g env NoInfo ty /// Check for byref types let CheckForByrefType cenv env ty check = - CheckTypeDeep cenv (ignore, Some (fun _deep tcref -> if isByrefTyconRef cenv.g tcref then check()), None, None, None) cenv.g env false ty + CheckTypeDeep cenv (ignore, Some (fun _ctx tcref -> if isByrefTyconRef cenv.g tcref then check()), None, None, None) cenv.g env NoInfo ty /// check captures under lambdas /// @@ -516,7 +534,7 @@ let CheckTypeForAccess (cenv: cenv) env objName valAcc m ty = if isLessAccessible tyconAcc valAcc then errorR(Error(FSComp.SR.chkTypeLessAccessibleThanType(tcref.DisplayName, (objName())), m)) - CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env false ty + CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env NoInfo ty let WarnOnWrongTypeForAccess (cenv: cenv) env objName valAcc m ty = if cenv.reportErrors then @@ -534,7 +552,7 @@ let WarnOnWrongTypeForAccess (cenv: cenv) env objName valAcc m ty = let warningText = errorText + Environment.NewLine + FSComp.SR.tcTypeAbbreviationsCheckedAtCompileTime() warning(AttributeChecking.ObsoleteWarning(warningText, m)) - CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env false ty + CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env NoInfo ty /// Indicates whether a byref or byref-like type is permitted at a particular location [] @@ -629,16 +647,26 @@ let CheckTypeAux permitByRefLike (cenv: cenv) env m ty onInnerByrefError = else errorR (Error(FSComp.SR.checkNotSufficientlyGenericBecauseOfScope(tp.DisplayName), m)) - let visitTyconRef isInner tcref = + let visitTyconRef (ctx:TypeInstCtx) tcref = + let checkInner() = + match ctx with + | TopLevelAllowingByRef -> false + | TyparInst(parentTcRef) + | IlGenericInst(parentTcRef,_) when isByrefTyconRef cenv.g parentTcRef -> false + | _ -> true + + let isInnerByRefLike() = checkInner() && isByrefLikeTyconRef cenv.g m tcref + + let permitByRefLike = + if ctx.TyparAllowsRefStruct() then PermitByRefType.All else permitByRefLike - let isInnerByRefLike = isInner && isByrefLikeTyconRef cenv.g m tcref match permitByRefLike with | PermitByRefType.None when isByrefLikeTyconRef cenv.g m tcref -> errorR(Error(FSComp.SR.chkErrorUseOfByref(), m)) - | PermitByRefType.NoInnerByRefLike when isInnerByRefLike -> + | PermitByRefType.NoInnerByRefLike when isInnerByRefLike() -> onInnerByrefError () - | PermitByRefType.SpanLike when isByrefTyconRef cenv.g tcref || isInnerByRefLike -> + | PermitByRefType.SpanLike when isByrefTyconRef cenv.g tcref || isInnerByRefLike() -> onInnerByrefError () | _ -> () @@ -665,7 +693,13 @@ let CheckTypeAux permitByRefLike (cenv: cenv) env m ty onInnerByrefError = cenv.potentialUnboundUsesOfVals <- cenv.potentialUnboundUsesOfVals.Add(vref.Stamp, m) | _ -> () - CheckTypeDeep cenv (ignore, Some visitTyconRef, Some visitAppTy, Some visitTraitSolution, Some visitTyar) cenv.g env false ty + let initialCtx = + match permitByRefLike with + | PermitByRefType.SpanLike + | PermitByRefType.NoInnerByRefLike -> TopLevelAllowingByRef + | _ -> NoInfo + + CheckTypeDeep cenv (ignore, Some visitTyconRef, Some visitAppTy, Some visitTraitSolution, Some visitTyar) cenv.g env initialCtx ty let CheckType permitByRefLike cenv env m ty = CheckTypeAux permitByRefLike cenv env m ty (fun () -> errorR(Error(FSComp.SR.chkErrorUseOfByref(), m))) @@ -1458,9 +1492,31 @@ and CheckExprOp cenv env (op, tyargs, args, m) ctxt expr = CombineTwoLimits limit1 limit2 | TOp.ILCall (_, _, _, _, _, _, _, ilMethRef, enclTypeInst, methInst, retTypes), _, _ -> + CheckTypeInstNoByrefs cenv env m tyargs - CheckTypeInstNoByrefs cenv env m enclTypeInst - CheckTypeInstNoByrefs cenv env m methInst + + match enclTypeInst,methInst with + | [],[] -> () + | enclTypeInst,methInst -> + let tyconRef = ImportILTypeRef cenv.amap m ilMethRef.DeclaringTypeRef + match tyconRef.TypeReprInfo with + | TILObjectRepr(TILObjectReprData(scoref, _, tdef)) -> + (enclTypeInst,tdef.GenericParams) + ||> List.iter2 (fun typeInst typeGeneric -> + if not typeGeneric.HasAllowsRefStruct then + CheckTypeNoByrefs cenv env m typeInst) + + match methInst with + | [] -> () + | methInst -> + let methDef = resolveILMethodRefWithRescope (rescopeILType scoref) tdef ilMethRef + (methInst,methDef.GenericParams) + ||> List.iter2 (fun methInst methGeneric -> + if not methGeneric.HasAllowsRefStruct then + CheckTypeNoByrefs cenv env m methInst) + + | _ -> () + CheckTypeInstNoInnerByrefs cenv env m retTypes // permit byref returns let hasReceiver = diff --git a/src/Compiler/Checking/infos.fs b/src/Compiler/Checking/infos.fs index a5bdaaf28830..23afa7bece57 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, whether 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 d013011192ee..9ab99a8346be 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/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 2b1308dcddcd..6758b6dcd94c 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -2070,6 +2070,7 @@ type AnonTypeGenerationTable() = HasReferenceTypeConstraint = false HasNotNullableValueTypeConstraint = false HasDefaultConstructorConstraint = false + HasAllowsRefStruct = false MetadataIndex = NoMetadataIdx } ] @@ -5733,6 +5734,7 @@ and GenGenericParam cenv eenv (tp: Typar) = HasReferenceTypeConstraint = refTypeConstraint HasNotNullableValueTypeConstraint = notNullableValueTypeConstraint || emitUnmanagedInIlOutput HasDefaultConstructorConstraint = defaultConstructorConstraint + HasAllowsRefStruct = false } //-------------------------------------------------------------------------- diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 4776f65c28e7..fdf1953fbdab 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1536,6 +1536,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 0e5d0ff84751..1fedee2968a8 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/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 173c8ba44eac..60ab98e8c5f3 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 diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index f48c78209bf1..9b4576b76eef 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -1492,6 +1492,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 7420a6afdc42..8c77b399371a 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -1492,6 +1492,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 845bd6a8ca8a..aec3384de853 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -1492,6 +1492,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 85ce7e5e43c8..11fd19591fe9 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -1492,6 +1492,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 887bd6e57cd7..bdb6c6cfdc35 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -1492,6 +1492,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 1d5a0740a1e2..e18a2a57d001 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -1492,6 +1492,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 e8eebabddf58..f67ef6dee65c 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -1492,6 +1492,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 ca1b6a407b3a..0b8f543addd0 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -1492,6 +1492,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 f927f204fae5..32a996dd3792 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -1492,6 +1492,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 5c3701a06de9..4b36d207a2b7 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -1492,6 +1492,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 9b13279b1ebf..e89810f7f64f 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -1492,6 +1492,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 c583e45c53d2..670fecf8c49c 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -1492,6 +1492,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 17fc8934c3c6..1650af344ba7 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -1492,6 +1492,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 1178f3e704e3..0e1cbf437ca4 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 9e4cf639944c..6390383dbd87 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 127fe13bf979..67f941bd5bcb 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 2a2c7a09b7e9..0fff3c6c6e05 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 14108f77f92d..d4c152a8ce37 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 0956d43e1f87..5edf4dc0c5ae 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 01c56c32109a..95ed41ea2472 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 0a8ff69084cb..58897d97d51b 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 c946d58421f8..0e99fd80dc1b 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 2571094f73fd..2dee671e4265 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 83c9e539fa53..244fc1147467 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,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() @@ -306,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] @@ -377,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 ) @@ -396,8 +401,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 +412,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 ) @@ -446,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 ) @@ -465,8 +466,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 +477,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 +509,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 +528,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 +551,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 9f2e59097fe9..8ca2c18433ef 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/Interop/ByrefTests.fs b/tests/FSharp.Compiler.ComponentTests/Interop/ByrefTests.fs index dca7589c903c..db89445122b9 100644 --- a/tests/FSharp.Compiler.ComponentTests/Interop/ByrefTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Interop/ByrefTests.fs @@ -53,4 +53,169 @@ module ``Byref interop verification tests`` = """ |> asExe |> compileAndRun - |> shouldSucceed \ No newline at end of file + |> shouldSucceed + + [] + let ``Ref structs in generics - can declare`` () = + FSharp """module Foo +open System +let x(a:Action>) = a.Invoke(ReadOnlySpan([||])) + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + + [] + let ``Ref structs in generics - can return as inner`` () = + + FSharp """module Foo +open System +let x() = + let a:(Action>) = Unchecked.defaultof<_> + a + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + + [] + let ``Ref structs in generics - can use in object expressions`` () = + FSharp """module Foo +open System + +let main _args = + let comparer = + { new System.IComparable> + with member x.CompareTo(o) = 42 } + comparer.CompareTo(ReadOnlySpan([||])) + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + + [] + let ``Ref structs in generics - can use in foreach`` () = + FSharp """module Foo +open System + +let processSeq (input:seq>) = + for ros in input do + printfn "%i" (ros.Length) + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + + [] + let ``Ref structs in generics - IL and runtime test`` () = + FSharp """module Foo +open System +open System.Collections.Generic + +let myDict = ["x",1;"xyz",2] |> dict |> Dictionary + +let checkIfPresent (input:ReadOnlySpan) = + let altLookup = myDict.GetAlternateLookup>() + let present = altLookup.ContainsKey(input) + for c in input do + printf "%c" c + printfn ": %A" present + +[] +let main _args = + checkIfPresent(ReadOnlySpan([||])) + checkIfPresent("x".AsSpan()) + checkIfPresent(ReadOnlySpan([|'x';'y';'z'|])) + 0 + """ + |> asExe + |> withLangVersionPreview + |> compileAndRun + |> shouldSucceed + |> verifyOutputContains [|": false";"x: true";"xyz: true"|] + |> verifyIL + ["call valuetype [System.Collections]System.Collections.Generic.Dictionary`2/AlternateLookup`1 [System.Collections]System.Collections.Generic.CollectionExtensions::GetAlternateLookup>(class [System.Collections]System.Collections.Generic.Dictionary`2)"] + + [] + let ``Ref structs in generics - GetAlternateLookup`` () = + FSharp """module Foo +open System +open System.Collections.Generic + +let main _args = + let myDict = ["x",1;"y",2] |> dict |> Dictionary + let altLookup = myDict.GetAlternateLookup>() + altLookup.ContainsKey(ReadOnlySpan([|'x'|])) + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + + [] + [ Async.RunSynchronously")>] + [] + let ``Ref structs in generics - builders`` (build:string) (getter:string) = + + FSharp $$$"""module Foo +open System + +let getAction() = + {{{build}}} { + let x = new Action>(fun ros -> printfn "%i" ros.Length) + return x + } + +let getBuilderResult() = + {{{build}}} { + let! myAction = getAction() + myAction.Invoke(ReadOnlySpan([|1|])) + return myAction + } + +[] +let main _args = + let myTask = getBuilderResult(){{{getter}}} + printfn "%O" myTask + 0 + """ + |> asExe + |> withLangVersionPreview + |> compileAndRun + |> shouldSucceed + |> verifyOutputContains [|"1";"System.Action`1[System.ReadOnlySpan`1[System.Int32]]"|] + |> verifyIL + [ if build = "task" then + "valuetype [FSharp.Core]Microsoft.FSharp.Control.TaskStateMachineData`1>>>" + else "[FSharp.Core]Microsoft.FSharp.Control.FSharpAsync`1>>>" ] + + [] + let ``Ref structs in generics - negative tests`` () = + FSharp """module Foo +open System +open System.Collections.Generic + +[] +type MyRecordFullOfWrongStuff<'T> = + { Value : Span<'T> + MyMap : list> + MyDict: Dictionary> + Nested: Span> } + +let processRecord (recd:MyRecordFullOfWrongStuff>>) = + recd.MyDict.["x"] + + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics + [ Error 412, Line 7, Col 7, Line 7, Col 12, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL." + Error 437, Line 6, Col 6, Line 6, Col 30, "A type would store a byref typed value. This is not permitted by Common IL." + Error 412, Line 8, Col 7, Line 8, Col 12, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL." + Error 412, Line 9, Col 7, Line 9, Col 13, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL." + Error 412, Line 10, Col 7, Line 10, Col 13, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL." + Error 3300, Line 12, Col 20, Line 12, Col 24, "The parameter 'recd' has an invalid type 'MyRecordFullOfWrongStuff>>'. This is not permitted by the rules of Common IL." + Error 412, Line 13, Col 5, Line 13, Col 22, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL." + Error 412, Line 13, Col 5, Line 13, Col 16, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL." + Error 412, Line 13, Col 5, Line 13, Col 9, "A type instantiation involves a byref type. This is not permitted by the rules of Common IL."] + diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs index 7e3e39f67f73..de6d1c888cac 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs @@ -8,6 +8,8 @@ let withNullnessOptions cu = |> withCheckNulls |> 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 = @@ -299,6 +301,57 @@ 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 = Functions.printMyType x + """ + +[] +let duWithExtensionProvidedNullableToString = """A | B + +type MyCustomType with + override x.ToString() = null + """ + +let toStringCodeSnippet myTypeDef = + FSharp $"""module MyLibrary + +type MyCustomType = {myTypeDef} + +let onlyWantNotNullString(x:string) = () + +let processBool (x:MyCustomType) = + onlyWantNotNullString(x.ToString()) +""" + |> 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 diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 77a6ce3e6999..58e660c6ae14 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -653,9 +653,11 @@ FSharp.Compiler.AbstractIL.IL+ILFieldSpec: System.String Name FSharp.Compiler.AbstractIL.IL+ILFieldSpec: System.String ToString() FSharp.Compiler.AbstractIL.IL+ILFieldSpec: System.String get_Name() FSharp.Compiler.AbstractIL.IL+ILFieldSpec: Void .ctor(ILFieldRef, ILType) +FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean HasAllowsRefStruct FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean HasDefaultConstructorConstraint FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean HasNotNullableValueTypeConstraint FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean HasReferenceTypeConstraint +FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean get_HasAllowsRefStruct() FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean get_HasDefaultConstructorConstraint() FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean get_HasNotNullableValueTypeConstraint() FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean get_HasReferenceTypeConstraint() @@ -672,7 +674,7 @@ FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Microsoft.FSharp.Collection FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: System.String Name FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: System.String ToString() FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: System.String get_Name() -FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Void .ctor(System.String, Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILType], ILGenericVariance, Boolean, Boolean, Boolean, ILAttributesStored, Int32) +FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Void .ctor(System.String, Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILType], ILGenericVariance, Boolean, Boolean, Boolean, Boolean, ILAttributesStored, Int32) FSharp.Compiler.AbstractIL.IL+ILGenericVariance+Tags: Int32 CoVariant FSharp.Compiler.AbstractIL.IL+ILGenericVariance+Tags: Int32 ContraVariant FSharp.Compiler.AbstractIL.IL+ILGenericVariance+Tags: Int32 NonVariant diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 77a6ce3e6999..58e660c6ae14 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -653,9 +653,11 @@ FSharp.Compiler.AbstractIL.IL+ILFieldSpec: System.String Name FSharp.Compiler.AbstractIL.IL+ILFieldSpec: System.String ToString() FSharp.Compiler.AbstractIL.IL+ILFieldSpec: System.String get_Name() FSharp.Compiler.AbstractIL.IL+ILFieldSpec: Void .ctor(ILFieldRef, ILType) +FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean HasAllowsRefStruct FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean HasDefaultConstructorConstraint FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean HasNotNullableValueTypeConstraint FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean HasReferenceTypeConstraint +FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean get_HasAllowsRefStruct() FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean get_HasDefaultConstructorConstraint() FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean get_HasNotNullableValueTypeConstraint() FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Boolean get_HasReferenceTypeConstraint() @@ -672,7 +674,7 @@ FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Microsoft.FSharp.Collection FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: System.String Name FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: System.String ToString() FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: System.String get_Name() -FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Void .ctor(System.String, Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILType], ILGenericVariance, Boolean, Boolean, Boolean, ILAttributesStored, Int32) +FSharp.Compiler.AbstractIL.IL+ILGenericParameterDef: Void .ctor(System.String, Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILType], ILGenericVariance, Boolean, Boolean, Boolean, Boolean, ILAttributesStored, Int32) FSharp.Compiler.AbstractIL.IL+ILGenericVariance+Tags: Int32 CoVariant FSharp.Compiler.AbstractIL.IL+ILGenericVariance+Tags: Int32 ContraVariant FSharp.Compiler.AbstractIL.IL+ILGenericVariance+Tags: Int32 NonVariant diff --git a/tests/fsharp/Compiler/Language/SpanTests.fs b/tests/fsharp/Compiler/Language/SpanTests.fs index f869ff80a58b..bd89f0fcbfe9 100644 --- a/tests/fsharp/Compiler/Language/SpanTests.fs +++ b/tests/fsharp/Compiler/Language/SpanTests.fs @@ -175,4 +175,22 @@ type IsByRefLikeAttribute() = inherit Attribute() type T(span: Span) = struct end """ [| |] + + [] + let ``A byref struct with custom attr can be passed as typar``() = + CompilerAssert.TypeCheckWithErrors """ +namespace System.Runtime.CompilerServices + +open System + +[] +type IsByRefLikeAttribute() = inherit Attribute() + +[] +type T(span: Span) = struct end + +module WhatEver = + let processT (a: Action, ie: seq, asList: list) = () + """ + [| FSharpDiagnosticSeverity.Error, 3300, (13, 45, 13, 51), "The parameter 'asList' has an invalid type 'T list'. This is not permitted by the rules of Common IL." |] #endif \ No newline at end of file