diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 6437d4d151c..a6096cebb0d 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -398,7 +398,7 @@ let rec CheckTypeDeep (cenv: cenv) (visitTy, visitTyconRefOpt, visitAppTyOpt, vi if tcref.CanDeref && tcref.IsILTycon && tinst.Length = tcref.ILTyconRawMetadata.GenericParams.Length then (tinst,tcref.ILTyconRawMetadata.GenericParams) - ||> List.iter2 (fun ty ilGenericParam -> + ||> ListInline.iter2 (fun ty ilGenericParam -> let typeInstParent = IlGenericInst(tcref, ilGenericParam) CheckTypeDeep cenv f g env typeInstParent ty) else @@ -645,7 +645,7 @@ let CheckInterfaceTypeArgForUnimplementedStaticAbstractMembers (cenv: cenv) m (t if cenv.reportErrors then // Only check if the type parameter has interface constraints let hasInterfaceConstraint = - typar.Constraints |> List.exists (function + typar.Constraints |> ListInline.exists (function | TyparConstraint.CoercesTo(constraintTy, _) -> isInterfaceTy cenv.g constraintTy | _ -> false) @@ -710,7 +710,7 @@ let CheckTypeAux permitByRefLike (cenv: cenv) env m ty onInnerByrefError = if tcref.CanDeref then let typars = tcref.Typars if typars.Length = tinst.Length then - (typars, tinst) ||> List.iter2 (CheckInterfaceTypeArgForUnimplementedStaticAbstractMembers cenv m) + (typars, tinst) ||> ListInline.iter2 (fun typar typeArg -> CheckInterfaceTypeArgForUnimplementedStaticAbstractMembers cenv m typar typeArg) let visitTraitSolution info = match info with diff --git a/src/Compiler/Utilities/illib.fs b/src/Compiler/Utilities/illib.fs index a5d44c12bdd..870c76a790b 100644 --- a/src/Compiler/Utilities/illib.fs +++ b/src/Compiler/Utilities/illib.fs @@ -436,6 +436,29 @@ module Option = module internal ValueTuple = let inline map1Of2 ([] f) struct (a1, a2) = struct (f a1, a2) +/// List combinators that inline the closure argument into a cursor loop, so a caller on a +/// hot path allocates no closure even when the lambda captures per-call state. +module ListInline = + + let inline iter2 ([] action: 'T1 -> 'T2 -> unit) (list1: 'T1 list) (list2: 'T2 list) = + let mutable l1 = list1 + let mutable l2 = list2 + + while not l1.IsEmpty && not l2.IsEmpty do + action l1.Head l2.Head + l1 <- l1.Tail + l2 <- l2.Tail + + let inline exists ([] predicate: 'T -> bool) (list: 'T list) = + let mutable remaining = list + let mutable found = false + + while not found && not remaining.IsEmpty do + found <- predicate remaining.Head + remaining <- remaining.Tail + + found + module List = let sortWithOrder (c: IComparer<'T>) elements = diff --git a/src/Compiler/Utilities/illib.fsi b/src/Compiler/Utilities/illib.fsi index a4bba551042..92b66a96646 100644 --- a/src/Compiler/Utilities/illib.fsi +++ b/src/Compiler/Utilities/illib.fsi @@ -152,6 +152,12 @@ module internal Option = val attempt: f: (unit -> 'T) -> 'T option +module internal ListInline = + + val inline iter2: action: ('T1 -> 'T2 -> unit) -> list1: 'T1 list -> list2: 'T2 list -> unit + + val inline exists: predicate: ('T -> bool) -> list: 'T list -> bool + module internal List = val sortWithOrder: c: IComparer<'T> -> elements: 'T list -> 'T list