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 f) { }"); + builder.AppendLine(" static void F2(object arg)"); + builder.AppendLine(" {"); + for (int i = 0; i < nFunctions; i++) + { + builder.AppendLine($" F1(arg{i} => arg{i});"); + } + builder.AppendLine(" }"); + builder.AppendLine("}"); + + var source = builder.ToString(); + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + CheckIsSimpleMethod(comp, "F2", true); + } + + [Theory] + [InlineData("class Program { static object F() => null; }", "F", true)] + [InlineData("class Program { static void F() { } }", "F", true)] + [InlineData("class Program { static void F() { { } { } { } } }", "F", true)] + [InlineData("class Program { static void F() { ;;; } }", "F", false)] + [InlineData("class Program { static void F2(System.Action a) { } static void F() { F2(() => { }); } }", "F", true)] + [InlineData("class Program { static void F() { void Local() { } } }", "F", false)] + [InlineData("class Program { static void F() { System.Action a = () => { }; } }", "F", false)] + [InlineData("class Program { static void F() { if (true) { } } }", "F", false)] + [InlineData("class Program { static void F() { while (true) { } } }", "F", false)] + [InlineData("class Program { static void F() { try { } finally { } } }", "F", false)] + [InlineData("class Program { static void F() { label: F(); } }", "F", false)] + [WorkItem(49745, "https://github.com/dotnet/roslyn/issues/49745")] + public void NullableState_IsSimpleMethod(string source, string methodName, bool expectedResult) + { + var comp = CreateCompilation(source); + var diagnostics = comp.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error); + diagnostics.Verify(); + CheckIsSimpleMethod(comp, methodName, expectedResult); + } + + private static void CheckIsSimpleMethod(CSharpCompilation comp, string methodName, bool expectedResult) + { + var tree = comp.SyntaxTrees[0]; + var model = (CSharpSemanticModel)comp.GetSemanticModel(tree); + var methodDeclaration = tree.GetCompilationUnitRoot().DescendantNodes().OfType().Single(m => m.Identifier.ToString() == methodName); + var methodBody = methodDeclaration.Body; + BoundBlock block; + if (methodBody is { }) + { + var binder = model.GetEnclosingBinder(methodBody.SpanStart); + block = binder.BindEmbeddedBlock(methodBody, new DiagnosticBag()); + } + else + { + var expressionBody = methodDeclaration.ExpressionBody; + var binder = model.GetEnclosingBinder(expressionBody.SpanStart); + block = binder.BindExpressionBodyAsBlock(expressionBody, new DiagnosticBag()); + } + var actualResult = NullableWalker.IsSimpleMethodVisitor.IsSimpleMethod(block); + Assert.Equal(expectedResult, actualResult); + } } } diff --git a/src/Interactive/HostProcess/InteractiveHost64.csproj b/src/Interactive/HostProcess/InteractiveHost64.csproj index 276274e06931..44e834da6daa 100644 --- a/src/Interactive/HostProcess/InteractiveHost64.csproj +++ b/src/Interactive/HostProcess/InteractiveHost64.csproj @@ -5,7 +5,7 @@ false Exe - net472;net5.0-windows + net472;net5.0-windows7.0 win10-x64 true diff --git a/src/Interactive/HostTest/InteractiveHost.UnitTests.csproj b/src/Interactive/HostTest/InteractiveHost.UnitTests.csproj index b60b76a252d6..1c8d675206ef 100644 --- a/src/Interactive/HostTest/InteractiveHost.UnitTests.csproj +++ b/src/Interactive/HostTest/InteractiveHost.UnitTests.csproj @@ -40,7 +40,7 @@ - TargetFramework=net5.0-windows + TargetFramework=net5.0-windows7.0 InteractiveHostFiles_Core diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstanceFactory.cs b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstanceFactory.cs index d4e006c837b8..d1163a251b07 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstanceFactory.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstanceFactory.cs @@ -364,6 +364,8 @@ private static Process StartNewVisualStudioProcess(string installationPath, int // influences. processStartInfo.Environment.Remove("DOTNET_MULTILEVEL_LOOKUP"); processStartInfo.Environment.Remove("DOTNET_INSTALL_DIR"); + processStartInfo.Environment.Remove("DotNetRoot"); + processStartInfo.Environment.Remove("DotNetTool"); // The first element of the path in CI is a .dotnet used for the Roslyn build. Make sure to remove that. if (processStartInfo.Environment.TryGetValue("BUILD_SOURCESDIRECTORY", out var sourcesDirectory)) diff --git a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj index bd80e1adfb70..b43035b2e9c2 100644 --- a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj +++ b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj @@ -269,7 +269,7 @@ InteractiveHost.Core64 - TargetFramework=net5.0-windows + TargetFramework=net5.0-windows7.0 true false diff --git a/src/Workspaces/CoreTestUtilities/Roslyn.Services.UnitTests.Utilities.csproj b/src/Workspaces/CoreTestUtilities/Roslyn.Services.UnitTests.Utilities.csproj index 62a0430360ca..a486b15579a5 100644 --- a/src/Workspaces/CoreTestUtilities/Roslyn.Services.UnitTests.Utilities.csproj +++ b/src/Workspaces/CoreTestUtilities/Roslyn.Services.UnitTests.Utilities.csproj @@ -4,7 +4,7 @@ Library Microsoft.CodeAnalysis.UnitTests - net5.0-windows;netcoreapp3.1;net472 + net5.0-windows7.0;netcoreapp3.1;net472 true false true diff --git a/src/Workspaces/MSBuildTest/Resources/global.json b/src/Workspaces/MSBuildTest/Resources/global.json new file mode 100644 index 000000000000..c3c7f97f6e2a --- /dev/null +++ b/src/Workspaces/MSBuildTest/Resources/global.json @@ -0,0 +1,4 @@ +{ + // this file is empty to ensure we get the "standard" behavior as if + // no global.json was specified in the first place +}