Skip to content

Commit

Permalink
Make ILTypeDef interface calculation lazy (#17392)
Browse files Browse the repository at this point in the history
* wip

* wip

* wip

* wip

* formatting

* release notes

---------

Co-authored-by: Petr <psfinaki@users.noreply.github.com>
Co-authored-by: Vlad Zarytovskii <vzaritovsky@hotmail.com>
Co-authored-by: Tomas Grosup <tomasgrosup@microsoft.com>
  • Loading branch information
4 people authored Sep 18, 2024
1 parent 52133d0 commit f564cc9
Show file tree
Hide file tree
Showing 16 changed files with 164 additions and 114 deletions.
2 changes: 1 addition & 1 deletion docs/release-notes/.FSharp.Compiler.Service/9.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
* Better CE error reporting when using `use!` with `and!` ([PR #17671](https://github.com/dotnet/fsharp/pull/17671))
* Better error reporting for let bindings. ([PR #17601](https://github.com/dotnet/fsharp/pull/17601))
* Optimize ILTypeDef interface impls reading from metadata. ([PR #17382](https://github.com/dotnet/fsharp/pull/17382))
* Make ILTypeDef interface impls calculation lazy. ([PR #17392](https://github.com/dotnet/fsharp/pull/17392))
* Better error reporting for active patterns. ([PR #17666](https://github.com/dotnet/fsharp/pull/17666))


### Breaking Changes
40 changes: 25 additions & 15 deletions src/Compiler/AbstractIL/il.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1909,6 +1909,23 @@ let inline conditionalAdd condition flagToAdd source =

let NoMetadataIdx = -1

type InterfaceImpl =
{ Idx: int; Type: ILType; mutable CustomAttrsStored: ILAttributesStored }

member x.CustomAttrs =
match x.CustomAttrsStored with
| ILAttributesStored.Reader f ->
let res = ILAttributes(f x.Idx)
x.CustomAttrsStored <- ILAttributesStored.Given res
res
| ILAttributesStored.Given attrs -> attrs

static member Create(ilType: ILType, customAttrsStored: ILAttributesStored) =
{ Idx = NoMetadataIdx; Type = ilType; CustomAttrsStored = customAttrsStored }

static member Create(ilType: ILType) = InterfaceImpl.Create(ilType, emptyILCustomAttrsStored)


[<NoComparison; NoEquality; StructuredFormatDisplay("{DebugText}")>]
type ILMethodDef
(
Expand Down Expand Up @@ -2635,8 +2652,7 @@ type ILTypeDef
name: string,
attributes: TypeAttributes,
layout: ILTypeDefLayout,
implements: ILTypes,
implementsCustomAttrs: (ILAttributesStored * int) list option,
implements: InterruptibleLazy<InterfaceImpl list>,
genericParams: ILGenericParameterDefs,
extends: ILType option,
methods: ILMethodDefs,
Expand All @@ -2659,7 +2675,6 @@ type ILTypeDef
attributes,
layout,
implements,
implementsCustomAttrs,
genericParams,
extends,
methods,
Expand All @@ -2676,7 +2691,6 @@ type ILTypeDef
attributes,
layout,
implements,
implementsCustomAttrs,
genericParams,
extends,
methods,
Expand All @@ -2703,8 +2717,6 @@ type ILTypeDef

member _.Implements = implements

member _.ImplementsCustomAttrs = implementsCustomAttrs

member _.Extends = extends

member _.Methods = methods
Expand Down Expand Up @@ -2744,8 +2756,7 @@ type ILTypeDef
?properties,
?newAdditionalFlags,
?customAttrs,
?securityDecls,
?implementsCustomAttrs
?securityDecls
) =
ILTypeDef(
name = defaultArg name x.Name,
Expand All @@ -2754,7 +2765,6 @@ type ILTypeDef
genericParams = defaultArg genericParams x.GenericParams,
nestedTypes = defaultArg nestedTypes x.NestedTypes,
implements = defaultArg implements x.Implements,
implementsCustomAttrs = defaultArg implementsCustomAttrs x.ImplementsCustomAttrs,
extends = defaultArg extends x.Extends,
methods = defaultArg methods x.Methods,
securityDecls = defaultArg securityDecls x.SecurityDecls,
Expand Down Expand Up @@ -3333,6 +3343,8 @@ let mkILTypeDefs l = mkILTypeDefsFromArray (Array.ofList l)
let mkILTypeDefsComputed f = ILTypeDefs f
let emptyILTypeDefs = mkILTypeDefsFromArray [||]

let emptyILInterfaceImpls = InterruptibleLazy<InterfaceImpl list>.FromValue([])

// --------------------------------------------------------------------
// Operations on method tables.
// --------------------------------------------------------------------
Expand Down Expand Up @@ -4240,7 +4252,7 @@ let mkILSimpleStorageCtor (baseTySpec, ty, extraParams, flds, access, tag, impor
let mkILStorageCtor (preblock, ty, flds, access, tag, imports) =
mkILStorageCtorWithParamNames (preblock, ty, [], addParamNames flds, access, tag, imports)

let mkILGenericClass (nm, access, genparams, extends, impl, methods, fields, nestedTypes, props, events, attrs, init) =
let mkILGenericClass (nm, access, genparams, extends, impls, methods, fields, nestedTypes, props, events, attrs, init) =
let attributes =
convertTypeAccessFlags access
||| TypeAttributes.AutoLayout
Expand All @@ -4254,8 +4266,7 @@ let mkILGenericClass (nm, access, genparams, extends, impl, methods, fields, nes
name = nm,
attributes = attributes,
genericParams = genparams,
implements = impl,
implementsCustomAttrs = None,
implements = InterruptibleLazy.FromValue(impls),
layout = ILTypeDefLayout.Auto,
extends = Some extends,
methods = methods,
Expand All @@ -4279,8 +4290,7 @@ let mkRawDataValueTypeDef (iltyp_ValueType: ILType) (nm, size, pack) =
||| TypeAttributes.ExplicitLayout
||| TypeAttributes.BeforeFieldInit
||| TypeAttributes.AnsiClass),
implements = [],
implementsCustomAttrs = None,
implements = emptyILInterfaceImpls,
extends = Some iltyp_ValueType,
layout = ILTypeDefLayout.Explicit { Size = Some size; Pack = Some pack },
methods = emptyILMethods,
Expand Down Expand Up @@ -5586,7 +5596,7 @@ and refsOfILMethodImpl s m =
and refsOfILTypeDef s (td: ILTypeDef) =
refsOfILTypeDefs s td.NestedTypes
refsOfILGenericParams s td.GenericParams
refsOfILTypes s td.Implements
refsOfILTypes s (td.Implements.Value |> List.map _.Type)
Option.iter (refsOfILType s) td.Extends
refsOfILMethodDefs s td.Methods
refsOfILFieldDefs s (td.Fields.AsList())
Expand Down
27 changes: 17 additions & 10 deletions src/Compiler/AbstractIL/il.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,15 @@ type ILCallingSignature =
ArgTypes: ILTypes
ReturnType: ILType }

type InterfaceImpl =
{ Idx: int
Type: ILType
mutable CustomAttrsStored: ILAttributesStored }

member CustomAttrs: ILAttributes
static member Create: ilType: ILType * customAttrsStored: ILAttributesStored -> InterfaceImpl
static member Create: ilType: ILType -> InterfaceImpl

/// Actual generic parameters are always types.
type ILGenericArgs = ILType list

Expand Down Expand Up @@ -1518,8 +1527,7 @@ type ILTypeDef =
name: string *
attributes: TypeAttributes *
layout: ILTypeDefLayout *
implements: ILTypes *
implementsCustomAttrs: (ILAttributesStored * int) list option *
implements: InterruptibleLazy<InterfaceImpl list> *
genericParams: ILGenericParameterDefs *
extends: ILType option *
methods: ILMethodDefs *
Expand All @@ -1539,8 +1547,7 @@ type ILTypeDef =
name: string *
attributes: TypeAttributes *
layout: ILTypeDefLayout *
implements: ILTypes *
implementsCustomAttrs: (ILAttributesStored * int) list option *
implements: InterruptibleLazy<InterfaceImpl list> *
genericParams: ILGenericParameterDefs *
extends: ILType option *
methods: ILMethodDefs *
Expand All @@ -1559,8 +1566,7 @@ type ILTypeDef =
member GenericParams: ILGenericParameterDefs
member Layout: ILTypeDefLayout
member NestedTypes: ILTypeDefs
member Implements: ILTypes
member ImplementsCustomAttrs: (ILAttributesStored * int) list option
member Implements: InterruptibleLazy<InterfaceImpl list>
member Extends: ILType option
member Methods: ILMethodDefs
member SecurityDecls: ILSecurityDecls
Expand Down Expand Up @@ -1609,7 +1615,7 @@ type ILTypeDef =
?name: string *
?attributes: TypeAttributes *
?layout: ILTypeDefLayout *
?implements: ILTypes *
?implements: InterruptibleLazy<InterfaceImpl list> *
?genericParams: ILGenericParameterDefs *
?extends: ILType option *
?methods: ILMethodDefs *
Expand All @@ -1620,8 +1626,7 @@ type ILTypeDef =
?properties: ILPropertyDefs *
?newAdditionalFlags: ILTypeDefAdditionalFlags *
?customAttrs: ILAttributesStored *
?securityDecls: ILSecurityDecls *
?implementsCustomAttrs: (ILAttributesStored * int) list option ->
?securityDecls: ILSecurityDecls ->
ILTypeDef

/// Represents a prefix of information for ILTypeDef.
Expand Down Expand Up @@ -2161,7 +2166,7 @@ val internal mkILGenericClass:
ILTypeDefAccess *
ILGenericParameterDefs *
ILType *
ILType list *
InterfaceImpl list *
ILMethodDefs *
ILFieldDefs *
ILTypeDefs *
Expand Down Expand Up @@ -2245,6 +2250,8 @@ val internal mkCtorMethSpecForDelegate: ILGlobals -> ILType * bool -> ILMethodSp
/// The toplevel "class" for a module or assembly.
val internal mkILTypeForGlobalFunctions: ILScopeRef -> ILType

val emptyILInterfaceImpls: InterruptibleLazy<InterfaceImpl list>

/// Making tables of custom attributes, etc.
val mkILCustomAttrs: ILAttribute list -> ILAttributes
val mkILCustomAttrsFromArray: ILAttribute[] -> ILAttributes
Expand Down
7 changes: 6 additions & 1 deletion src/Compiler/AbstractIL/ilmorph.fs
Original file line number Diff line number Diff line change
Expand Up @@ -368,8 +368,13 @@ let rec tdef_ty2ty_ilmbody2ilmbody_mdefs2mdefs isInKnownSet enc fs (tdef: ILType
let mdefsR = fMethodDefs (enc, tdef) tdef.Methods
let fdefsR = fdefs_ty2ty fTyInCtxtR tdef.Fields

let implements =
tdef.Implements.Value
|> List.map (fun x -> { x with Type = fTyInCtxtR x.Type })
|> InterruptibleLazy.FromValue

tdef.With(
implements = List.map fTyInCtxtR tdef.Implements,
implements = implements,
genericParams = gparams_ty2ty fTyInCtxtR tdef.GenericParams,
extends = Option.map fTyInCtxtR tdef.Extends,
methods = mdefsR,
Expand Down
5 changes: 3 additions & 2 deletions src/Compiler/AbstractIL/ilprint.fs
Original file line number Diff line number Diff line change
Expand Up @@ -752,8 +752,9 @@ let goutput_superclass env os =
output_string os "extends "
(goutput_typ_with_shortened_class_syntax env) os typ

let goutput_implements env os (imp: ILTypes) =
let goutput_implements env os (imp: InterfaceImpl list) =
if not (List.isEmpty imp) then
let imp = imp |> Seq.map _.Type
output_string os "implements "
output_seq ", " (goutput_typ_with_shortened_class_syntax env) os imp

Expand Down Expand Up @@ -836,7 +837,7 @@ let rec goutput_tdef enc env contents os (cd: ILTypeDef) =
output_string os "\n\t"
goutput_superclass env os cd.Extends
output_string os "\n\t"
goutput_implements env os cd.Implements
goutput_implements env os cd.Implements.Value
output_string os "\n{\n "

if contents then
Expand Down
37 changes: 21 additions & 16 deletions src/Compiler/AbstractIL/ilread.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2192,8 +2192,7 @@ and typeDefReader ctxtH : ILTypeDefStored =
let fdefs = seekReadFields ctxt (numTypars, hasLayout) fieldsIdx endFieldsIdx
let nested = seekReadNestedTypeDefs ctxt idx

let impls, intImplsAttrs =
seekReadInterfaceImpls ctxt mdv numTypars idx |> List.unzip
let impls = seekReadInterfaceImpls ctxt mdv numTypars idx

let mimpls = seekReadMethodImpls ctxt numTypars idx
let props = seekReadProperties ctxt numTypars idx
Expand All @@ -2206,7 +2205,6 @@ and typeDefReader ctxtH : ILTypeDefStored =
layout = layout,
nestedTypes = nested,
implements = impls,
implementsCustomAttrs = Some intImplsAttrs,
extends = super,
methods = mdefs,
securityDeclsStored = ctxt.securityDeclsReader_TypeDef,
Expand Down Expand Up @@ -2240,19 +2238,26 @@ and seekReadNestedTypeDefs (ctxt: ILMetadataReader) tidx =
|])

and seekReadInterfaceImpls (ctxt: ILMetadataReader) mdv numTypars tidx =
seekReadIndexedRows (
ctxt.getNumRows TableNames.InterfaceImpl,
id,
id,
(fun idx ->
let mutable addr = ctxt.rowAddr TableNames.InterfaceImpl idx
let _tidx = seekReadUntaggedIdx TableNames.TypeDef ctxt mdv &addr
simpleIndexCompare tidx _tidx),
isSorted ctxt TableNames.InterfaceImpl,
(fun idx ->
let intfIdx = seekReadInterfaceIdx ctxt mdv idx
seekReadTypeDefOrRef ctxt numTypars AsObject [] intfIdx, (ctxt.customAttrsReader_InterfaceImpl, idx))
)
InterruptibleLazy(fun () ->
seekReadIndexedRows (
ctxt.getNumRows TableNames.InterfaceImpl,
id,
id,
(fun idx ->
let mutable addr = ctxt.rowAddr TableNames.InterfaceImpl idx
let _tidx = seekReadUntaggedIdx TableNames.TypeDef ctxt mdv &addr
simpleIndexCompare tidx _tidx),
isSorted ctxt TableNames.InterfaceImpl,
(fun idx ->
let intfIdx = seekReadInterfaceIdx ctxt mdv idx
let ilType = seekReadTypeDefOrRef ctxt numTypars AsObject [] intfIdx

{
Idx = idx
Type = ilType
CustomAttrsStored = ctxt.customAttrsReader_InterfaceImpl
})
))

and seekReadGenericParams ctxt numTypars (a, b) : ILGenericParameterDefs =
ctxt.seekReadGenericParams (GenericParamsIdx(numTypars, a, b))
Expand Down
6 changes: 4 additions & 2 deletions src/Compiler/AbstractIL/ilreflect.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2178,7 +2178,8 @@ let rec buildTypeDefPass2 cenv nesting emEnv (tdef: ILTypeDef) =
let typB = envGetTypB emEnv tref
let emEnv = envPushTyvars emEnv (getGenericArgumentsOfType typB)
// add interface impls
tdef.Implements
tdef.Implements.Value
|> List.map _.Type
|> convTypes cenv emEnv
|> List.iter (fun implT -> typB.AddInterfaceImplementationAndLog implT)
// add methods, properties
Expand Down Expand Up @@ -2339,7 +2340,8 @@ let createTypeRef (visited: Dictionary<_, _>, created: Dictionary<_, _>) emEnv t
if verbose2 then
dprintf "buildTypeDefPass4: Creating Interface Chain of %s\n" tdef.Name

tdef.Implements |> List.iter (traverseType CollectTypes.All)
tdef.Implements.Value
|> List.iter (fun x -> traverseType CollectTypes.All x.Type)

if verbose2 then
dprintf "buildTypeDefPass4: Do value types in fields of %s\n" tdef.Name
Expand Down
11 changes: 4 additions & 7 deletions src/Compiler/AbstractIL/ilwrite.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1323,7 +1323,7 @@ and GenTypeDefPass2 pidx enc cenv (tdef: ILTypeDef) =
// Now generate or assign index numbers for tables referenced by the maps.
// Don't yet generate contents of these tables - leave that to pass3, as
// code may need to embed these entries.
cenv.implementsIdxs[tidx] <- tdef.Implements |> List.map (GenImplementsPass2 cenv env tidx)
cenv.implementsIdxs[tidx] <- tdef.Implements.Value |> List.map (fun x -> GenImplementsPass2 cenv env tidx x.Type)

tdef.Fields.AsList() |> List.iter (GenFieldDefPass2 tdef cenv tidx)
tdef.Methods |> Seq.iter (GenMethodDefPass2 tdef cenv tidx)
Expand Down Expand Up @@ -2875,12 +2875,9 @@ let rec GenTypeDefPass3 enc cenv (tdef: ILTypeDef) =
let env = envForTypeDef tdef
let tidx = GetIdxForTypeDef cenv (TdKey(enc, tdef.Name))

match tdef.ImplementsCustomAttrs with
| None -> ()
| Some attrList ->
attrList
|> List.zip cenv.implementsIdxs[tidx]
|> List.iter (fun (impIdx,(attrs,metadataIdx)) -> GenCustomAttrsPass3Or4 cenv (hca_InterfaceImpl,impIdx) (attrs.GetCustomAttrs metadataIdx))
tdef.Implements.Value
|> List.zip cenv.implementsIdxs[tidx]
|> List.iter (fun (impIdx, impl) -> GenCustomAttrsPass3Or4 cenv (hca_InterfaceImpl,impIdx) impl.CustomAttrs)

tdef.Properties.AsList() |> List.iter (GenPropertyPass3 cenv env)
tdef.Events.AsList() |> List.iter (GenEventPass3 cenv env)
Expand Down
13 changes: 6 additions & 7 deletions src/Compiler/Checking/TypeHierarchy.fs
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,16 @@ let GetImmediateInterfacesOfMetadataType g amap m skipUnref ty (tcref: TyconRef)
// succeeded with more reported. There are pathological corner cases where this
// doesn't apply: e.g. for mscorlib interfaces like IComparable, but we can always
// assume those are present.
match tdef.ImplementsCustomAttrs with
| Some attrsList when g.langFeatureNullness && g.checkNullness ->
for (attrs,attrsIdx),intfTy in tdef.Implements |> List.zip attrsList do
if skipUnref = SkipUnrefInterfaces.No || CanRescopeAndImportILType scoref amap m intfTy then
let checkNullness = g.langFeatureNullness && g.checkNullness
for {Idx = attrsIdx; Type = intfTy; CustomAttrsStored = attrs} in tdef.Implements.Value do
if skipUnref = SkipUnrefInterfaces.No || CanRescopeAndImportILType scoref amap m intfTy then
if checkNullness then
let typeAttrs = AttributesFromIL(attrsIdx,attrs)
let nullness = {DirectAttributes = typeAttrs; Fallback = FromClass typeAttrs}
RescopeAndImportILType scoref amap m tinst nullness intfTy
| _ ->
for intfTy in tdef.Implements do
if skipUnref = SkipUnrefInterfaces.No || CanRescopeAndImportILType scoref amap m intfTy then
else
RescopeAndImportILTypeSkipNullness scoref amap m tinst intfTy

| FSharpOrArrayOrByrefOrTupleOrExnTypeMetadata ->
for intfTy in tcref.ImmediateInterfaceTypesOfFSharpTycon do
instType (mkInstForAppTy g ty) intfTy ]
Expand Down
6 changes: 2 additions & 4 deletions src/Compiler/CodeGen/EraseClosures.fs
Original file line number Diff line number Diff line change
Expand Up @@ -572,8 +572,7 @@ let rec convIlxClosureDef cenv encl (td: ILTypeDef) clo =
name = td.Name,
genericParams = td.GenericParams,
attributes = td.Attributes,
implements = [],
implementsCustomAttrs = None,
implements = emptyILInterfaceImpls,
nestedTypes = emptyILTypeDefs,
layout = ILTypeDefLayout.Auto,
extends = Some cenv.mkILTyFuncTy,
Expand Down Expand Up @@ -707,8 +706,7 @@ let rec convIlxClosureDef cenv encl (td: ILTypeDef) clo =
name = td.Name,
genericParams = td.GenericParams,
attributes = td.Attributes,
implements = [],
implementsCustomAttrs = None,
implements = emptyILInterfaceImpls,
layout = ILTypeDefLayout.Auto,
nestedTypes = emptyILTypeDefs,
extends = Some nowEnvParentClass,
Expand Down
Loading

0 comments on commit f564cc9

Please sign in to comment.