When [<MethodImpl(...)>] is applied to a property accessor, the compiler does not translate it into the method's IL implementation flags. Instead it emits it as an ordinary custom attribute, which has no effect on the runtime or on downstream tools.
MethodImplAttribute is a pseudo-custom-attribute: it must be encoded in the method's implflags, and it should never appear in metadata as a real custom attribute.
The failure is silent. The code compiles without a warning, and the attribute looks like it was applied.
Repro
module P
open System.Runtime.CompilerServices
[<AbstractClass; Sealed>]
type A =
static member P1 with [<MethodImpl(MethodImplOptions.AggressiveInlining)>] get () = 1
[<AbstractClass; Sealed>]
type B =
static member P2 with [<MethodImpl(MethodImplOptions.NoInlining)>] set (v: int) = ignore v
[<AbstractClass; Sealed>]
type C =
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
static member M1() = 1
Actual
Inspecting the emitted assembly:
A get_P1 implFlags=IL customAttrs=["MethodImplAttribute"]
B set_P2 implFlags=IL customAttrs=["MethodImplAttribute"]
C M1 implFlags=AggressiveInlining customAttrs=[]
The accessors get_P1 and set_P2 keep implFlags=IL, so the requested option is lost, and MethodImplAttribute is emitted as a custom attribute. The plain method C.M1 is correct.
Expected
get_P1 and set_P2 behave like C.M1: the option appears in implflags and no MethodImplAttribute custom attribute is emitted.
Notes
This is not specific to NoInlining. It applies to any MethodImplOptions carried by an accessor, including AggressiveInlining, Synchronized and PreserveSig, and it affects both getters and setters.
The cause looks like ComputeMethodImplAttribs running on an attribute list that has already had the accessor-applied attributes partitioned out, in IlxGen.fs (around the attrsAppliedToGetterOrSetter handling).
[<MethodImpl(...)>] on a property member (rather than inside the accessor) is not a workaround either, since that is rejected with error FS0842: This attribute cannot be applied to property, event, return value. Valid targets are: constructor, method. So a plain method is currently the only reliable way to request a method impl option.
Why it matters
This was found while making Array2D trim- and AOT-clean. A trimming feature switch needs MethodImplOptions.NoInlining so that ILLink and ILC still have a real call to substitute. Written as a property getter (the shape every BCL feature switch uses, such as RuntimeFeature.IsDynamicCodeSupported), the attribute silently disappeared, and the only reason the substitution still worked was an unrelated compiler internal. There is no way to assert the intended contract from the emitted metadata, because the flag is simply not there.
When
[<MethodImpl(...)>]is applied to a property accessor, the compiler does not translate it into the method's IL implementation flags. Instead it emits it as an ordinary custom attribute, which has no effect on the runtime or on downstream tools.MethodImplAttributeis a pseudo-custom-attribute: it must be encoded in the method'simplflags, and it should never appear in metadata as a real custom attribute.The failure is silent. The code compiles without a warning, and the attribute looks like it was applied.
Repro
Actual
Inspecting the emitted assembly:
The accessors
get_P1andset_P2keepimplFlags=IL, so the requested option is lost, andMethodImplAttributeis emitted as a custom attribute. The plain methodC.M1is correct.Expected
get_P1andset_P2behave likeC.M1: the option appears inimplflagsand noMethodImplAttributecustom attribute is emitted.Notes
This is not specific to
NoInlining. It applies to anyMethodImplOptionscarried by an accessor, includingAggressiveInlining,SynchronizedandPreserveSig, and it affects both getters and setters.The cause looks like
ComputeMethodImplAttribsrunning on an attribute list that has already had the accessor-applied attributes partitioned out, inIlxGen.fs(around theattrsAppliedToGetterOrSetterhandling).[<MethodImpl(...)>]on a property member (rather than inside the accessor) is not a workaround either, since that is rejected witherror FS0842: This attribute cannot be applied to property, event, return value. Valid targets are: constructor, method. So a plain method is currently the only reliable way to request a method impl option.Why it matters
This was found while making
Array2Dtrim- and AOT-clean. A trimming feature switch needsMethodImplOptions.NoInliningso that ILLink and ILC still have a real call to substitute. Written as a property getter (the shape every BCL feature switch uses, such asRuntimeFeature.IsDynamicCodeSupported), the attribute silently disappeared, and the only reason the substitution still worked was an unrelated compiler internal. There is no way to assert the intended contract from the emitted metadata, because the flag is simply not there.