-
Couldn't load subscription status.
- Fork 833
Allow extension methods without type attribute. #13839
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
283ea86 to
dc66673
Compare
|
As 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
44044af
Compare
4870254 to
f34a854
Compare
|
Humm. There seems to be some error regarding vsintegration that have not touched . Maybe we could rerun the CI to see if that helps |
|
@edgarfgp I should open the What happens when you have: module Foo
[<System.Runtime.CompilerServices.Extension>]
val PlusOne: a: int -> intand module Foo
[<System.Runtime.CompilerServices.Extension>]
let PlusOne (a:int) = a + 1will that still be picked up? |
I think for the sake of completeness. We should look at it and see how doable is :) |
|
@vzarytovskii Should we put this under a preview flag ? |
|
@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 |
|
@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? |
I would say so, yes. Better guard it by the preview version. |
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
248cc8e
Compare
08041ab to
c97ca98
Compare
31e32b2 to
a336508
Compare
|
Passing now, please approve :) |
|
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.