未验证 提交 5959af0c 编写于 作者: M msftbot[bot] 提交者: GitHub

Merge pull request #49806 from dotnet/merges/release/dev16.9-to-master

Merge release/dev16.9 to master
......@@ -30,7 +30,7 @@ internal interface ILocalDataFlowState : ILocalState
/// <summary>
/// A mapping from local variables to the index of their slot in a flow analysis local state.
/// </summary>
protected readonly PooledDictionary<VariableIdentifier, int> _variableSlot = PooledDictionary<VariableIdentifier, int>.GetInstance();
protected PooledDictionary<VariableIdentifier, int> _variableSlot = PooledDictionary<VariableIdentifier, int>.GetInstance();
/// <summary>
/// A mapping from the local variable slot to the symbol for the local variable itself. This
......
......@@ -116,7 +116,7 @@ public VisitArgumentResult(VisitResult visitResult, Optional<LocalState> stateFo
/// <summary>
/// The inferred type at the point of declaration of var locals and parameters.
/// </summary>
private readonly PooledDictionary<Symbol, TypeWithAnnotations> _variableTypes = SpecializedSymbolCollections.GetPooledSymbolDictionaryInstance<Symbol, TypeWithAnnotations>();
private PooledDictionary<Symbol, TypeWithAnnotations> _variableTypes = SpecializedSymbolCollections.GetPooledSymbolDictionaryInstance<Symbol, TypeWithAnnotations>();
/// <summary>
/// Binder for symbol being analyzed.
......@@ -190,6 +190,11 @@ public VisitArgumentResult(VisitResult visitResult, Optional<LocalState> stateFo
/// </summary>
private readonly bool _isSpeculative;
/// <summary>
/// Is a method that contains only blocks, expression statements, and lambdas.
/// </summary>
private readonly bool _isSimpleMethod;
/// <summary>
/// True if this walker was created using an initial state.
/// </summary>
......@@ -380,6 +385,7 @@ protected override void Free()
_returnTypesOpt = returnTypesOpt;
_snapshotBuilderOpt = snapshotBuilderOpt;
_isSpeculative = isSpeculative;
_isSimpleMethod = IsSimpleMethodVisitor.IsSimpleMethod(node);
if (initialState != null)
{
......@@ -404,6 +410,68 @@ protected override void Free()
}
}
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)
......@@ -2640,6 +2708,37 @@ private void VisitStatementsWithLocalFunctions(BoundBlock block)
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<VariableIdentifier, int>.GetInstance();
foreach (var pair in oldVariableSlot)
{
_variableSlot.Add(pair.Key, pair.Value);
}
_variableTypes = SpecializedSymbolCollections.GetPooledSymbolDictionaryInstance<Symbol, TypeWithAnnotations>();
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();
......@@ -2681,6 +2780,16 @@ private void VisitStatementsWithLocalFunctions(BoundBlock block)
_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;
......
......@@ -4,6 +4,7 @@
#nullable disable
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Roslyn.Test.Utilities;
using System.Linq;
......@@ -375,5 +376,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<object, object> 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<MethodDeclarationSyntax>().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);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册