-
Notifications
You must be signed in to change notification settings - Fork 803
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
Allow extension methods without type attribute. #13839
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 -> int and module Foo
[<System.Runtime.CompilerServices.Extension>]
let PlusOne (a:int) = a + 1 will 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.