Skip to content
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

Replace Map with Dictionary #13044

Merged
merged 10 commits into from
May 13, 2022
Merged
74 changes: 55 additions & 19 deletions src/Compiler/Checking/import.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module internal FSharp.Compiler.Import

open System.Collections.Concurrent
open System.Collections.Generic
open System.Collections.Immutable
open Internal.Utilities.Library
open Internal.Utilities.Library.Extras
open FSharp.Compiler
Expand Down Expand Up @@ -586,25 +587,60 @@ let ImportILAssemblyTypeDefs (amap, m, auxModLoader, aref, mainmod: ILModuleDef)
CombineCcuContentFragments m (mainmod :: mtypsForExportedTypes)

/// Import the type forwarder table for an IL assembly
let ImportILAssemblyTypeForwarders (amap, m, exportedTypes: ILExportedTypesAndForwarders) =
// Note 'td' may be in another module or another assembly!
// Note: it is very important that we call auxModLoader lazily
[ //printfn "reading forwarders..."
for exportedType in exportedTypes.AsList() do
let ImportILAssemblyTypeForwarders (amap, m, exportedTypes: ILExportedTypesAndForwarders): CcuTypeForwarderTable =
let rec addToTree tree path item value =
match path with
| [] ->
{ tree with
Children =
tree.Children.Add(
item,
{ Value = Some value
Children = ImmutableDictionary.Empty }
) }
| nodeKey :: rest ->
match tree.Children.TryGetValue(nodeKey) with
| true, subTree -> { tree with Children = tree.Children.SetItem(nodeKey, addToTree subTree rest item value) }
| false, _ -> { tree with Children = tree.Children.Add(nodeKey, mkTreeWith rest item value) }

and mkTreeWith path item value =
match path with
| [] ->
{ Value = None
Children =
ImmutableDictionary.Empty.Add(
item,
{ Value = Some value
Children = ImmutableDictionary.Empty }
) }
| nodeKey :: rest ->
{ Value = None
Children = ImmutableDictionary.Empty.Add(nodeKey, mkTreeWith rest item value) }

let rec addNested
(exportedType: ILExportedTypeOrForwarder)
(nets: ILNestedExportedTypes)
(enc: string list)
(tree: CcuTypeForwarderTree<string, Lazy<EntityRef>>)
: CcuTypeForwarderTree<string, Lazy<EntityRef>> =
(tree, nets.AsList())
||> List.fold(fun tree net ->
let tcref = lazy ImportILTypeRefUncached (amap ()) m (ILTypeRef.Create(exportedType.ScopeRef, enc, net.Name))
addToTree tree enc exportedType.Name tcref
|> addNested exportedType net.Nested [yield! enc; yield net.Name])

match exportedTypes.AsList() with
| [] -> CcuTypeForwarderTable.Empty
| rootTypes ->
({ Value = None; Children = ImmutableDictionary.Empty } , rootTypes)
||> List.fold(fun tree exportedType ->
let ns, n = splitILTypeName exportedType.Name
//printfn "found forwarder for %s..." n
let tcref = lazy ImportILTypeRefUncached (amap()) m (ILTypeRef.Create(exportedType.ScopeRef, [], exportedType.Name))
yield (Array.ofList ns, n), tcref
let rec nested (nets: ILNestedExportedTypes) enc =
[ for net in nets.AsList() do

//printfn "found nested forwarder for %s..." net.Name
let tcref = lazy ImportILTypeRefUncached (amap()) m (ILTypeRef.Create (exportedType.ScopeRef, enc, net.Name))
yield (Array.ofList enc, exportedType.Name), tcref
yield! nested net.Nested (enc @ [ net.Name ]) ]
yield! nested exportedType.Nested (ns@[n])
] |> Map.ofList

let tcref = lazy ImportILTypeRefUncached (amap ()) m (ILTypeRef.Create(exportedType.ScopeRef, [], exportedType.Name))
addToTree tree ns n tcref
|> addNested exportedType exportedType.Nested [yield! ns; yield n]
)
|> fun root -> { Root = root }

