Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@

### Improved

* IL: cache C# extension methods per CCU ([PR #20256](https://github.com/dotnet/fsharp/pull/20256))
* Nullness warning FS3261 on dotted method or property access (e.g. `x.Member`) now underlines the receiver expression and includes the member name and (when known) the binding name in the message. ([Issue #19658](https://github.com/dotnet/fsharp/issues/19658), [PR #19814](https://github.com/dotnet/fsharp/pull/19814))
* Direct delegate construction ([PR ##19993](https://github.com/dotnet/fsharp/pull/19993))

Expand Down
60 changes: 44 additions & 16 deletions src/Compiler/Checking/NameResolution.fs
Original file line number Diff line number Diff line change
Expand Up @@ -587,28 +587,16 @@ let GetTyconRefForExtensionMembers minfo (deref: Entity) amap m g =
None

/// Get the info for all the .NET-style extension members listed as static members in the type.
let private GetCSharpStyleIndexedExtensionMembersForTyconRef (amap: Import.ImportMap) m (tcrefOfStaticClass: TyconRef) =
let private ComputeCSharpStyleExtensionMembers (amap: Import.ImportMap) m (tcrefOfStaticClass: TyconRef) (importFailed: bool ref) =
let g = amap.g

let isApplicable =
IsTyconRefUsedForCSharpStyleExtensionMembers g tcrefOfStaticClass ||

g.langVersion.SupportsFeature(LanguageFeature.CSharpExtensionAttributeNotRequired) &&
tcrefOfStaticClass.IsLocalRef &&
not tcrefOfStaticClass.IsTypeAbbrev

if not isApplicable then [] else

let ty = generalizedTyconRef g tcrefOfStaticClass
let pri = NextExtensionMethodPriority()

let methods =
protectAssemblyExploration []
(fun () -> GetImmediateIntrinsicMethInfosOfType (None, AccessorDomain.AccessibleFromSomeFSharpCode) g amap m ty)

[ for minfo in methods do
if IsMethInfoPlainCSharpStyleExtensionMember g m true minfo then
let ilExtMem = ILExtMem (tcrefOfStaticClass, minfo, pri)
// 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.
//
Expand All @@ -620,9 +608,49 @@ let private GetCSharpStyleIndexedExtensionMembersForTyconRef (amap: Import.Impor
// methods for tuple occur in C# code)
let thisTyconRef = GetTyconRefForExtensionMembers minfo tcrefOfStaticClass.Deref amap m g
match thisTyconRef with
| None -> ()
| Some (Some tcref) -> yield Choice1Of2(tcref, ilExtMem)
| Some None -> yield Choice2Of2 ilExtMem ]
| None -> importFailed.Value <- true
| Some thisTyconRefOpt -> yield (thisTyconRefOpt, minfo) ]

let private GetCSharpStyleIndexedExtensionMembersForTyconRef (amap: Import.ImportMap) m (tcrefOfStaticClass: TyconRef) =
let g = amap.g

let isApplicable =
IsTyconRefUsedForCSharpStyleExtensionMembers g tcrefOfStaticClass ||

g.langVersion.SupportsFeature(LanguageFeature.CSharpExtensionAttributeNotRequired) &&
tcrefOfStaticClass.IsLocalRef &&
not tcrefOfStaticClass.IsTypeAbbrev

// Checked before touching the CCU or its cache, so a non-extension class costs nothing extra.
if not isApplicable then [] else

let shape =
// A local class is still gaining members while its own file is checked; only an imported one is
// immutable enough to share. The cache lives on the CCU, so it goes when the project does.
if tcrefOfStaticClass.IsLocalRef then
ComputeCSharpStyleExtensionMembers amap m tcrefOfStaticClass (ref false)
else
let cache = tcrefOfStaticClass.nlr.Ccu.Deref.CSharpStyleExtensionMembersCache
match cache.TryGetValue tcrefOfStaticClass.Stamp with
| true, shape -> shape :?> (TyconRef option * MethInfo) list
| _ ->
let importFailed = ref false
let shape = ComputeCSharpStyleExtensionMembers amap m tcrefOfStaticClass importFailed

if not importFailed.Value then
cache.TryAdd(tcrefOfStaticClass.Stamp, (shape :> obj)) |> ignore

shape

if List.isEmpty shape then [] else

let pri = NextExtensionMethodPriority()

[ for thisTyconRefOpt, minfo in shape do
let ilExtMem = ILExtMem (tcrefOfStaticClass, minfo, pri)
match thisTyconRefOpt with
| Some tcref -> yield Choice1Of2(tcref, ilExtMem)
| None -> yield Choice2Of2 ilExtMem ]

/// Query the declared properties of a type (including inherited properties)
let IntrinsicPropInfosOfTypeInScope (infoReader: InfoReader) optFilter ad findFlag m ty =
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Checking/import.fs
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@ let ImportILAssembly(amap: unit -> ImportMap, m, auxModuleLoader, xmlDocInfoLoad
MemberSignatureEquality= (fun ty1 ty2 -> typeEquivAux EraseAll (amap()).g ty1 ty2)
TryGetILModuleDef = (fun () -> Some ilModule)
TypeForwarders = forwarders
CSharpStyleExtensionMembersCache = ConcurrentDictionary(1, 0)
XmlDocumentationInfo =
match xmlDocInfoLoader, fileName with
| Some xmlDocInfoLoader, Some fileName -> xmlDocInfoLoader.TryLoad(fileName)
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/Driver/CompilerImports.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
module internal FSharp.Compiler.CompilerImports

open System
open System.Collections.Concurrent
open System.Collections.Generic
open System.Diagnostics
open System.IO
Expand Down Expand Up @@ -1532,6 +1533,7 @@ and [<Sealed>] TcImports
ImportProvidedType = (fun ty -> ImportProvidedType (tcImports.GetImportMap()) m ty)
TryGetILModuleDef = (fun () -> Some ilModule)
TypeForwarders = CcuTypeForwarderTable.Empty
CSharpStyleExtensionMembersCache = ConcurrentDictionary(1, 0)
XmlDocumentationInfo =
match tcConfig.xmlDocInfoLoader with
| Some xmlDocInfoLoader -> xmlDocInfoLoader.TryLoad(fileName)
Expand Down Expand Up @@ -2156,6 +2158,7 @@ and [<Sealed>] TcImports
UsesFSharp20PlusQuotations = minfo.usesQuotations
MemberSignatureEquality = (fun ty1 ty2 -> typeEquivAux EraseAll (tcImports.GetTcGlobals()) ty1 ty2)
TypeForwarders = ImportILAssemblyTypeForwarders(tcImports.GetImportMap, m, ilModule.GetRawTypeForwarders())
CSharpStyleExtensionMembersCache = ConcurrentDictionary(1, 0)
#if !NO_TYPEPROVIDERS
XmlDocumentationInfo =
match tcConfig.xmlDocInfoLoader with
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/Driver/ParseAndCheckInputs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module internal FSharp.Compiler.ParseAndCheckInputs
open System
open System.IO
open System.Threading
open System.Collections.Concurrent
open System.Collections.Generic

open FSharp.Compiler.Parser
Expand Down Expand Up @@ -1098,6 +1099,7 @@ let GetInitialTcState (m, ccuName, tcConfig: TcConfig, tcGlobals, tcImports: TcI
Contents = ccuContents
MemberSignatureEquality = typeEquivAux EraseAll tcGlobals
TypeForwarders = CcuTypeForwarderTable.Empty
CSharpStyleExtensionMembersCache = ConcurrentDictionary(1, 0)
XmlDocumentationInfo = None
}

Expand Down
4 changes: 3 additions & 1 deletion src/Compiler/TypedTree/TypedTree.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5888,7 +5888,9 @@ type CcuData =

/// The table of .NET CLI type forwarders for this assembly
TypeForwarders: CcuTypeForwarderTable


CSharpStyleExtensionMembersCache: ConcurrentDictionary<Stamp, obj>

XmlDocumentationInfo: XmlDocumentationInfo option }

[<DebuggerBrowsable(DebuggerBrowsableState.Never)>]
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/TypedTree/TypedTree.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module internal rec FSharp.Compiler.TypedTree

open System
open System.Diagnostics
open System.Collections.Concurrent
open System.Collections.Generic
open System.Collections.Immutable
open Internal.Utilities.Collections
Expand Down Expand Up @@ -4228,6 +4229,10 @@ type CcuData =

/// The table of .NET CLI type forwarders for this assembly
TypeForwarders: CcuTypeForwarderTable

/// C#-style extension members of this assembly's static classes, keyed by static class stamp.
/// Typed as obj because MethInfo is declared after this file.
CSharpStyleExtensionMembersCache: ConcurrentDictionary<Stamp, obj>
XmlDocumentationInfo: XmlDocumentationInfo option
}

Expand Down
Loading