From 41a384b7abd05c820d090868137ea0ac9d4a2952 Mon Sep 17 00:00:00 2001 From: David Naylor Date: Wed, 22 Oct 2025 22:50:24 +0200 Subject: [PATCH] Respect the return qualifier for attributes on class methods --- FSharp.sln | 4 + .../.FSharp.Compiler.Service/11.0.0.md | 3 +- .../Checking/Expressions/CheckExpressions.fs | 157 +++++++++--------- .../EmittedIL/Misc/Misc.fs | 8 +- .../Misc/ReturnAttributeOnClassMethod.fs | 5 + .../ReturnAttributeOnClassMethod.fs.il.bsl | 81 +++++++++ 6 files changed, 178 insertions(+), 80 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/EmittedIL/Misc/ReturnAttributeOnClassMethod.fs create mode 100644 tests/FSharp.Compiler.ComponentTests/EmittedIL/Misc/ReturnAttributeOnClassMethod.fs.il.bsl diff --git a/FSharp.sln b/FSharp.sln index 83933b62da3..bbda2fe2fa4 100644 --- a/FSharp.sln +++ b/FSharp.sln @@ -146,6 +146,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".FSharp.Compiler.Service", docs\release-notes\.FSharp.Compiler.Service\8.0.400.md = docs\release-notes\.FSharp.Compiler.Service\8.0.400.md docs\release-notes\.FSharp.Compiler.Service\9.0.100.md = docs\release-notes\.FSharp.Compiler.Service\9.0.100.md docs\release-notes\.FSharp.Compiler.Service\9.0.200.md = docs\release-notes\.FSharp.Compiler.Service\9.0.200.md + docs\release-notes\.FSharp.Compiler.Service\9.0.202.md = docs\release-notes\.FSharp.Compiler.Service\9.0.202.md + docs\release-notes\.FSharp.Compiler.Service\9.0.300.md = docs\release-notes\.FSharp.Compiler.Service\9.0.300.md + docs\release-notes\.FSharp.Compiler.Service\10.0.100.md = docs\release-notes\.FSharp.Compiler.Service\10.0.100.md + docs\release-notes\.FSharp.Compiler.Service\11.0.0.md = docs\release-notes\.FSharp.Compiler.Service\11.0.0.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".FSharp.Core", ".FSharp.Core", "{23798638-A1E9-4DAE-9C9C-F5D87499ADD6}" diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.0.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.0.md index 655ad02ad2a..cd8d5743784 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.0.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.0.md @@ -6,6 +6,7 @@ * Fix name is bound multiple times is not reported in 'as' pattern ([PR #18984](https://github.com/dotnet/fsharp/pull/18984)) * Fix: warn FS0049 on upper union case label. ([PR #19003](https://github.com/dotnet/fsharp/pull/19003)) * Type relations cache: handle potentially "infinite" types ([PR #19010](https://github.com/dotnet/fsharp/pull/19010)) +* Respect the return qualifier for attributes on class methods ([PR #19025](https://github.com/dotnet/fsharp/pull/19025)) ### Added @@ -13,4 +14,4 @@ * Parallel compilation stabilised and enabled by default ([PR #18998](https://github.com/dotnet/fsharp/pull/18998)) -### Breaking Changes \ No newline at end of file +### Breaking Changes diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 5d9197599be..d1d5b4a7a14 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -1023,7 +1023,7 @@ type TcCanFail = | IgnoreMemberResoutionError | IgnoreAllErrors | ReportAllErrors - + [] [] type TcTrueMatchClause = @@ -1896,9 +1896,9 @@ let FreshenAbstractSlot g amap m synTyparDecls absMethInfo = // Work out the required type of the member let argTysFromAbsSlot = argTys |> List.mapSquared (instType typarInstFromAbsSlot) - let retTyFromAbsSlot = - retTy - |> GetFSharpViewOfReturnType g + let retTyFromAbsSlot = + retTy + |> GetFSharpViewOfReturnType g |> instType typarInstFromAbsSlot typarsFromAbsSlotAreRigid, typarsFromAbsSlot, argTysFromAbsSlot, retTyFromAbsSlot @@ -2308,12 +2308,12 @@ module GeneralizationHelpers = | SynMemberKind.PropertySet | SynMemberKind.PropertyGetSet -> if not (isNil declaredTypars) then - let declaredTyparsRange = - declaredTypars + let declaredTyparsRange = + declaredTypars |> List.map(fun typar -> typar.Range) - + let m = declaredTyparsRange |> List.fold (fun r a -> unionRanges r a) range0 - + errorR(Error(FSComp.SR.tcPropertyRequiresExplicitTypeParameters(), m)) | SynMemberKind.Constructor -> if not (isNil declaredTypars) then @@ -2535,8 +2535,8 @@ module BindingNormalization = let warnOnUpper = if not args.IsEmpty then WarnOnUpperUnionCaseLabel - else AllIdsOK - + else AllIdsOK + match ResolvePatternLongIdent cenv.tcSink nameResolver warnOnUpper true m ad env.NameEnv TypeNameResolutionInfo.Default longId extraDot with | Item.NewDef id -> if id.idText = opNameCons then @@ -2991,9 +2991,9 @@ let TcRuntimeTypeTest isCast isOperator (cenv: cenv) denv m tgtTy srcTy = else error(Error(FSComp.SR.tcTypeTestErased(NicePrint.minimalStringOfType denv tgtTy, NicePrint.minimalStringOfType denv (stripTyEqnsWrtErasure EraseAll g tgtTy)), m)) else - let checkTrgtNullness = + let checkTrgtNullness = match (srcTy,g),(tgtTy,g) with - | (NullableRefType|NullTrueValue|NullableTypar), WithoutNullRefType when g.checkNullness && isCast -> + | (NullableRefType|NullTrueValue|NullableTypar), WithoutNullRefType when g.checkNullness && isCast -> let srcNice = NicePrint.minimalStringOfTypeWithNullness denv srcTy let tgtNice = NicePrint.minimalStringOfTypeWithNullness denv tgtTy warning(Error(FSComp.SR.tcDowncastFromNullableToWithoutNull(srcNice,tgtNice,tgtNice), m)) @@ -4854,9 +4854,9 @@ and TcTypesOrMeasures optKinds (cenv: cenv) newOk checkConstraints occ env tpenv match stripTyEqns cenv.g ttype with | TType_measure tm -> CheckUnitOfMeasureAttributes cenv.g tm | _ -> () - + ttypes, tpenv - + elif isNil kinds then error(Error(FSComp.SR.tcUnexpectedTypeArguments(), m)) else error(Error(FSComp.SR.tcTypeParameterArityMismatch((List.length kinds), (List.length args)), m)) @@ -5895,7 +5895,7 @@ and TcExprUndelayed (cenv: cenv) (overallTy: OverallTy) env tpenv (synExpr: SynE TcExprTuple cenv overallTy env tpenv (isExplicitStruct, args, m) | SynExpr.AnonRecd (isStruct, withExprOpt, unsortedFieldExprs, mWholeExpr, trivia) -> - match withExprOpt with + match withExprOpt with | None | IsSimpleOrBoundExpr -> TcNonControlFlowExpr env <| fun env -> TcPossiblyPropagatingExprLeafThenConvert (fun ty -> isAnonRecdTy g ty || isTyparTy g ty) cenv overallTy env mWholeExpr (fun overallTy -> @@ -5928,7 +5928,7 @@ and TcExprUndelayed (cenv: cenv) (overallTy: OverallTy) env tpenv (synExpr: SynE let binds = unionBindingAndMembers binds members TcExprObjectExpr cenv overallTy env tpenv (synObjTy, argopt, binds, extraImpls, mNewExpr, m) - | SynExpr.Record (inherits, withExprOpt, synRecdFields, mWholeExpr) -> + | SynExpr.Record (inherits, withExprOpt, synRecdFields, mWholeExpr) -> match withExprOpt with | None | IsSimpleOrBoundExpr -> TcNonControlFlowExpr env <| fun env -> @@ -6163,9 +6163,9 @@ and TcExprDowncast (cenv: cenv) overallTy env tpenv (synExpr, synInnerExpr, m) = // TcRuntimeTypeTest ensures tgtTy is a nominal type. Hence we can insert a check here // based on the nullness semantics of the nominal type. - let expr = + let expr = match (tgtTy,g) with - | NullTrueValue | NullableRefType | NullableTypar when g.checkNullness -> mkCallUnboxFast g m tgtTy innerExpr + | NullTrueValue | NullableRefType | NullableTypar when g.checkNullness -> mkCallUnboxFast g m tgtTy innerExpr | _ -> mkCallUnbox g m tgtTy innerExpr expr, tpenv @@ -6488,7 +6488,7 @@ and TcIteratedLambdas (cenv: cenv) isFirst (env: TcEnv) overallTy takenNames tpe parsedData |> Option.map fst |> Option.defaultValue [] - + let vs, (TcPatLinearEnv (tpenv, names, takenNames)) = cenv.TcSimplePats cenv isMember CheckCxs domainTy env (TcPatLinearEnv (tpenv, Map.empty, takenNames)) synSimplePats (parsedPatterns, isFirst) @@ -7294,7 +7294,7 @@ and TcObjectExpr (cenv: cenv) env tpenv (objTy, realObjTy, argopt, binds, extraI let overridesAndVirts, tpenv = ComputeObjectExprOverrides cenv env tpenv impls - // 2. check usage conditions + // 2. check usage conditions for ovd in overridesAndVirts do let (m, implTy, dispatchSlots, dispatchSlotsKeyed, availPriorOverrides, overrides) = ovd let overrideSpecs = overrides |> List.map fst @@ -7719,7 +7719,7 @@ and TcConstExpr cenv (overallTy: OverallTy) env m tpenv c = | TType_fun(rangeType= rangeType) -> checkAttributeInMeasure rangeType | TType_measure tm -> CheckUnitOfMeasureAttributes g tm | _ -> () - + checkAttributeInMeasure cTy Expr.Const (c', m, cTy), cTy, tpenv) @@ -9312,7 +9312,7 @@ and TcValueItemThen cenv overallTy env vref tpenv mItem afterResolution delayed PropagateThenTcDelayed cenv overallTy env tpenv mExprAndTypeArgs vexpFlex vexpFlex.Type ExprAtomicFlag.Atomic otherDelayed // Value get - + | _ -> let _, vExpr, isSpecial, _, _, tpenv = TcVal cenv env tpenv vref None (Some afterResolution) mItem @@ -9321,7 +9321,7 @@ and TcValueItemThen cenv overallTy env vref tpenv mItem afterResolution delayed | Expr.Const (Const.String value, _, _) -> TcConstStringExpr cenv overallTy env mItem tpenv value LiteralArgumentType.StaticField | _ -> vExpr, tpenv - let getCenvForVref cenv (vref:ValRef) = + let getCenvForVref cenv (vref:ValRef) = match TryFindLocalizedFSharpStringAttribute g g.attrib_WarnOnWithoutNullArgumentAttribute vref.Attribs with | Some _ as msg -> { cenv with css.WarnWhenUsingWithoutNullOnAWithNullTarget = msg} | None when cenv.css.WarnWhenUsingWithoutNullOnAWithNullTarget <> None -> @@ -10724,7 +10724,7 @@ and TcMatchClause cenv inputTy (resultTy: OverallTy) env isFirst tpenv synMatchC TcTrueMatchClause.Yes else TcTrueMatchClause.No - + let pat, whenExprOpt, vspecs, envinner, tpenv = TcMatchPattern cenv inputTy env tpenv synPat synWhenExprOpt isTrueMatchClause let resultEnv = @@ -10978,51 +10978,17 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt // { new Foo } -> SynExpr.ObjExpr // { new Foo() } -> SynExpr.ObjExpr // { New Foo with ... } -> SynExpr.ObjExpr - | SynExpr.ComputationExpr(false, SynExpr.New(_, targetType, expr, m), _) -> + | SynExpr.ComputationExpr(false, SynExpr.New(_, targetType, expr, m), _) -> false, SynExpr.ObjExpr(targetType, Some(expr, None), None, [], [], [], m, rhsExpr.Range), overallTy, overallTy | e -> false, e, overallTy, overallTy - // Check the attributes of the binding, parameters or return value - let TcAttrs tgt isRet attrs = - // For all but attributes positioned at the return value, disallow implicitly - // targeting the return value. - let tgtEx = if isRet then enum 0 else AttributeTargets.ReturnValue - let attrs, _ = TcAttributesMaybeFailEx TcCanFail.ReportAllErrors cenv envinner tgt tgtEx attrs - let attrs: Attrib list = attrs - if attrTgt = enum 0 && not (isNil attrs) then - for attr in attrs do - errorR(Error(FSComp.SR.tcAttributesAreNotPermittedOnLetBindings(), attr.Range)) - attrs - - // Rotate [] from binding to return value - // Also patch the syntactic representation - let retAttribs, valAttribs, valSynData = - let attribs = TcAttrs attrTgt false attrs - let rotRetSynAttrs, rotRetAttribs, valAttribs = - // Do not rotate if some attrs fail to typecheck... - if attribs.Length <> attrs.Length then [], [], attribs - else attribs - |> List.zip attrs - |> List.partition(function | _, Attrib(_, _, _, _, _, Some ts, _) -> ts &&& AttributeTargets.ReturnValue <> enum 0 | _ -> false) - |> fun (r, v) -> (List.map fst r, List.map snd r, List.map snd v) - let retAttribs = - match rtyOpt with - | Some (SynBindingReturnInfo(attributes = Attributes retAttrs)) -> - rotRetAttribs @ TcAttrs AttributeTargets.ReturnValue true retAttrs - | None -> rotRetAttribs - let valSynData = - match rotRetSynAttrs with - | [] -> valSynData - | {Range=mHead} :: _ -> - let (SynValData(valMf, SynValInfo(args, SynArgInfo(attrs, opt, retId)), valId)) = valSynData - SynValData(valMf, SynValInfo(args, SynArgInfo({Attributes=rotRetSynAttrs; Range=mHead} :: attrs, opt, retId)), valId) - retAttribs, valAttribs, valSynData + let retAttribs, valAttribs, valSynData = TcNormalizeReturnAttribs cenv envinner attrTgt attrs valSynData rtyOpt let isVolatile = HasFSharpAttribute g g.attrib_VolatileFieldAttribute valAttribs let inlineFlag = ComputeInlineFlag memberFlagsOpt isInline isMutable g valAttribs mBinding let argAttribs = - spatsL |> List.map (SynInfo.InferSynArgInfoFromSimplePats >> List.map (SynInfo.AttribsOfArgData >> TcAttrs AttributeTargets.Parameter false)) + spatsL |> List.map (SynInfo.InferSynArgInfoFromSimplePats >> List.map (SynInfo.AttribsOfArgData >> TcAttrs cenv envinner attrTgt AttributeTargets.Parameter false)) // Assert the return type of an active pattern. A [] attribute may be used on a partial active pattern. let isStructRetTy = HasFSharpAttribute g g.attrib_StructAttribute retAttribs @@ -11114,7 +11080,7 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt let isMultiCasePartialAP = memberFlagsOpt.IsNone && not apinfo.IsTotal && apinfo.ActiveTags.Length > 1 if isMultiCasePartialAP then errorR(Error(FSComp.SR.tcPartialActivePattern(), m)) - + if Option.isSome memberFlagsOpt && not spatsL.IsEmpty then errorR(Error(FSComp.SR.tcInvalidActivePatternName(apinfo.LogicalName), m)) @@ -11220,6 +11186,30 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt CheckedBindingInfo(inlineFlag, valAttribs, xmlDoc, tcPatPhase2, explicitTyparInfo, nameToPrelimValSchemeMap, rhsExprChecked, argAndRetAttribs, overallPatTy, mBinding, debugPoint, isCompGen, literalValue, isFixed), tpenv +// Rotate [] from binding to return value +// Also patch the syntactic representation +and TcNormalizeReturnAttribs cenv env attrTgt attrs valSynData rtyOpt= + let rotRetSynAttrs, rotRetAttribs, valAttribs = + let attribs = TcAttrs cenv env attrTgt attrTgt false attrs + // Do not rotate if some attrs fail to typecheck... + if List.length attribs <> List.length attrs then [], [], attribs + else attribs + |> List.zip attrs + |> List.partition(function | _, Attrib(_, _, _, _, _, Some ts, _) -> ts &&& AttributeTargets.ReturnValue <> enum 0 | _ -> false) + |> fun (r, v) -> (List.map fst r, List.map snd r, List.map snd v) + let retAttribs = + match rtyOpt with + | Some (SynBindingReturnInfo(attributes = Attributes retAttrs)) -> + rotRetAttribs @ TcAttrs cenv env attrTgt AttributeTargets.ReturnValue true retAttrs + | None -> rotRetAttribs + let valSynData = + match rotRetSynAttrs with + | [] -> valSynData + | {Range=mHead} :: _ -> + let (SynValData(valMf, SynValInfo(args, SynArgInfo(attrs, opt, retId)), valId)) = valSynData + SynValData(valMf, SynValInfo(args, SynArgInfo({Attributes=rotRetSynAttrs; Range=mHead} :: attrs, opt, retId)), valId) + retAttribs, valAttribs, valSynData + // Note: // - Let bound values can only have attributes that uses AttributeTargets.Field ||| AttributeTargets.Property ||| AttributeTargets.ReturnValue // - Let function bindings can only have attributes that uses AttributeTargets.Method ||| AttributeTargets.ReturnValue @@ -11308,9 +11298,9 @@ and TcNonRecursiveBinding declKind cenv env tpenv ty binding = and ResolveAttributeType (cenv: cenv) (env: TcEnv) (mAttr: range) (tycon: Ident list) = let tpenv = emptyUnscopedTyparEnv let ad = env.eAccessRights - + let tyPath, tyId = List.frontAndBack tycon - + let try1 n = let tyid = mkSynId tyId.idRange n let tycon = (tyPath @ [tyid]) @@ -11352,7 +11342,7 @@ and CheckAttributeUsage (g: TcGlobals) (mAttr: range) (tcref: TyconRef) (attrTgt validOnDefault, inheritedDefault | _ -> validOnDefault, inheritedDefault - + // Determine valid attribute targets let attributeTargets = enum validOn &&& attrTgt let directedTargets = @@ -11362,7 +11352,7 @@ and CheckAttributeUsage (g: TcGlobals) (mAttr: range) (tcref: TyconRef) (attrTgt errorR(Error(FSComp.SR.tcUnrecognizedAttributeTarget(), attrTarget.idRange)) attributeTargets | ShortFormAttributeTarget -> attributeTargets &&& ~~~ attrEx - + let constrainedTargets = attributeTargets &&& directedTargets // Check if attribute is valid for the target @@ -11393,7 +11383,7 @@ and CheckAttributeUsage (g: TcGlobals) (mAttr: range) (tcref: TyconRef) (attrTgt let allowedTargets = attributeTargetsToString validOn warning(InvalidAttributeTargetForLanguageElement(elementTargets, allowedTargets, mAttr)) - + constrainedTargets //------------------------------------------------------------------------- @@ -11450,12 +11440,12 @@ and TcAttributeEx canFail (cenv: cenv) (env: TcEnv) attrTgt attrEx (synAttr: Syn let mkAttribExpr e = AttribExpr(e, EvalLiteralExprOrAttribArg g e) - + let checkPropSetterAttribAccess m (pinfo: PropInfo) = let setterMeth = pinfo.SetterMethod if not <| IsTypeAndMethInfoAccessible cenv.amap m ad ad setterMeth then - errorR(Error (FSComp.SR.tcPropertyCannotBeSetPrivateSetter(pinfo.PropertyName), m)) - + errorR(Error (FSComp.SR.tcPropertyCannotBeSetPrivateSetter(pinfo.PropertyName), m)) + let namedAttribArgMap = attributeAssignedNamedItems |> List.map (fun (CallerNamedArg(id, CallerArg(callerArgTy, m, isOpt, callerArgExpr))) -> if isOpt then error(Error(FSComp.SR.tcOptionalArgumentsCannotBeUsedInCustomAttribute(), m)) @@ -11547,6 +11537,17 @@ and TcAttributesCanFail cenv env attrTgt synAttribs = and TcAttributes cenv env attrTgt synAttribs = TcAttributesMaybeFail TcCanFail.ReportAllErrors cenv env attrTgt synAttribs |> fst +// Check the attributes of the binding, parameters or return value +and TcAttrs cenv env attrTgt tgt isRet attrs = + // For all but attributes positioned at the return value, disallow implicitly + // targeting the return value. + let tgtEx = if isRet then enum 0 else AttributeTargets.ReturnValue + let attrs, _ = TcAttributesMaybeFailEx TcCanFail.ReportAllErrors cenv env tgt tgtEx attrs + if attrTgt = enum 0 && not (isNil attrs) then + for attr in attrs do + errorR(Error(FSComp.SR.tcAttributesAreNotPermittedOnLetBindings(), attr.Range)) + attrs + //------------------------------------------------------------------------- // TcLetBinding //------------------------------------------------------------------------ @@ -11837,7 +11838,7 @@ and ApplyAbstractSlotInference (cenv: cenv) (envinner: TcEnv) (_: Val option) (a // 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). // We only keep a WithNull nullness if it was part of an explicit type instantiation - let canChangeNullableRetTy = + let canChangeNullableRetTy = match g.checkNullness, renaming with | false, _ -> false | true, [] -> true @@ -11850,10 +11851,10 @@ and ApplyAbstractSlotInference (cenv: cenv) (envinner: TcEnv) (_: Val option) (a let declaredTypars = (if typarsFromAbsSlotAreRigid then typarsFromAbsSlot else declaredTypars) - let retTyFromAbsSlot = + let retTyFromAbsSlot = if canChangeNullableRetTy then retTyFromAbsSlot |> changeWithNullReqTyToVariable g - else retTyFromAbsSlot + else retTyFromAbsSlot let absSlotTy = mkMethodTy g argTysFromAbsSlot retTyFromAbsSlot @@ -12246,14 +12247,14 @@ and AnalyzeAndMakeAndPublishRecursiveValue // Pull apart the inputs let (NormalizedBinding(vis1, bindingKind, isInline, isMutable, bindingSynAttribs, bindingXmlDoc, synTyparDecls, valSynData, declPattern, bindingRhs, mBinding, debugPoint)) = binding - let (NormalizedBindingRhs(_, _, bindingExpr)) = bindingRhs - let (SynValData(memberFlagsOpt, valSynInfo, thisIdOpt)) = valSynData + let (NormalizedBindingRhs(_, rtyOpt, bindingExpr)) = bindingRhs + let (SynValData(memberFlagsOpt, _, thisIdOpt)) = valSynData let (ContainerInfo(altActualParent, tcrefContainerInfo)) = containerInfo let attrTgt = declKind.AllowedAttribTargets memberFlagsOpt // Check the attributes on the declaration - let bindingAttribs = TcAttributes cenv env attrTgt bindingSynAttribs + let _, bindingAttribs, (SynValData(_, valSynInfo, _) as valSynData) = TcNormalizeReturnAttribs cenv env attrTgt bindingSynAttribs valSynData rtyOpt // Allocate the type inference variable for the inferred type let ty = NewInferenceType g @@ -12896,7 +12897,7 @@ let TcAndPublishValSpec (cenv: cenv, env, containerInfo: ContainerInfo, declKind let valinfos, tpenv = TcValSpec cenv env declKind newOk containerInfo memFlagsOpt None tpenv synValSig attrs let denv = env.DisplayEnv - let viss = + let viss = match memFlagsOpt with | Some ({MemberKind = SynMemberKind.PropertyGetSet as propKind}) -> let getterAccess, setterAccess = getGetterSetterAccess vis propKind g.langVersion @@ -12963,4 +12964,4 @@ let TcAndPublishValSpec (cenv: cenv, env, containerInfo: ContainerInfo, declKind assert(vspec.InlineInfo = inlineFlag) - vspec, tpenv) \ No newline at end of file + vspec, tpenv) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Misc/Misc.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Misc/Misc.fs index c21c35496f3..c5ee285d626 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Misc/Misc.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Misc/Misc.fs @@ -1,7 +1,6 @@ namespace EmittedIL.RealInternalSignature open Xunit -open System.IO open FSharp.Test open FSharp.Test.Compiler @@ -218,3 +217,10 @@ module Misc = |> getCompilation |> asExe |> verifyCompilation + + [] + let ``ReturnAttributeOnClassMethod_fs`` compilation = + compilation + |> getCompilation + |> asExe + |> verifyCompilation diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Misc/ReturnAttributeOnClassMethod.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Misc/ReturnAttributeOnClassMethod.fs new file mode 100644 index 00000000000..f05effd0999 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Misc/ReturnAttributeOnClassMethod.fs @@ -0,0 +1,5 @@ +open System.Diagnostics.CodeAnalysis + +type Class() = + [] + static member ClassMethod () = obj() diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Misc/ReturnAttributeOnClassMethod.fs.il.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Misc/ReturnAttributeOnClassMethod.fs.il.bsl new file mode 100644 index 00000000000..12357dfe9c7 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Misc/ReturnAttributeOnClassMethod.fs.il.bsl @@ -0,0 +1,81 @@ + + + + + +.assembly extern runtime { } +.assembly extern FSharp.Core { } +.assembly assembly +{ + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.FSharpInterfaceDataVersionAttribute::.ctor(int32, + int32, + int32) = ( 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 ) + + + + + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module assembly.exe + +.imagebase {value} +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 +.corflags 0x00000001 + + + + + +.class public abstract auto ansi sealed assembly + extends [runtime]System.Object +{ + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .class auto ansi serializable nested public Class + extends [runtime]System.Object + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 ) + .method public specialname rtspecialname instance void .ctor() cil managed + { + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: callvirt instance void [runtime]System.Object::.ctor() + IL_0006: ldarg.0 + IL_0007: pop + IL_0008: ret + } + + .method public static object ClassMethod() cil managed + { + .param [0] + .custom instance void [runtime]System.Diagnostics.CodeAnalysis.NotNullAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: newobj instance void [runtime]System.Object::.ctor() + IL_0005: ret + } + + } + +} + +.class private abstract auto ansi sealed ''.$assembly + extends [runtime]System.Object +{ + .method public static void main@() cil managed + { + .entrypoint + + .maxstack 8 + IL_0000: ret + } + +} + + + + +