/// Import an IL assembly as a new TAST CCU
let ImportILAssembly(amap: unit -> ImportMap, m, auxModuleLoader, xmlDocInfoLoader: IXmlDocumentationInfoLoader option, ilScopeRef, sourceDir, fileName, ilModule: ILModuleDef, invalidateCcu: IEvent<string>) =
invalidateCcu |> ignore
Expand All @@ -616,7 +652,7 @@ let ImportILAssembly(amap: unit -> ImportMap, m, auxModuleLoader, xmlDocInfoLoad
let mty = ImportILAssemblyTypeDefs(amap, m, auxModuleLoader, aref, ilModule)
let forwarders =
match ilModule.Manifest with
| None -> Map.empty
| None -> CcuTypeForwarderTable.Empty
| Some manifest -> ImportILAssemblyTypeForwarders(amap, m, manifest.ExportedTypes)

let ccuData: CcuData =
Expand Down
3 changes: 2 additions & 1 deletion src/Compiler/Checking/import.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/// Functions to import .NET binary metadata as TAST objects
module internal FSharp.Compiler.Import

open System.Collections.Immutable
open Internal.Utilities.Library
open FSharp.Compiler.AbstractIL.IL
open FSharp.Compiler.TcGlobals
Expand Down Expand Up @@ -96,7 +97,7 @@ val internal ImportILAssembly:

/// Import the type forwarder table for an IL assembly
val internal ImportILAssemblyTypeForwarders:
(unit -> ImportMap) * range * ILExportedTypesAndForwarders -> Map<string array * string, Lazy<EntityRef>>
(unit -> ImportMap) * range * ILExportedTypesAndForwarders -> CcuTypeForwarderTable

/// Import an IL type as an F# type, first rescoping to view the metadata from the current assembly
/// being compiled. importInst gives the context for interpreting type variables.
Expand Down
3 changes: 2 additions & 1 deletion src/Compiler/Driver/CompilerImports.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module internal FSharp.Compiler.CompilerImports

open System
open System.Collections.Generic
open System.Collections.Immutable
open System.Diagnostics
open System.IO

Expand Down Expand Up @@ -1114,7 +1115,7 @@ and [<Sealed>] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse
MemberSignatureEquality = (fun ty1 ty2 -> typeEquivAux EraseAll g ty1 ty2)
ImportProvidedType = (fun ty -> ImportProvidedType (tcImports.GetImportMap()) m ty)
TryGetILModuleDef = (fun () -> Some ilModule)
TypeForwarders = Map.empty
TypeForwarders = CcuTypeForwarderTable.Empty
XmlDocumentationInfo =
match tcConfig.xmlDocInfoLoader with
| Some xmlDocInfoLoader -> xmlDocInfoLoader.TryLoad(fileName)
Expand Down
5 changes: 3 additions & 2 deletions src/Compiler/Driver/ParseAndCheckInputs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module internal FSharp.Compiler.ParseAndCheckInputs

open System
open System.IO
open System.Collections.Generic

open Internal.Utilities.Collections
open Internal.Utilities.Library
Expand Down Expand Up @@ -727,7 +728,7 @@ let CheckSimulateException(tcConfig: TcConfig) =
| Some("tc-oe") -> raise(OverflowException())
| Some("tc-atmm") -> raise(ArrayTypeMismatchException())
| Some("tc-bif") -> raise(BadImageFormatException())
| Some("tc-knf") -> raise(System.Collections.Generic.KeyNotFoundException())
| Some("tc-knf") -> raise(KeyNotFoundException())
| Some("tc-ior") -> raise(IndexOutOfRangeException())
| Some("tc-ic") -> raise(InvalidCastException())
| Some("tc-ip") -> raise(InvalidProgramException())
Expand Down Expand Up @@ -808,7 +809,7 @@ let GetInitialTcState(m, ccuName, tcConfig: TcConfig, tcGlobals, tcImports: TcIm
ILScopeRef=ILScopeRef.Local
Contents=ccuContents
MemberSignatureEquality= typeEquivAux EraseAll tcGlobals
TypeForwarders=Map.empty
TypeForwarders= CcuTypeForwarderTable.Empty
XmlDocumentationInfo = None }

let ccu = CcuThunk.Create(ccuName, ccuData)
Expand Down
44 changes: 37 additions & 7 deletions src/Compiler/TypedTree/TypedTree.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
module internal rec FSharp.Compiler.TypedTree

open System
open System.Collections.Generic
open System.Collections.Generic
open System.Collections.Immutable
open System.Diagnostics
open System.Reflection

Expand Down Expand Up @@ -5291,8 +5292,39 @@ type CcuData =

override x.ToString() = sprintf "CcuData(%A)" x.FileName

type CcuTypeForwarderTree<'TKey, 'TValue> =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a comment that either

  1. Make this a generally-named thing in Utilities ("LookupTree") OR
  2. Key and Value should be specialized to their specific types string and Lazy<EntityRef>>.

At the moment the name is specific but the code is generic (and only ever instantiated at one type), which doesn't sit quite right

{
Value : 'TValue option
Children : ImmutableDictionary<'TKey, CcuTypeForwarderTree<'TKey, 'TValue>>
}

static member Empty = { Value = None; Children = ImmutableDictionary.Empty }

module CcuTypeForwarderTable =
let rec findInTree (remainingPath: ArraySegment<string>) (finalKey : string) (tree:CcuTypeForwarderTree<string, Lazy<EntityRef>>): Lazy<EntityRef> option =
let nodes = tree.Children
let searchTerm =
if remainingPath.Count = 0 then
finalKey
else
remainingPath.Array.[remainingPath.Offset]
match nodes.TryGetValue searchTerm with
| true, innerTree ->
if remainingPath.Count = 0 then
innerTree.Value
else
findInTree (ArraySegment<string>(remainingPath.Array, remainingPath.Offset + 1, remainingPath.Count - 1)) finalKey innerTree
| false, _ -> None

/// Represents a table of .NET CLI type forwarders for an assembly
type CcuTypeForwarderTable = Map<string[] * string, Lazy<EntityRef>>
type CcuTypeForwarderTable =
{
Root : CcuTypeForwarderTree<string, Lazy<EntityRef>>
}

static member Empty : CcuTypeForwarderTable = { Root = CcuTypeForwarderTree<_,_>.Empty }
member this.TryGetValue (path:string array) (item:string): Lazy<EntityRef> option =
CcuTypeForwarderTable.findInTree (ArraySegment path) item this.Root

type CcuReference = string // ILAssemblyRef

Expand Down Expand Up @@ -5381,7 +5413,7 @@ type CcuThunk =
member ccu.Contents = ccu.Deref.Contents

/// The table of type forwarders for this assembly
member ccu.TypeForwarders: Map<string[] * string, Lazy<EntityRef>> = ccu.Deref.TypeForwarders
member ccu.TypeForwarders: CcuTypeForwarderTable = ccu.Deref.TypeForwarders

/// The table of modules and namespaces at the "root" of the assembly
member ccu.RootModulesAndNamespaces = ccu.Contents.ModuleOrNamespaceType.ModuleAndNamespaceDefinitions
Expand Down Expand Up @@ -5418,10 +5450,8 @@ type CcuThunk =
/// Try to resolve a path into the CCU by referencing the .NET/CLI type forwarder table of the CCU
member ccu.TryForward(nlpath: string[], item: string) : EntityRef option =
ccu.EnsureDerefable nlpath
let key = nlpath, item
match ccu.TypeForwarders.TryGetValue key with
| true, entity -> Some(entity.Force())
| _ -> None
ccu.TypeForwarders.TryGetValue nlpath item
|> Option.map (fun entity -> entity.Force())

/// Used to make forward calls into the type/assembly loader when comparing member signatures during linking
member ccu.MemberSignatureEquality(ty1: TType, ty2: TType) =
Expand Down
20 changes: 20 additions & 0 deletions tests/benchmarks/FCSSourceFiles/FCSSourceFiles.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\Compiler\FSharp.Compiler.Service.fsproj" />
</ItemGroup>

</Project>
Loading