Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
de9bdcb
Optimizer: don't inline named functions in debug builds
auduchinok Apr 3, 2026
f347518
Xlf
auduchinok Apr 7, 2026
e56312b
Release notes
auduchinok Apr 7, 2026
de06245
Don't specialize local functions
auduchinok Apr 7, 2026
858b276
Remove specialized values caching
auduchinok Apr 7, 2026
2edadd1
Fix witness passing
auduchinok Apr 8, 2026
6f2124b
Inline NoDynamicInvocation and some builtin functions
auduchinok Apr 9, 2026
7644494
Add more tests
auduchinok Apr 9, 2026
4b37275
More witness fixes
auduchinok Apr 11, 2026
a9eb58c
Don't check accessibility for non-inlined functions
auduchinok Apr 10, 2026
b117bc9
Fix
auduchinok Apr 12, 2026
4b5b607
Fix cross-file inline same collision
auduchinok Apr 12, 2026
201ab9a
More SRTP
auduchinok Apr 12, 2026
b809ba8
Accessibility
auduchinok Apr 13, 2026
2b14f15
Fantomas
auduchinok Apr 13, 2026
85c5d38
Inline when extra optimization loops
auduchinok Apr 13, 2026
c05ea7a
Specialize signature-hidden values
auduchinok Apr 13, 2026
f3033aa
Byref
auduchinok Apr 14, 2026
7ab4138
Add more tests
auduchinok Apr 14, 2026
e2e0ee6
Another accessibility attempt
auduchinok Apr 15, 2026
0965acf
Fix inlined definition check
auduchinok Apr 15, 2026
d2143a5
Fix referencing debug builds from optimized
auduchinok Apr 15, 2026
fa2aa8e
Update baselines
auduchinok Apr 15, 2026
8f12151
Nested inline with different type args
auduchinok Apr 15, 2026
50c564d
Pickle ValInline.InlinedDefinition as ValInline.Always
auduchinok Apr 16, 2026
9581dcb
Better cross-file name collision fix
auduchinok Apr 18, 2026
c7260d3
Better byref fix
auduchinok Apr 18, 2026
d3f9fea
Better SRTP fix
auduchinok Apr 18, 2026
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 @@ -40,3 +40,4 @@
* Added warning FS3884 when a function or delegate value is used as an interpolated string argument. ([PR #19289](https://github.com/dotnet/fsharp/pull/19289))
* Add `#version;;` directive to F# Interactive to display version and environment information. ([Issue #13307](https://github.com/dotnet/fsharp/issues/13307), [PR #19332](https://github.com/dotnet/fsharp/pull/19332))

* Optimizer: don't inline named functions in debug builds ([PR #19548](https://github.com/dotnet/fsharp/pull/19548)
40 changes: 37 additions & 3 deletions src/Compiler/CodeGen/IlxGen.fs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ type IlxGenOptions =

/// When set to true, the IlxGen will delay generation of method bodies and generated them later in parallel (parallelized across files)
parallelIlxGenEnabled: bool

inlineNamedFunctions: bool
}

/// Compilation environment for compiling a fragment of an assembly
Expand Down Expand Up @@ -5682,8 +5684,13 @@ and GenTraitCall (cenv: cenv) cgbuf eenv (traitInfo: TraitConstraintInfo, argExp

| None ->

// If witnesses are available, we should now always find trait witnesses in scope
assert not generateWitnesses
// When inlineNamedFunctions is true, all trait calls should be resolved via witnesses in scope.
// When inlineNamedFunctions is false, inline functions are kept as calls rather than inlined.
// Their witness arguments may contain TraitCall operations for constraints that were resolved
// without a witness (e.g., when the constraint is satisfied by a known concrete type).
// In such cases, generateWitnesses can be true (because other witnesses are in scope) but
// the specific trait's witness is not found. Fall through to the constraint solver to resolve it.
assert (not generateWitnesses || not cenv.options.inlineNamedFunctions)

let exprOpt =
CommitOperationResult(ConstraintSolver.CodegenWitnessExprForTraitConstraint cenv.tcVal g cenv.amap m traitInfo argExprs)
Expand Down Expand Up @@ -7132,7 +7139,19 @@ and GetIlxClosureFreeVars cenv m (thisVars: ValRef list) boxity eenv takenNames
let cloName =
// Ensure that we have an g.CompilerGlobalState
assert (g.CompilerGlobalState |> Option.isSome)
g.CompilerGlobalState.Value.StableNameGenerator.GetUniqueCompilerGeneratedName(basenameSafeForUseAsTypename, expr.Range, uniq)
// The closure name counter is keyed by (basicName, fileIndex). When an expression is copied
// from another file (e.g. specializing an inline function body across files), its ranges
// still point at the original file, so its closures fall into a different counter bucket
// than closures minted for the current file. Since all these closures live under the same
// enclosing type, that can produce two closures with the same final name. Bucket the counter
// by the enclosing type's file while keeping expr.Range's StartLine for the displayed name.
let nameRange =
if expr.Range.FileIndex = eenv.cloc.Range.FileIndex then
expr.Range
else
Range.mkFileIndexRange eenv.cloc.Range.FileIndex expr.Range.Start expr.Range.End

g.CompilerGlobalState.Value.StableNameGenerator.GetUniqueCompilerGeneratedName(basenameSafeForUseAsTypename, nameRange, uniq)

let ilCloTypeRef = NestedTypeRefForCompLoc eenv.cloc cloName

Expand Down Expand Up @@ -7190,6 +7209,21 @@ and GetIlxClosureFreeVars cenv m (thisVars: ValRef list) boxity eenv takenNames

let cloFreeTyvars = cloFreeTyvars.FreeTypars |> Zset.elements

// When generating witnesses, witness types may reference type variables that appear
// only in SRTP constraints of the captured type variables (e.g. 'b in 'a : (member M: unit -> 'b)).
// Include those so they are available when generating witness field types.
let cloFreeTyvars =
if ComputeGenerateWitnesses g eenv then
let extra =
GetTraitWitnessInfosOfTypars g 0 cloFreeTyvars
|> List.collect (fun w ->
(freeInType CollectTyparsNoCaching (GenWitnessTy g w)).FreeTypars
|> Zset.elements)

(cloFreeTyvars @ extra) |> List.distinctBy (fun tp -> tp.Stamp)
else
cloFreeTyvars

let eenvinner = eenv |> EnvForTypars cloFreeTyvars

let ilCloTyInner =
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/CodeGen/IlxGen.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ type internal IlxGenOptions =

/// When set to true, the IlxGen will delay generation of method bodies and generate them later in parallel (parallelized across files)
parallelIlxGenEnabled: bool

/// Indicates if inline named functions are being inlined or emitted as calls
inlineNamedFunctions: bool
}

/// The results of the ILX compilation of one fragment of an assembly
Expand Down
12 changes: 12 additions & 0 deletions src/Compiler/Driver/CompilerConfig.fs
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,8 @@ type TcConfigBuilder =

mutable strictIndentation: bool option

mutable inlineNamedFunctions: bool option

mutable exename: string option

// If true - the compiler will copy FSharp.Core.dll along the produced binaries
Expand Down Expand Up @@ -853,6 +855,7 @@ type TcConfigBuilder =
dumpSignatureData = false
realsig = false
strictIndentation = None
inlineNamedFunctions = None
compilationMode = TcGlobals.CompilationMode.Unset
}

