@@ -10,7 +10,7 @@ The F# compiler code base is slowly being updated to better coding standards. Th
The future work includes
* [ ] Consistent use of fantomas formatting acrossas much of the codebase as feasible
* [ ] Consistent use of fantomas formatting acrossas much of the codebase as feasible
* [ ] Consistent naming conventions
* [ ] Reduction in line length
* [ ] Reduction in single-character identifiers
...
...
@@ -29,7 +29,7 @@ The compiler codebase uses various abbreviations. Here are some of the most comm
| `arginfo` | Argument (parameter) metadata |
| `ccu` | Reference to an F# compilation unit = an F# DLL (possibly including the DLL being compiled) |
| `celem` | Custom attribute element |
| `cenv` | Compilation environment. Means different things in different contexts, but usually a parameter for a singlecompilation state object being passed through a set of related functions in a single phase. The compilation state is often mutable. |
| `cenv` | Compilation environment. Means different things in different contexts, but usually a parameter for a singlecompilation state object being passed through a set of related functions in a single phase. The compilation state is often mutable. |
| `cpath` | Compilation path, meaning A.B.C for the overall names containing a type or module definition |
| `css` | Constraint solver state. |
| `denv` | Display Environment. Parameters guiding the formatting of types |
...
...
@@ -43,7 +43,7 @@ The compiler codebase uses various abbreviations. Here are some of the most comm
| `lid` | Long Identifier |
| `m` | A source code range marker |
| `mimpl` | IL interface method implementation |
| `minfo` | An info object for a method (whet a .NET method, an F# method or a provided method) |
| `minfo` | An info object for a method (whether a .NET method, an F# method or a provided method) |
| `modul` | a Typed Tree structure for a namespace or F# module |
| `pat` | Pattern, a syntactic AST node representing part of a pattern in a pattern match |
| `pinfo` | An info object for a property (whether a .NET property, an F# property or a provided property) |
@@ -14,7 +14,7 @@ On all platforms, the following factors affect startup performance:
* Time to open referenced assemblies (for example, `mscorlib.dll`, `FSharp.Core.dll`) and analyze them for the types and namespaces defined. This depends particularly on whether this is correctly done in an on-demand way.
* Time to process "open" declarations are the top of each file. Processing these declarations have been observed to take time in some cases of F# compilation.
* Time to process "open" declarations at the top of each file. Processing these declarations have been observed to take time in some cases of F# compilation.
* Factors specific to the specific files being compiled.
@@ -40,7 +40,7 @@ Emitted debug information includes:
* The PDB file/information (embedded or in PDB file) which contains
* Debug "sequence" points for IL code
* Names of locals and the IL code scopes over which those names are active
* The attributes on IL methods such as `CompilerGeneratedAttribute` and `DebuggerNonUserCodeAttribute`, wee below
* The attributes on IL methods such as `CompilerGeneratedAttribute` and `DebuggerNonUserCodeAttribute`, see below
* We add some codegen to give better debug experiences, see below.
We almost always now emit the [Portable PDB](https://github.com/dotnet/runtime/blob/main/docs/design/specs/PortablePdb-Metadata.md) format.
...
...
@@ -73,7 +73,7 @@ We use the terms "sequence point" and "debug point" interchangeably. The word "s
Breakpoints have two existences which must give matching behavior:
* At design-time, before debugging is launched, `ValidateBreakpointLocation` is called to validate every breakpoint. This operators on the SyntaxTree and forms a kind of "gold-standard" about the exact places where break points are valid.
* At design-time, before debugging is launched, `ValidateBreakpointLocation` is called to validate every breakpoint. This operates on the SyntaxTree and forms a kind of "gold-standard" about the exact places where break points are valid.
* At run-time, breakpoints are "mapped" by the .NET runtime to actual sequence points found in the PDB data for .NET methods. The runtime searches all methods with debug points for the relevant document and determines where to "bind" the actual breakpoint to. A typical debugger can bind a breakpoint to multiple locations.
...
...
@@ -201,14 +201,14 @@ Other computation expressions such as `async { .. }` or `builder { ... }` get de
* For every `builder.TryFinally` call, a debug point covering the `try` keyword is added immediately within the body lambda expression. A debug point covering the `finally` keyword is added immediately within the finally lambda expression. No debug point is added for the `builder.TryFinally` call itself even if used in statement position.
* For every `builder.Yield`, `builder.Return`, `builder.YieldFrom` or `builder.ReturnFrom` call, debug points are placed on the expression as if it were control flow. For example `yield 1` will place a debug point on `1`and `yield! printfn "hello"; [2]` will place two debug points.
* For every `builder.Yield`, `builder.Return`, `builder.YieldFrom` or `builder.ReturnFrom` call, debug points are placed on the expression as if it were control flow. For example `yield 1` will place a debug point on `1`and `yield! printfn "hello"; [2]` will place two debug points.
* No debug point is added for the `builder.Run`, `builder.Run` or `builder.Delay` calls at the entrance to the computation expression, nor the `builder.Delay` calls implied by `try/with` or `try/finally` or sequential `Combine` calls.
The computations are often "cold-start" anyway, leading to a two-phase debug problem.
The "step-into" and "step-over" behaviour for computation expressions is often buggy because it is performed with respect to the de-sugaring and inlining rather than the original source.
For example, a "step over" on a "while" with a non-inlined `builder.While` will step over the whole call, when the used expects it to step the loop.
For example, a "step over" on a "while" with a non-inlined `builder.While` will step over the whole call, when the user expects it to step the loop.
One approach is to inline the `builder.While` method, and apply `[<InlineIfLambda>]` to the body function. This however has only limited success
as at some points inlining fails to fully flatten. Builders implemented with resumable code tend to be much better in this regards as
more complete inlining and code-flattening is applied.
...
...
@@ -298,7 +298,7 @@ Debug points for `task { .. }` poses much harder problems. We use "while" loops
As mentioned above, other computation expressions such as `async { .. }` have significant problems with their debug points.
The main problem is stepping: even after inlining the code for computation expressions is rarely "flattened" enough, so, for example, a "step-into" is required to get into the second part of an `expr1; expr2` construct (i.e. an `async.Combine(..., async.Delay(fun () -> ...)))`) where the user expects to press "step-over".
The main problem is stepping: even after inlining the code for computation expressions is rarely "flattened" enough, so, for example, a "step-into" is required to get into the second part of an `expr1; expr2` construct (i.e. an `async.Combine(..., async.Delay(fun () -> ...))`) where the user expects to press "step-over".
Breakpoints tend to be less problematic.
...
...
@@ -336,7 +336,7 @@ These methods also get `CompilerGeneratedAttribute`, and `DebuggerNonUserCodeAtt
> TODO: we should also consider emitting `ExcludeFromCodeCoverageAttribute`, being assessed at time of writing, however the absence of debug points should be sufficient to exclude these.
> TODO: the `NewABC` methods are missing `CompilerGeneratedAttribute`, and `DebuggerNonUserCodeAttribute`. However the absence of debug points should be sufficient to exclude these from code coverage and profiling.
> TODO: the `NewABC` methods are missing `CompilerGeneratedAttribute`, and `DebuggerNonUserCodeAttribute`. However, the absence of debug points should be sufficient to exclude these from code coverage and profiling.
### Generated closures for lambdas
...
...
@@ -464,7 +464,7 @@ We do some "extra" code gen to improve debugging. It is likely much of this coul
### 'this' value
For `member x.Foo() = ...` the implementation of the member adds a local variable `x` containing the `this` pointer from `ldarg.0`. THis means hovering over `x` in the method produces the right value, as does `x.Property` etc.
For `member x.Foo() = ...` the implementation of the member adds a local variable `x` containing the `this` pointer from `ldarg.0`. This means hovering over `x` in the method produces the right value, as does `x.Property` etc.
For the compiler, a key file is `FSComp.txt` olding most of the messages. There are also a few other similar files including some old error messages in `FSStrings.resx`.
For the compiler, a key file is `FSComp.txt`holding most of the messages. There are also a few other similar files including some old error messages in `FSStrings.resx`.
@@ -19,7 +19,7 @@ The compiler accepts large inputs such as:
The compiler performs constant folding for large constants so there are no costs to using them at runtime. However, this is subject to a machine's stack size when compiling, leading to `StackOverflow` exceptions if those constants are very large. The same can be observed for certain kinds of array, list, or sequence expressions. This appears to be more prominent when compiling on macOS because macOS has a smaller stack size.
Many sources of `StackOverflow` exceptions prior to F# 4.7 when processing these kinds of constructs were resolved by processing them on the heap via continuation passing techniques. This avoids filling data on the stack and appears to have negligible effects on overall throughout or memory usage of the compiler.
Many sources of `StackOverflow` exceptions prior to F# 4.7 when processing these kinds of constructs were resolved by processing them on the heap via continuation passing techniques. This avoids filling data on the stack and appears to have negligible effects on overall throughput or memory usage of the compiler.
The inner `loop` function is emitted as a separate static method named `loop@2` and incurs no overhead involved with allocatin an `FSharpFunc` at runtime.
The inner `loop` function is emitted as a separate static method named `loop@2` and incurs no overhead involved with allocating an `FSharpFunc` at runtime.
In [`LowerCallsAndSeqs.fs`](https://github.com/dotnet/fsharp/blob/main/src/fsharp/LowerCallsAndSeqs.fs), a few optimizations are performed:
@@ -81,7 +81,7 @@ The following are the key phases and high-level logical operations of the F# com
* _Pattern match compilation_, see [PatternMatchCompilation.fsi](https://github.com/dotnet/fsharp/blob/main/src/fsharp/PatternMatchCompilation.fsi)/[PatternMatchCompilation.fs](https://github.com/dotnet/fsharp/blob/main/src/fsharp/PatternMatchCompilation.fs). Accepts a subset of checked Typed Tree nodes representing F# pattern matching and produces Typed Tree expressions implementing the pattern matching. Called during type checking as each construct involving pattern matching is processed.
* _Constraint solving_, see [ConstraintSolver.fsi](https://github.com/dotnet/fsharp/blob/main/src/fsharp/ConstraintSolver.fsi)/[ConstraintSolver.fs](https://github.com/dotnet/fsharp/blob/main/src/fsharp/ConstraintSolver.fs).A constraint solver state is maintained during type checking of a single file, and constraints are progressively asserted (i.e. added to this state). Fresh inference variables are generated and variables are eliminated (solved). Variables are also generalized at various language constructs, or explicitly declared, making them "rigid". Called during type checking as each construct is processed.
* _Constraint solving_, see [ConstraintSolver.fsi](https://github.com/dotnet/fsharp/blob/main/src/fsharp/ConstraintSolver.fsi)/[ConstraintSolver.fs](https://github.com/dotnet/fsharp/blob/main/src/fsharp/ConstraintSolver.fs).A constraint solver state is maintained during type checking of a single file, and constraints are progressively asserted (i.e. added to this state). Fresh inference variables are generated and variables are eliminated (solved). Variables are also generalized at various language constructs, or explicitly declared, making them "rigid". Called during type checking as each construct is processed.
* _Post-inference type checks_, see [PostInferenceChecks.fsi](https://github.com/dotnet/fsharp/blob/main/src/fsharp/PostInferenceChecks.fsi)/[PostInferenceChecks.fs](https://github.com/dotnet/fsharp/blob/main/src/fsharp/PostInferenceChecks.fs). Called at the end of type checking/inference for each file. A range of checks that can only be enforced after type checking on a file is complete, such as analysis when using `byref<'T>` or other `IsByRefLike` structs.
...
...
@@ -112,4 +112,4 @@ The F# compiler is bootstrapped. That is, an existing F# compiler is used to bui
## FSharp.Build
`FSharp.Build.dll` and `Microsoft.FSharp.targets` give MSBuild support for F# projects (`.fsproj`) and contain the. Although not strictly part of the F# compiler, they are essential for using F# in all contexts for .NET, aside from some more targeted scripting scenarios. The targets expose things like the `CoreCompile` and `Fsc` tasks called by MSBuild.
`FSharp.Build.dll` and `Microsoft.FSharp.targets` give MSBuild support for F# projects (`.fsproj`) and contain the targets. Although not strictly part of the F# compiler, they are essential for using F# in all contexts for .NET, aside from some more targeted scripting scenarios. The targets expose things like the `CoreCompile` and `Fsc` tasks called by MSBuild.