Uh oh!
There was an error while loading. Please reload this page.
Allow extension methods without type attribute. - #13839
Conversation
283ea86 to
dc66673CompareAs discussed on Slack: Probably a different approach will work better here - if type has static method(s) with |
As @vzarytovskii mentions, and same is true for assembly, and possibly module (the latter I'm not sure of, it wasn't clear from the suggestions thread, I don't know if C# also adds it to .NET modules). Also, assuming there's an upcoming RFC, can we specify there what kind of members will be "detected" and what rules specifically apply? |
Regarding of detecting those, it is already in place, and probably should be changed to not check enclosing type, may be used as reference: fsharp/src/Compiler/Checking/NameResolution.fs Lines 509 to 515 in e06e079 |
dc66673 to
44044afCompare4870254 to
f34a854Compareedgarfgp
commented
Sep 17, 2022
Humm. There seems to be some error regarding vsintegration that have not touched . Maybe we could rerun the CI to see if that helps |
nojaf
commented
Sep 21, 2022
@edgarfgp I should open the What happens when you have: moduleFoo[<System.Runtime.CompilerServices.Extension>]valPlusOne:a:int ->intand moduleFoo[<System.Runtime.CompilerServices.Extension>]letPlusOne(a:int)= a +1will that still be picked up? |
edgarfgp
commented
Sep 21, 2022
I think for the sake of completeness. We should look at it and see how doable is :) |
edgarfgp
commented
Sep 21, 2022
@vzarytovskii Should we put this under a preview flag ? |
smoothdeveloper
commented
Sep 21, 2022
@edgarfgp can there be a test that it works with VB.NET too? |
@smoothdeveloper As far I can see there is not way to do that in the compiler. If you know how please let me know :) Update : We have tested with a local compiler version with a VB project and works Ok |
smoothdeveloper
commented
Sep 21, 2022
@Edgarfp this test checks things among F#, C# & VB.NET: Lines 2069 to 2074 in eaa723d Maybe there is a way which is similar with string literal with code in it in the unit tests, albeit I don't favour having those written like this, and generally prefer to deal with standalone code files.
Can you confirm if this works without adding the Extension attribute to the assembly? |
vzarytovskii
commented
Sep 21, 2022
I would say so, yes. Better guard it by the preview version. |
nojaf
commented
Sep 22, 2022
This is outside the scope of our proposal. The fact that VB still needs the assembly attribute is no change in the current behaviour. |
dc12d16 to
248cc8eCompare08041ab to
c97ca98Compare31e32b2 to
a336508CompareT-Gro
commented
Nov 3, 2022
Passing now, please approve :) |
Uh oh!
There was an error while loading. Please reload this page.
edgarfgp
commented
Nov 3, 2022
@T-Gro You are an absolute legend. Thanks for the help. |
| let thisTyconRef = | ||
| try | ||
| let rs = | ||
| match metadataOfTycon tcrefOfStaticClass.Deref, minfo with | ||
| | ILTypeMetadata (TILObjectReprData(scoref, _, _)), ILMeth(_, ILMethInfo(_, _, _, ilMethod, _), _) -> | ||
| match ilMethod.ParameterTypes with | ||
| | firstTy :: _ -> | ||
| match firstTy with | ||
| | ILType.Boxed tspec | ILType.Value tspec -> | ||
| let tref = (tspec |> rescopeILTypeSpec scoref).TypeRef | ||
| if Import.CanImportILTypeRef amap m tref then | ||
| let tcref = tref |> Import.ImportILTypeRef amap m | ||
| if isCompiledTupleTyconRef g tcref || tyconRefEq g tcref g.fastFunc_tcr then None | ||
| else Some tcref | ||
| else None | ||
| | _ -> None | ||
| | _ -> None | ||
| | _ -> | ||
| // The results are indexed by the TyconRef of the first 'this' argument, if any. | ||
| // So we need to go and crack the type of the 'this' argument. | ||
| let thisTy = minfo.GetParamTypes(amap, m, generalizeTypars minfo.FormalMethodTypars).Head.Head | ||
| match thisTy with | ||
| | AppTy g (tcrefOfTypeExtended, _) when not (isByrefTy g thisTy) -> Some tcrefOfTypeExtended | ||
| | _ -> None |
There was a problem hiding this comment.
This seems to duplicate the code up above - could we have factored this out to prevent the duplication?
| let ty = generalizedTyconRef g tcrefOfStaticClass | ||
| let minfos = | ||
| GetImmediateIntrinsicMethInfosOfType (None, AccessorDomain.AccessibleFromSomeFSharpCode) g amap m ty | ||
| |> List.filter (IsMethInfoPlainCSharpStyleExtensionMember g m true) |
There was a problem hiding this comment.
I'm concerned about the performance implications of this preview feature. If my late-night analysis is correct it seems to me we're now looking into the methods of every type in every opened namespace. This must be traversing a lot more metadata than previously. This will be very significant both from a perf question - think what happens if there are types in referenced assemblies that were previously unused that contain 100,000 methods. It also has some correctness implications see #15158.
Given the feature being implemented this is a little tricky to avoid. We really have to be looking for the ExtensionAttribute that mark the types before we traverse them, and avoiding the traversal for types that do not contain extension methods. That's really what the attributes are for.
Now, these attributes will be correctly present in compiled reference assemblies because they are either explicit or added in CheckDeclarations, so for referenced assemblies we can just look at them. However these attributes are not necessarily present yet in the assembly being compiled - e.g. in the case of namespace rec where one part of the recursive set of types opens another before the attributes have been fully stitched in.
My (late night dont-take-as-gospel) recommendation is that this code be adjusted to look for ExtensionAttribute, except in the assembly being compiled. So something like:
if g.langVersion.SupportsFeature(LanguageFeature.CSharpExtensionAttributeNotRequired) then
let ty = generalizedTyconRef g tcrefOfStaticClass
let csharpStyleExtensionMembers = if IsTyconRefUsedForCSharpStyleExtensionMembers g m tcrefOfStaticClass || tcrefOfStaticClass.IsLocalRef then
GetImmediateIntrinsicMethInfosOfType (None, AccessorDomain.AccessibleFromSomeFSharpCode) g amap m ty
|> List.filter (IsMethInfoPlainCSharpStyleExtensionMember g m true)
else
[]
if not minfos.IsEmpty then
let pri = NextExtensionMethodPriority()
@edgarfgp and I are trying to address fsharp/fslang-suggestions#835.
It currently works when the extension method is being consumed in the same F# project.
It doesn't appear to be exposed when consumed in a C# project.
I'm probably still missing something and I could use a pointer.