Expand Down Expand Up @@ -1253,6 +1256,15 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) =
member _.fsiMultiAssemblyEmit = data.fsiMultiAssemblyEmit
member _.FxResolver = data.FxResolver
member _.strictIndentation = data.strictIndentation

member _.inlineNamedFunctions =
data.inlineNamedFunctions
|> Option.defaultValue (
not data.debuginfo
|| data.optSettings.LocalOptimizationsEnabled
|| data.extraOptimizationIterations > 0
)

member _.primaryAssembly = data.primaryAssembly
member _.noFeedback = data.noFeedback
member _.stackReserveSize = data.stackReserveSize
Expand Down
4 changes: 4 additions & 0 deletions src/Compiler/Driver/CompilerConfig.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,8 @@ type TcConfigBuilder =

mutable strictIndentation: bool option

mutable inlineNamedFunctions: bool option

mutable exename: string option

mutable copyFSharpCore: CopyFSharpCoreFlag
Expand Down Expand Up @@ -814,6 +816,8 @@ type TcConfig =

member strictIndentation: bool option

member inlineNamedFunctions: bool

member GetTargetFrameworkDirectories: unit -> string list

/// Get the loaded sources that exist and issue a warning for the ones that don't
Expand Down
8 changes: 8 additions & 0 deletions src/Compiler/Driver/CompilerOptions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,14 @@ let languageFlags tcConfigB =
None,
Some(FSComp.SR.optsStrictIndentation (formatOptionSwitch (Option.defaultValue false tcConfigB.strictIndentation)))
)

CompilerOption(
"inline-named-functions",
tagNone,
OptionSwitch(fun switch -> tcConfigB.inlineNamedFunctions <- Some(switch = OptionSwitch.On)),
None,
Some(FSComp.SR.optsInlineNamedFunctions ())
)
]

// OptionBlock: Advanced user options
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/Driver/OptimizeInputs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ let ApplyAllOptimizations
// Only do abstractBigTargets in the first phase, and only when TLR is on.
abstractBigTargets = tcConfig.doTLR
reportingPhase = true
inlineNamedFunctions = tcConfig.inlineNamedFunctions
}

// Only do these two steps in the first phase.
Expand Down Expand Up @@ -578,6 +579,7 @@ let GenerateIlxCode
isInteractiveItExpr = isInteractiveItExpr
alwaysCallVirt = tcConfig.alwaysCallVirt
parallelIlxGenEnabled = tcConfig.parallelIlxGen
inlineNamedFunctions = tcConfig.inlineNamedFunctions
}

ilxGenerator.GenerateCode(ilxGenOpts, optimizedImpls, topAttrs.assemblyAttrs, topAttrs.netModuleAttrs)
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,7 @@ optsSetLangVersion,"Specify language version such as 'latest' or 'preview'."
optsDisableLanguageFeature,"Disable a specific language feature by name."
optsSupportedLangVersions,"Supported language versions:"
optsStrictIndentation,"Override indentation rules implied by the language version (%s by default)"
optsInlineNamedFunctions,"Inline named 'inline' functions"
nativeResourceFormatError,"Stream does not begin with a null resource and is not in '.RES' format."
nativeResourceHeaderMalformed,"Resource header beginning at offset %s is malformed."
formatDashItem," - %s"
Expand Down
Loading
Loading