-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #44026 from 333fred/merge-master
- Loading branch information
Showing
911 changed files
with
57,266 additions
and
14,797 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
docs/compilers/CSharp/Compiler Breaking Changes - DotNet 5.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
## This document lists known breaking changes in Roslyn in C# 9.0 which will be introduced with .NET 5. | ||
|
||
1. Beginning with C# 9.0, when you switch on a value of type `byte` or `sbyte`, the compiler tracks which values have been handled and which have not. Technically, we do so for all numeric types, but in practice it is only a breaking change for the types `byte` and `sbyte`. For example, the following program contains a switch statement that explicitly handles *all* of the possible values of the switch's controlling expression | ||
```csharp | ||
int M(byte b) | ||
{ | ||
switch (b) | ||
{ | ||
case 0: case 1: case 2: ... | ||
return 0; | ||
} | ||
return 1; | ||
} | ||
``` | ||
Since it returns from the method `M` on every value of the input, the following statement (`return 1;`) is considered not to be reachable and will produce a warning for unreachable code. Previously, the compiler did not analyze the set of values for completeness, and the following statement was considered reachable. This code compiles without warnings in C# 8.0 but will produce a warning in C# 9.0. | ||
|
||
Similarly, a switch expression that explicitly handles all values of an input of type `byte` or `sbyte` is considered to be complete. For example, the following program | ||
|
||
```csharp | ||
int M(byte b) | ||
{ | ||
return b switch | ||
{ | ||
0 => 0, 1 => 0, 2 => 0, ... byte.MaxValue => 0, | ||
}; | ||
} | ||
``` | ||
|
||
Would produce a warning when compiled in C# 8.0 (the switch expression does not handle all values of its input), but produces no warning in C# 9.0. Conversely, adding a fallback branch to it would suppress the warning in C# 8.0, but cause an error in C# 9.0 (the case is handled by previous cases): | ||
|
||
```csharp | ||
int M(byte b) | ||
{ | ||
return b switch | ||
{ | ||
0 => 0, 1 => 0, 2 => 0, ... byte.MaxValue => 0, | ||
_ => 1 // error in C# 9.0 | ||
}; | ||
} | ||
``` | ||
2. `not` as a type in a pattern not permitted in C# 9.0 | ||
In C# 9.0 we introduce a `not` pattern that negates the following pattern: | ||
```csharp | ||
bool IsNull(object o) => o is not null; | ||
``` | ||
We recommend the pattern `not null` as the most clear way to check if a value is not null. | ||
|
||
Because a pattern in C# 9.0 can start with `not` as part of the pattern, we no longer consider a pattern that starts with `not` to be referencing a type named `not`. The expression `o is not x` used to declare a variable `x` of type `not`. Now, it checks that the input `o` is not the same as the constant named `x`. | ||
|
||
3. `and` and `or` are not permitted as a pattern designator in C# 9.0 | ||
In C# 9.0, we introduce `and` and `or` pattern combinators that combine other patterns: | ||
```csharp | ||
bool IsSmall(int i) => o is 0 or 1 or 2; | ||
``` | ||
We also introduce type patterns so that you do not have to have an identifier naming the variable you are declaring: | ||
```csharp | ||
bool IsSignedIntegral(object o) => | ||
o is sbyte or short or int or long; | ||
``` | ||
Because the `and` and `or` combinators can follow a type pattern, the compiler interprets them as part of the pattern combinator rather than an identifier for the declaration pattern. Consequently, it is an error to use `or` or `and` as pattern variable identifiers starting with C# 9.0. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
Expression Breakpoints | ||
====================== | ||
|
||
The Roslyn C# compiler has infrastructure in place to produce breakpoints within expressions rather than only at statement boundaries. The first expression form that supports breakpoints is the *switch expression*. Because breakpoints locations correspond to sequence points in source, this is accomplished by producing additional sequence points. Sequence points are constrained to appear only at locations in the code where the evaluation stack is empty, so any expressions that have breakpoint support are translated using a `BoundSpillSequence`. That is processed by a compiler pass `SpillSequenceSpiller` to ensure that it only occurs where the stack is empty. | ||
|
||
Lowering of an expression that requires breakpoint support is done with the assistance of three new sequence-point bound statement nodes, which are placed in the `BoundSpillSequence` | ||
|
||
> ```xml | ||
> <!-- | ||
> This is used to save the debugger's idea of what the enclosing sequence | ||
> point is at this location in the code so that it can be restored later | ||
> by a BoundRestorePreviousSequencePoint node. When this statement appears, | ||
> the previous non-hidden sequence point is saved and associated with the | ||
> given Identifier. | ||
> --> | ||
> <Node Name="BoundSavePreviousSequencePoint" Base="BoundStatement"> | ||
> <Field Name="Identifier" Type="object"/> | ||
> </Node> | ||
> | ||
> <!-- | ||
> This is used to restore the debugger's idea of what the enclosing statement | ||
> is to some previous location without introducing a place where a breakpoint | ||
> would cause the debugger to stop. The identifier must have | ||
> previously been given in a BoundSavePreviousSequencePoint statement. This is used | ||
> to implement breakpoints within expressions (e.g. a switch expression). | ||
> --> | ||
> <Node Name="BoundRestorePreviousSequencePoint" Base="BoundStatement"> | ||
> <Field Name="Identifier" Type="object"/> | ||
> </Node> | ||
> | ||
> <!-- | ||
> This is used to set the debugger's idea of what the enclosing statement | ||
> is without causing the debugger to stop here when single stepping. | ||
> --> | ||
> <Node Name="BoundStepThroughSequencePoint" Base="BoundStatement"> | ||
> <Field Name="Span" Type="TextSpan"/> | ||
> </Node> | ||
> ``` | ||
A `BoundSavePreviousSequencePoint` is used to save the "current statement" information at the start of the expression, so that it can be restored after the expression. This is needed so that code that appears after the expression does not appear, in the debugger, to be executing the instrumented expression. | ||
Both the `BoundRestorePreviousSequencePoint` and `BoundStepThroughSequencePoint` are intended to change the debugger's idea of what the current statement is without triggering a location where single-stepping would cause the debugger to stop at that location. That is accomplished by the generation of the following sequence: | ||
> ```none | ||
> ldc.i4 1 | ||
> brtrue.s L | ||
> // sequence point | ||
> nop | ||
> L: | ||
> // hidden sequence point | ||
> ``` | ||
This can be seen, for example, in test `SwitchExpressionSequencePoints` | ||
The purpose of this instruction sequence is to cause there to be an unreachable IL opcode (the `nop`) having a sequence point, followed by a hidden sequence point to prevent the debugger from stopping at the following location. However, once it is executing the following code, the debugger's idea of the program's "current source location" will appear to be that location mentioned in the sequence point. | ||
A `BoundStepThroughSequencePoint` also modifies the debugger's view of the "enclosing statement", but without creating a location where a breakpoint can be set. While evaluating the state machine of a *switch expression*, this is used to make the "current statement" appear to be the *switch expression*. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.