diff --git a/docs/contributing/Building, Debugging, and Testing on Windows.md b/docs/contributing/Building, Debugging, and Testing on Windows.md
index 55241272fa7a..aafd28bb378d 100644
--- a/docs/contributing/Building, Debugging, and Testing on Windows.md
+++ b/docs/contributing/Building, Debugging, and Testing on Windows.md
@@ -15,12 +15,12 @@ The minimal required version of .NET Framework is 4.7.2.
## Developing with Visual Studio 2019
-1. [Visual Studio 2019 16.8p2](https://visualstudio.microsoft.com/downloads/)
+1. [Visual Studio 2019 16.8](https://visualstudio.microsoft.com/downloads/)
- Ensure C#, VB, MSBuild, .NET Core and Visual Studio Extensibility are included in the selected work loads
- - Ensure Visual Studio is on Version "16.8 Preview 3" or greater
+ - Ensure Visual Studio is on Version "16.8" or greater
- Ensure "Use previews of the .NET Core SDK" is checked in Tools -> Options -> Environment -> Preview Features
- Restart Visual Studio
-1. [.NET Core SDK 5.0 Release Candidate 2](https://dotnet.microsoft.com/download/dotnet-core/5.0) [Windows x64 installer](https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-5.0.100-rc.2-windows-x64-installer)
+1. [.NET Core SDK 5.0](https://dotnet.microsoft.com/download/dotnet-core/5.0) [Windows x64 installer](https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-5.0.100-windows-x64-installer)
1. [PowerShell 5.0 or newer](https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell). If you are on Windows 10, you are fine; you'll only need to upgrade if you're on earlier versions of Windows. The download link is under the ["Upgrading existing Windows PowerShell"](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-windows-powershell?view=powershell-6#upgrading-existing-windows-powershell) heading.
1. Run Restore.cmd
1. Open Roslyn.sln
diff --git a/eng/build.ps1 b/eng/build.ps1
index ca9236fc7dfb..ab8ba9658cca 100644
--- a/eng/build.ps1
+++ b/eng/build.ps1
@@ -605,6 +605,7 @@ function Prepare-TempDir() {
$env:TMP=$TempDir
Copy-Item (Join-Path $RepoRoot "src\Workspaces\MSBuildTest\Resources\.editorconfig") $TempDir
+ Copy-Item (Join-Path $RepoRoot "src\Workspaces\MSBuildTest\Resources\global.json") $TempDir
Copy-Item (Join-Path $RepoRoot "src\Workspaces\MSBuildTest\Resources\Directory.Build.props") $TempDir
Copy-Item (Join-Path $RepoRoot "src\Workspaces\MSBuildTest\Resources\Directory.Build.targets") $TempDir
Copy-Item (Join-Path $RepoRoot "src\Workspaces\MSBuildTest\Resources\Directory.Build.rsp") $TempDir
diff --git a/eng/targets/Imports.targets b/eng/targets/Imports.targets
index d91c8cdc3972..dfa90182ff15 100644
--- a/eng/targets/Imports.targets
+++ b/eng/targets/Imports.targets
@@ -175,30 +175,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/global.json b/global.json
index cbc11ce66fbc..852d51d1db2a 100644
--- a/global.json
+++ b/global.json
@@ -1,11 +1,11 @@
{
"sdk": {
- "version": "5.0.100-rc.2.20479.15",
+ "version": "5.0.100",
"allowPrerelease": true,
"rollForward": "major"
},
"tools": {
- "dotnet": "5.0.100-rc.2.20479.15",
+ "dotnet": "5.0.100",
"vs": {
"version": "16.8"
},
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/LocalDataFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/LocalDataFlowPass.cs
index 0cd4ff85fb59..77c16508ff28 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/LocalDataFlowPass.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/LocalDataFlowPass.cs
@@ -32,7 +32,7 @@ internal interface ILocalDataFlowState : ILocalState
///
/// A mapping from local variables to the index of their slot in a flow analysis local state.
///
- protected readonly PooledDictionary _variableSlot = PooledDictionary.GetInstance();
+ protected PooledDictionary _variableSlot = PooledDictionary.GetInstance();
///
/// A mapping from the local variable slot to the symbol for the local variable itself. This
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
index a3e04437aa0c..879bcd34f648 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
@@ -118,7 +118,7 @@ public VisitArgumentResult(VisitResult visitResult, Optional stateFo
///
/// The inferred type at the point of declaration of var locals and parameters.
///
- private readonly PooledDictionary _variableTypes = SpecializedSymbolCollections.GetPooledSymbolDictionaryInstance();
+ private PooledDictionary _variableTypes = SpecializedSymbolCollections.GetPooledSymbolDictionaryInstance();
///
/// Binder for symbol being analyzed.
@@ -192,6 +192,11 @@ public VisitArgumentResult(VisitResult visitResult, Optional stateFo
///
private readonly bool _isSpeculative;
+ ///
+ /// Is a method that contains only blocks, expression statements, and lambdas.
+ ///
+ private readonly bool _isSimpleMethod;
+
///
/// True if this walker was created using an initial state.
///
@@ -394,6 +399,7 @@ private NullableWalker(
_returnTypesOpt = returnTypesOpt;
_snapshotBuilderOpt = snapshotBuilderOpt;
_isSpeculative = isSpeculative;
+ _isSimpleMethod = IsSimpleMethodVisitor.IsSimpleMethod(node);
if (initialState != null)
{
@@ -418,6 +424,68 @@ private NullableWalker(
}
}
+ internal sealed class IsSimpleMethodVisitor : BoundTreeWalkerWithStackGuard
+ {
+ private bool _hasComplexity;
+
+ internal static bool IsSimpleMethod(BoundNode? node)
+ {
+ if (node is BoundConstructorMethodBody constructorBody && constructorBody.Initializer is { })
+ {
+ return false;
+ }
+ if (node is BoundMethodBodyBase methodBody)
+ {
+ var blockBody = methodBody.BlockBody;
+ var expressionBody = methodBody.ExpressionBody;
+ node = blockBody;
+ if (node is { })
+ {
+ if (expressionBody is { }) return false;
+ }
+ else
+ {
+ node = expressionBody;
+ }
+ }
+ var visitor = new IsSimpleMethodVisitor();
+ try
+ {
+ visitor.Visit(node);
+ return !visitor._hasComplexity;
+ }
+ catch (CancelledByStackGuardException)
+ {
+ return false;
+ }
+ }
+
+ public override BoundNode? Visit(BoundNode? node)
+ {
+ if (node is null)
+ {
+ return null;
+ }
+ if (_hasComplexity)
+ {
+ return node;
+ }
+ if (node is BoundExpression)
+ {
+ return base.Visit(node);
+ }
+ switch (node.Kind)
+ {
+ case BoundKind.Block:
+ case BoundKind.ExpressionStatement:
+ case BoundKind.ReturnStatement:
+ return base.Visit(node);
+ }
+ _hasComplexity = true;
+ return node;
+ }
+ }
+
public string GetDebuggerDisplay()
{
if (this.IsConditionalState)
@@ -2535,6 +2603,37 @@ private void AnalyzeLocalFunctionOrLambda(
var oldState = this.State;
this.State = state;
+ var oldVariableSlot = _variableSlot;
+ var oldVariableTypes = _variableTypes;
+ var oldVariableBySlot = variableBySlot;
+ var oldNextVariableSlot = nextVariableSlot;
+
+ // As an optimization, if the entire method is simple enough,
+ // we'll reset the set of variable slots and types after analyzing the nested function,
+ // to avoid accumulating entries in the outer function for variables that are
+ // local to the nested function. (Of course, this will drop slots associated
+ // with variables in the outer function that were first used in the nested function,
+ // such as a field access on a captured local, but the state associated with
+ // any such entries are dropped, so the slots can be dropped as well.)
+ // We don't optimize more complicated methods (methods that contain labels,
+ // branches, try blocks, local functions) because we track additional state for
+ // those nodes that might be invalidated if we drop the associated slots or types.
+ if (_isSimpleMethod)
+ {
+ _variableSlot = PooledDictionary.GetInstance();
+ foreach (var pair in oldVariableSlot)
+ {
+ _variableSlot.Add(pair.Key, pair.Value);
+ }
+ _variableTypes = SpecializedSymbolCollections.GetPooledSymbolDictionaryInstance();
+ foreach (var pair in oldVariableTypes)
+ {
+ _variableTypes.Add(pair.Key, pair.Value);
+ }
+ variableBySlot = new VariableIdentifier[oldVariableBySlot.Length];
+ Array.Copy(oldVariableBySlot, variableBySlot, oldVariableBySlot.Length);
+ }
+
var previousSlot = _snapshotBuilderOpt?.EnterNewWalker(lambdaOrFunctionSymbol) ?? -1;
var oldPending = SavePending();
@@ -2576,6 +2675,16 @@ private void AnalyzeLocalFunctionOrLambda(
_snapshotBuilderOpt?.ExitWalker(this.SaveSharedState(), previousSlot);
+ if (_isSimpleMethod)
+ {
+ nextVariableSlot = oldNextVariableSlot;
+ variableBySlot = oldVariableBySlot;
+ _variableTypes.Free();
+ _variableTypes = oldVariableTypes;
+ _variableSlot.Free();
+ _variableSlot = oldVariableSlot;
+ }
+
this.State = oldState;
_returnTypesOpt = oldReturnTypes;
_useDelegateInvokeParameterTypes = oldUseDelegateInvokeParameterTypes;
diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs
index 18080abfa42e..9ad81dfeeddb 100644
--- a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs
+++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Roslyn.Test.Utilities;
using System.Linq;
@@ -373,5 +374,75 @@ public void ArrayInitializationAnonymousTypes()
var comp = CreateCompilation(source);
comp.VerifyEmitDiagnostics();
}
+
+ [Fact]
+ [WorkItem(49745, "https://github.com/dotnet/roslyn/issues/49745")]
+ public void NullableStateLambdas()
+ {
+ const int nFunctions = 10000;
+
+ var builder = new StringBuilder();
+ builder.AppendLine("#nullable enable");
+ builder.AppendLine("class Program");
+ builder.AppendLine("{");
+ builder.AppendLine(" static void F1(System.Func