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

Merge pull request #39307 from agocke/nullable-localfunc

Move much of local function data flow to AbstractFlowPass
......@@ -2,7 +2,7 @@
namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class AbstractFlowPass<TLocalState>
internal partial class AbstractFlowPass<TLocalState, TLocalFunctionState>
{
/// <summary>
/// This is the "top" state of the data flow lattice. Generally, it is considered the state
......
......@@ -27,8 +27,9 @@ namespace Microsoft.CodeAnalysis.CSharp
/// href="https://en.wikipedia.org/wiki/Data-flow_analysis"/>) that moves upward through the <see cref="Join(ref
/// TLocalState, ref TLocalState)"/> operation.
/// </remarks>
internal abstract partial class AbstractFlowPass<TLocalState> : BoundTreeVisitor
where TLocalState : AbstractFlowPass<TLocalState>.ILocalState
internal abstract partial class AbstractFlowPass<TLocalState, TLocalFunctionState> : BoundTreeVisitor
where TLocalState : AbstractFlowPass<TLocalState, TLocalFunctionState>.ILocalState
where TLocalFunctionState : AbstractFlowPass<TLocalState, TLocalFunctionState>.AbstractLocalFunctionState
{
protected int _recursionDepth;
......@@ -47,6 +48,11 @@ internal abstract partial class AbstractFlowPass<TLocalState> : BoundTreeVisitor
/// </summary>
protected readonly Symbol _symbol;
/// <summary>
/// Reflects the enclosing member or lambda at the current location (in the bound tree).
/// </summary>
protected Symbol CurrentSymbol;
/// <summary>
/// The bound node of the method or initializer being analyzed.
/// </summary>
......@@ -280,6 +286,32 @@ protected bool IsInside
}
}
protected virtual void EnterParameters(ImmutableArray<ParameterSymbol> parameters)
{
foreach (var parameter in parameters)
{
EnterParameter(parameter);
}
}
protected virtual void EnterParameter(ParameterSymbol parameter)
{ }
protected virtual void LeaveParameters(
ImmutableArray<ParameterSymbol> parameters,
SyntaxNode syntax,
Location location)
{
foreach (ParameterSymbol parameter in parameters)
{
LeaveParameter(parameter, syntax, location);
}
}
protected virtual void LeaveParameter(ParameterSymbol parameter, SyntaxNode syntax, Location location)
{ }
public override BoundNode Visit(BoundNode node)
{
return VisitAlways(node);
......@@ -1062,8 +1094,6 @@ public override BoundNode VisitGlobalStatementInitializer(BoundGlobalStatementIn
public override BoundNode VisitLambda(BoundLambda node) => null;
public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node) => null;
public override BoundNode VisitLocal(BoundLocal node) => null;
public override BoundNode VisitLocalDeclaration(BoundLocalDeclaration node)
......@@ -1131,10 +1161,29 @@ public override BoundNode VisitCall(BoundCall node)
{
this.State = savedState;
}
else if (node.Method?.OriginalDefinition is LocalFunctionSymbol localFunc)
{
VisitLocalFunctionUse(localFunc, node.Syntax, isCall: true);
}
return null;
}
private void VisitLocalFunctionUse(LocalFunctionSymbol symbol, SyntaxNode syntax, bool isCall)
{
var localFuncState = GetOrCreateLocalFuncUsages(symbol);
VisitLocalFunctionUse(symbol, localFuncState, syntax);
if (isCall)
{
Meet(ref this.State, ref localFuncState.State);
}
localFuncState.Visited = true;
}
protected virtual void VisitLocalFunctionUse(LocalFunctionSymbol symbol, TLocalFunctionState localFunctionState, SyntaxNode syntax)
{ }
private void VisitReceiverBeforeCall(BoundExpression receiverOpt, MethodSymbol method)
{
if (method is null || method.MethodKind != MethodKind.Constructor)
......@@ -1337,6 +1386,10 @@ public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationE
VisitRvalue(methodGroup.ReceiverOpt);
}
}
else if (node.MethodOpt?.OriginalDefinition is LocalFunctionSymbol localFunc)
{
VisitLocalFunctionUse(localFunc, node.Syntax, isCall: false);
}
}
else
{
......@@ -1420,6 +1473,10 @@ public override BoundNode VisitConversion(BoundConversion node)
VisitRvalue(receiver);
}
}
else if (node.SymbolOpt?.OriginalDefinition is LocalFunctionSymbol localFunc)
{
VisitLocalFunctionUse(localFunc, node.Syntax, isCall: false);
}
}
else
{
......@@ -2993,7 +3050,7 @@ private void VisitMethodBodies(BoundBlock blockBody, BoundBlock expressionBody)
/// The possible places that we are processing when there is a region.
/// </summary>
/// <remarks>
/// This should be nested inside <see cref="AbstractFlowPass{TLocalState}"/> but is not due to https://github.com/dotnet/roslyn/issues/36992 .
/// This should be nested inside <see cref="AbstractFlowPass{TLocalState, TLocalFunctionState}"/> but is not due to https://github.com/dotnet/roslyn/issues/36992 .
/// </remarks>
internal enum RegionPlace { Before, Inside, After };
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp.Symbols;
#nullable enable
namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class AbstractFlowPass<TLocalState, TLocalFunctionState>
{
internal abstract class AbstractLocalFunctionState
{
/// <summary>
/// This is the part of the local function transfer function which
/// transfers knowledge additively. For example, in definite
/// assignment this would be captured state which is assigned by
/// the local function. When a local function is called, this
/// state is <see cref="Meet(ref TLocalState, ref TLocalState)"/>
/// with the current state.
/// </summary>
public TLocalState State;
public AbstractLocalFunctionState(TLocalState unreachableState)
{
State = unreachableState;
}
public bool Visited = false;
}
protected abstract TLocalFunctionState CreateLocalFunctionState();
private SmallDictionary<LocalFunctionSymbol, TLocalFunctionState>? _localFuncVarUsages = null;
protected TLocalFunctionState GetOrCreateLocalFuncUsages(LocalFunctionSymbol localFunc)
{
_localFuncVarUsages ??= new SmallDictionary<LocalFunctionSymbol, TLocalFunctionState>();
if (!_localFuncVarUsages.TryGetValue(localFunc, out TLocalFunctionState usages))
{
usages = CreateLocalFunctionState();
_localFuncVarUsages[localFunc] = usages;
}
return usages;
}
public override BoundNode? VisitLocalFunctionStatement(BoundLocalFunctionStatement localFunc)
{
var oldSymbol = this.CurrentSymbol;
var localFuncSymbol = localFunc.Symbol;
this.CurrentSymbol = localFuncSymbol;
var oldPending = SavePending(); // we do not support branches into a lambda
// SPEC: The entry point to a local function is always reachable.
// Captured variables are definitely assigned if they are definitely assigned on
// all branches into the local function.
var savedState = this.State;
this.State = this.TopState();
if (!localFunc.WasCompilerGenerated) EnterParameters(localFuncSymbol.Parameters);
// State changes to captured variables are recorded, as calls to local functions
// transition the state of captured variables if the variables have state changes
// across all branches leaving the local function
var localFunctionState = GetOrCreateLocalFuncUsages(localFuncSymbol);
var savedLocalFunctionState = LocalFunctionStart(localFunctionState);
var oldPending2 = SavePending();
// If this is an iterator, there's an implicit branch before the first statement
// of the function where the enumerable is returned.
if (localFuncSymbol.IsIterator)
{
PendingBranches.Add(new PendingBranch(null, this.State, null));
}
VisitAlways(localFunc.Body);
RestorePending(oldPending2); // process any forward branches within the lambda body
ImmutableArray<PendingBranch> pendingReturns = RemoveReturns();
RestorePending(oldPending);
Location? location = null;
if (!localFuncSymbol.Locations.IsDefaultOrEmpty)
{
location = localFuncSymbol.Locations[0];
}
LeaveParameters(localFuncSymbol.Parameters, localFunc.Syntax, location);
// Intersect the state of all branches out of the local function
var stateAtReturn = this.State;
foreach (PendingBranch pending in pendingReturns)
{
this.State = pending.State;
BoundNode branch = pending.Branch;
// Pass the local function identifier as a location if the branch
// is null or compiler generated.
LeaveParameters(localFuncSymbol.Parameters,
branch?.Syntax,
branch?.WasCompilerGenerated == false ? null : location);
Join(ref stateAtReturn, ref this.State);
}
// Record any changes to the state of captured variables
if (RecordStateChange(
savedLocalFunctionState,
localFunctionState,
ref stateAtReturn) &&
localFunctionState.Visited)
{
// If the sets have changed and we already used the results
// of this local function in another computation, the previous
// calculations may be invalid. We need to analyze until we
// reach a fixed-point.
stateChangedAfterUse = true;
localFunctionState.Visited = false;
}
this.State = savedState;
this.CurrentSymbol = oldSymbol;
return null;
}
private bool RecordStateChange(
TLocalFunctionState savedState,
TLocalFunctionState currentState,
ref TLocalState stateAtReturn)
{
bool anyChanged = Join(ref currentState.State, ref stateAtReturn);
// N.B. Do NOT shortcut this operation. LocalFunctionEnd may have important
// side effects to the local function state
anyChanged |= LocalFunctionEnd(savedState, currentState, ref stateAtReturn);
return anyChanged;
}
/// <summary>
/// Executed at the start of visiting a local function body. The <paramref name="state"/>
/// parameter holds the current state information for the local function being visited. To
/// save state information across the analysis, return an instance of <typeparamref name="TLocalFunctionState"/>.
/// </summary>
protected virtual TLocalFunctionState LocalFunctionStart(TLocalFunctionState state) => state;
/// <summary>
/// Executed after visiting a local function body. The <paramref name="savedState"/> is the
/// return value from <see cref="LocalFunctionStart(TLocalFunctionState)"/>. The <paramref name="currentState"/>
/// is state information for the local function that was just visited. <paramref name="stateAtReturn"/> is
/// the state after visiting the method.
/// </summary>
protected virtual bool LocalFunctionEnd(
TLocalFunctionState savedState,
TLocalFunctionState currentState,
ref TLocalState stateAtReturn)
{
return false;
}
}
}
......@@ -9,7 +9,7 @@
namespace Microsoft.CodeAnalysis.CSharp
{
internal abstract partial class AbstractFlowPass<TLocalState>
internal abstract partial class AbstractFlowPass<TLocalState, TLocalFunctionState>
{
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node)
{
......
......@@ -9,7 +9,7 @@
namespace Microsoft.CodeAnalysis.CSharp
{
internal class ControlFlowPass : AbstractFlowPass<ControlFlowPass.LocalState>
internal class ControlFlowPass : AbstractFlowPass<ControlFlowPass.LocalState, ControlFlowPass.LocalFunctionState>
{
private readonly PooledDictionary<LabelSymbol, BoundBlock> _labelsDefined = PooledDictionary<LabelSymbol, BoundBlock>.GetInstance();
private readonly PooledHashSet<LabelSymbol> _labelsUsed = PooledHashSet<LabelSymbol>.GetInstance();
......@@ -62,6 +62,15 @@ public bool Reachable
}
}
internal sealed class LocalFunctionState : AbstractLocalFunctionState
{
public LocalFunctionState(LocalState unreachableState)
: base(unreachableState)
{ }
}
protected override LocalFunctionState CreateLocalFunctionState() => new LocalFunctionState(UnreachableState());
protected override void Meet(ref LocalState self, ref LocalState other)
{
self.Alive &= other.Alive;
......@@ -377,5 +386,10 @@ public override BoundNode VisitBlock(BoundBlock node)
_currentBlock = parentBlock;
return result;
}
public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement localFunc)
{
return null;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
using System;
using System.Collections.Immutable;
using System.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class DefiniteAssignmentPass
{
private readonly SmallDictionary<LocalFunctionSymbol, LocalFuncUsages> _localFuncVarUsages =
new SmallDictionary<LocalFunctionSymbol, LocalFuncUsages>();
private class LocalFuncUsages
internal sealed class LocalFunctionState : AbstractLocalFunctionState
{
public BitVector ReadVars = BitVector.Empty;
public LocalState WrittenVars;
public ref LocalState WrittenVars => ref State;
public LocalFuncUsages(LocalState unreachableState)
{
// If we have yet to analyze the local function
// definition, assume it definitely assigns everything
WrittenVars = unreachableState;
}
public bool LocalFuncVisited { get; set; } = false;
public LocalFunctionState(LocalState unreachableState)
: base(unreachableState)
{ }
}
protected override LocalFunctionState CreateLocalFunctionState() => new LocalFunctionState(UnreachableState());
/// <summary>
/// At the local function's use site, checks that all variables read
/// are assigned and assigns all variables that are definitely assigned
/// to be definitely assigned.
/// At the local function's use site, checks that all variables read are assigned.
/// </summary>
private void ReplayReadsAndWrites(LocalFunctionSymbol localFunc,
SyntaxNode syntax,
bool writes)
protected override void VisitLocalFunctionUse(LocalFunctionSymbol localFunc, LocalFunctionState localFunctionState, SyntaxNode syntax)
{
_usedLocalFunctions.Add(localFunc);
var usages = GetOrCreateLocalFuncUsages(localFunc);
// First process the reads
ReplayReads(ref usages.ReadVars, syntax);
// Now the writes
if (writes)
{
Meet(ref this.State, ref usages.WrittenVars);
}
usages.LocalFuncVisited = true;
}
var reads = localFunctionState.ReadVars;
private void ReplayReads(ref BitVector reads, SyntaxNode syntax)
{
// Start at slot 1 (slot 0 just indicates reachability)
for (int slot = 1; slot < reads.Capacity; slot++)
{
......@@ -72,7 +45,7 @@ private void ReplayReads(ref BitVector reads, SyntaxNode syntax)
/// </summary>
/// <remarks>
/// Specifying the slot manually may be necessary if the symbol is a field,
/// in which case <see cref="LocalDataFlowPass{TLocalState}.VariableSlot(Symbol, int)"/>
/// in which case <see cref="LocalDataFlowPass{TLocalState, TLocalFunctionState}.VariableSlot(Symbol, int)"/>
/// will not know which containing slot to look for.
/// </remarks>
private void CheckIfAssignedDuringLocalFunctionReplay(Symbol symbol, SyntaxNode node, int slot)
......@@ -116,103 +89,9 @@ private int RootSlot(int slot)
}
}
public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement localFunc)
{
var oldSymbol = this.currentSymbol;
var localFuncSymbol = localFunc.Symbol;
this.currentSymbol = localFuncSymbol;
var oldPending = SavePending(); // we do not support branches into a lambda
// SPEC: The entry point to a local function is always reachable.
// Captured variables are definitely assigned if they are definitely assigned on
// all branches into the local function.
var savedState = this.State;
this.State = this.TopState();
if (!localFunc.WasCompilerGenerated) EnterParameters(localFuncSymbol.Parameters);
// Captured variables are definitely assigned if they are assigned on
// all branches into the local function, so we store all reads from
// possibly unassigned captured variables and later report definite
// assignment errors if any of the captured variables is not assigned
// on a particular branch.
//
// Assignments to captured variables are also recorded, as calls to local functions
// definitely assign captured variables if the variables are definitely assigned at
// all branches out of the local function.
var usages = GetOrCreateLocalFuncUsages(localFuncSymbol);
var oldReads = usages.ReadVars.Clone();
usages.ReadVars.Clear();
var oldPending2 = SavePending();
// If this is an iterator, there's an implicit branch before the first statement
// of the function where the enumerable is returned.
if (localFuncSymbol.IsIterator)
{
PendingBranches.Add(new PendingBranch(null, this.State, null));
}
VisitAlways(localFunc.Body);
RestorePending(oldPending2); // process any forward branches within the lambda body
ImmutableArray<PendingBranch> pendingReturns = RemoveReturns();
RestorePending(oldPending);
Location location = null;
if (!localFuncSymbol.Locations.IsDefaultOrEmpty)
{
location = localFuncSymbol.Locations[0];
}
LeaveParameters(localFuncSymbol.Parameters, localFunc.Syntax, location);
// Intersect the state of all branches out of the local function
LocalState stateAtReturn = this.State;
foreach (PendingBranch pending in pendingReturns)
{
this.State = pending.State;
BoundNode branch = pending.Branch;
// Pass the local function identifier as a location if the branch
// is null or compiler generated.
LeaveParameters(localFuncSymbol.Parameters,
branch?.Syntax,
branch?.WasCompilerGenerated == false ? null : location);
Join(ref stateAtReturn, ref this.State);
}
// Check for changes to the possibly unassigned and assigned sets
// of captured variables
if (RecordChangedVars(ref usages.WrittenVars,
ref stateAtReturn,
ref oldReads,
ref usages.ReadVars) &&
usages.LocalFuncVisited)
{
// If the sets have changed and we already used the results
// of this local function in another computation, the previous
// calculations may be invalid. We need to analyze until we
// reach a fixed-point. The previous writes are always valid,
// so they are stored in usages.WrittenVars, while the reads
// may be invalidated by new writes, so we throw the results out.
stateChangedAfterUse = true;
usages.LocalFuncVisited = false;
}
this.State = savedState;
this.currentSymbol = oldSymbol;
return null;
}
private void RecordReadInLocalFunction(int slot)
{
var localFunc = GetNearestLocalFunctionOpt(currentSymbol);
var localFunc = GetNearestLocalFunctionOpt(CurrentSymbol);
Debug.Assert(localFunc != null);
......@@ -243,30 +122,6 @@ private void RecordReadInLocalFunction(int slot)
}
}
private bool RecordChangedVars(ref LocalState oldWrites,
ref LocalState newWrites,
ref BitVector oldReads,
ref BitVector newReads)
{
bool anyChanged = Join(ref oldWrites, ref newWrites);
anyChanged |= RecordCapturedChanges(ref oldReads, ref newReads);
return anyChanged;
}
private bool RecordCapturedChanges(ref BitVector oldState,
ref BitVector newState)
{
// Build a list of variables that are both captured and assigned
var capturedMask = GetCapturedBitmask(ref newState);
var capturedAndSet = newState;
capturedAndSet.IntersectWith(capturedMask);
// Union and check to see if there are any changes
return oldState.UnionWith(capturedAndSet);
}
private BitVector GetCapturedBitmask(ref BitVector state)
{
BitVector mask = BitVector.Empty;
......@@ -294,21 +149,11 @@ private bool IsCapturedInLocalFunction(int slot)
// A variable is captured in a local function iff its
// container is higher in the tree than the nearest
// local function
var nearestLocalFunc = GetNearestLocalFunctionOpt(currentSymbol);
var nearestLocalFunc = GetNearestLocalFunctionOpt(CurrentSymbol);
return !(nearestLocalFunc is null) && Symbol.IsCaptured(rootSymbol, nearestLocalFunc);
}
private LocalFuncUsages GetOrCreateLocalFuncUsages(LocalFunctionSymbol localFunc)
{
LocalFuncUsages usages;
if (!_localFuncVarUsages.TryGetValue(localFunc, out usages))
{
usages = _localFuncVarUsages[localFunc] = new LocalFuncUsages(UnreachableState());
}
return usages;
}
private static LocalFunctionSymbol GetNearestLocalFunctionOpt(Symbol symbol)
{
while (symbol != null)
......@@ -322,5 +167,37 @@ private static LocalFunctionSymbol GetNearestLocalFunctionOpt(Symbol symbol)
}
return null;
}
protected override LocalFunctionState LocalFunctionStart(LocalFunctionState startState)
{
// Captured variables are definitely assigned if they are assigned on
// all branches into the local function, so we store all reads from
// possibly unassigned captured variables and later report definite
// assignment errors if any of the captured variables is not assigned
// on a particular branch.
var savedState = new LocalFunctionState(UnreachableState());
savedState.ReadVars = startState.ReadVars.Clone();
startState.ReadVars.Clear();
return savedState;
}
/// <summary>
/// State changes are handled by the base class. We override to find captured variables that
/// have been read before they were assigned and determine if the set has changed.
/// </summary>
protected override bool LocalFunctionEnd(
LocalFunctionState savedState,
LocalFunctionState currentState,
ref LocalState stateAtReturn)
{
// Build a list of variables that are both captured and read before assignment
var capturedMask = GetCapturedBitmask(ref currentState.ReadVars);
var capturedAndRead = currentState.ReadVars;
capturedAndRead.IntersectWith(capturedMask);
// Union and check to see if there are any changes
return savedState.ReadVars.UnionWith(capturedAndRead);
}
}
}
......@@ -7,7 +7,7 @@
namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class LocalDataFlowPass<TLocalState>
internal partial class LocalDataFlowPass<TLocalState, TLocalFunctionState>
{
internal readonly struct VariableIdentifier : IEquatable<VariableIdentifier>
{
......
......@@ -31,7 +31,9 @@ namespace Microsoft.CodeAnalysis.CSharp
/// <summary>
/// Implement C# definite assignment.
/// </summary>
internal partial class DefiniteAssignmentPass : LocalDataFlowPass<DefiniteAssignmentPass.LocalState>
internal partial class DefiniteAssignmentPass : LocalDataFlowPass<
DefiniteAssignmentPass.LocalState,
DefiniteAssignmentPass.LocalFunctionState>
{
/// <summary>
/// Some variables that should be considered initially assigned. Used for region analysis.
......@@ -88,11 +90,6 @@ internal partial class DefiniteAssignmentPass : LocalDataFlowPass<DefiniteAssign
/// </summary>
private BitVector _alreadyReported;
/// <summary>
/// Reflects the enclosing member or lambda at the current location (in the bound tree).
/// </summary>
protected Symbol currentSymbol { get; private set; }
/// <summary>
/// true if we should check to ensure that out parameters are assigned on every exit point.
/// </summary>
......@@ -135,7 +132,7 @@ internal partial class DefiniteAssignmentPass : LocalDataFlowPass<DefiniteAssign
{
this.initiallyAssignedVariables = null;
_sourceAssembly = ((object)member == null) ? null : (SourceAssemblySymbol)member.ContainingAssembly;
this.currentSymbol = member;
this.CurrentSymbol = member;
_unassignedVariableAddressOfSyntaxes = unassignedVariableAddressOfSyntaxes;
_requireOutParamsAssigned = requireOutParamsAssigned;
_trackClassFields = trackClassFields;
......@@ -157,7 +154,7 @@ internal partial class DefiniteAssignmentPass : LocalDataFlowPass<DefiniteAssign
{
this.initiallyAssignedVariables = initiallyAssignedVariables;
_sourceAssembly = ((object)member == null) ? null : (SourceAssemblySymbol)member.ContainingAssembly;
this.currentSymbol = member;
this.CurrentSymbol = member;
_unassignedVariableAddressOfSyntaxes = null;
_requireOutParamsAssigned = true;
this.topLevelMethod = member as MethodSymbol;
......@@ -180,7 +177,7 @@ internal partial class DefiniteAssignmentPass : LocalDataFlowPass<DefiniteAssign
{
this.initiallyAssignedVariables = initiallyAssignedVariables;
_sourceAssembly = null;
this.currentSymbol = member;
this.CurrentSymbol = member;
_unassignedVariableAddressOfSyntaxes = unassignedVariableAddressOfSyntaxes;
_shouldCheckConverted = this.GetType() == typeof(DefiniteAssignmentPass);
}
......@@ -264,16 +261,16 @@ protected override ImmutableArray<PendingBranch> RemoveReturns()
{
var result = base.RemoveReturns();
if (currentSymbol is MethodSymbol currentMethod && currentMethod.IsAsync && !currentMethod.IsImplicitlyDeclared)
if (CurrentSymbol is MethodSymbol currentMethod && currentMethod.IsAsync && !currentMethod.IsImplicitlyDeclared)
{
var foundAwait = result.Any(pending => HasAwait(pending));
if (!foundAwait)
{
// If we're on a LambdaSymbol, then use its 'DiagnosticLocation'. That will be
// much better than using its 'Location' (which is the entire span of the lambda).
var diagnosticLocation = currentSymbol is LambdaSymbol lambda
var diagnosticLocation = CurrentSymbol is LambdaSymbol lambda
? lambda.DiagnosticLocation
: currentSymbol.Locations[0];
: CurrentSymbol.Locations[0];
Diagnostics.Add(ErrorCode.WRN_AsyncLacksAwaits, diagnosticLocation);
}
......@@ -314,7 +311,7 @@ private static bool HasAwait(PendingBranch pending)
protected virtual void ReportUnassignedOutParameter(ParameterSymbol parameter, SyntaxNode node, Location location)
{
if (!_requireOutParamsAssigned && ReferenceEquals(topLevelMethod, currentSymbol))
if (!_requireOutParamsAssigned && ReferenceEquals(topLevelMethod, CurrentSymbol))
{
return;
}
......@@ -447,7 +444,7 @@ protected void Analyze(ref bool badRegion, DiagnosticBag diagnostics)
/// <param name="rangeVariableUnderlyingParameter">If variable.Kind is RangeVariable, its underlying lambda parameter. Else null.</param>
private void CheckCaptured(Symbol variable, ParameterSymbol rangeVariableUnderlyingParameter = null)
{
if (currentSymbol is SourceMethodSymbol sourceMethod &&
if (CurrentSymbol is SourceMethodSymbol sourceMethod &&
Symbol.IsCaptured(rangeVariableUnderlyingParameter ?? variable, sourceMethod))
{
NoteCaptured(variable);
......@@ -788,7 +785,7 @@ protected override bool TryGetReceiverAndMember(BoundExpression expr, out BoundE
{
var propAccess = (BoundPropertyAccess)expr;
if (Binder.AccessingAutoPropertyFromConstructor(propAccess, this.currentSymbol))
if (Binder.AccessingAutoPropertyFromConstructor(propAccess, this.CurrentSymbol))
{
var propSymbol = propAccess.PropertySymbol;
member = (propSymbol as SourcePropertySymbol)?.BackingField;
......@@ -1013,7 +1010,7 @@ private bool IsAssigned(BoundExpression node, out int unassignedSlot)
case BoundKind.PropertyAccess:
{
var propertyAccess = (BoundPropertyAccess)node;
if (Binder.AccessingAutoPropertyFromConstructor(propertyAccess, this.currentSymbol))
if (Binder.AccessingAutoPropertyFromConstructor(propertyAccess, this.CurrentSymbol))
{
var property = propertyAccess.PropertySymbol;
var backingField = (property as SourcePropertySymbol)?.BackingField;
......@@ -1345,19 +1342,10 @@ protected override LocalState ReachableBottomState()
return result;
}
private void EnterParameters(ImmutableArray<ParameterSymbol> parameters)
{
// label out parameters as not assigned.
foreach (var parameter in parameters)
{
EnterParameter(parameter);
}
}
protected virtual void EnterParameter(ParameterSymbol parameter)
protected override void EnterParameter(ParameterSymbol parameter)
{
int slot = GetOrCreateSlot(parameter);
if (parameter.RefKind == RefKind.Out && !(this.currentSymbol is MethodSymbol currentMethod && currentMethod.IsAsync)) // out parameters not allowed in async
if (parameter.RefKind == RefKind.Out && !(this.CurrentSymbol is MethodSymbol currentMethod && currentMethod.IsAsync)) // out parameters not allowed in async
{
if (slot > 0) SetSlotState(slot, initiallyAssignedVariables?.Contains(parameter) == true);
}
......@@ -1369,7 +1357,7 @@ protected virtual void EnterParameter(ParameterSymbol parameter)
}
}
private void LeaveParameters(ImmutableArray<ParameterSymbol> parameters, SyntaxNode syntax, Location location)
protected override void LeaveParameters(ImmutableArray<ParameterSymbol> parameters, SyntaxNode syntax, Location location)
{
Debug.Assert(!this.IsConditionalState);
if (!this.State.Reachable)
......@@ -1377,14 +1365,10 @@ private void LeaveParameters(ImmutableArray<ParameterSymbol> parameters, SyntaxN
// if the code is not reachable, then it doesn't matter if out parameters are assigned.
return;
}
foreach (ParameterSymbol parameter in parameters)
{
LeaveParameter(parameter, syntax, location);
}
base.LeaveParameters(parameters, syntax, location);
}
private void LeaveParameter(ParameterSymbol parameter, SyntaxNode syntax, Location location)
protected override void LeaveParameter(ParameterSymbol parameter, SyntaxNode syntax, Location location)
{
if (parameter.RefKind != RefKind.None)
{
......@@ -1678,7 +1662,7 @@ public override BoundNode VisitLocal(BoundLocal node)
LocalSymbol localSymbol = node.LocalSymbol;
CheckAssigned(localSymbol, node.Syntax);
if (localSymbol.IsFixed && this.currentSymbol is MethodSymbol currentMethod &&
if (localSymbol.IsFixed && this.CurrentSymbol is MethodSymbol currentMethod &&
(currentMethod.MethodKind == MethodKind.AnonymousFunction ||
currentMethod.MethodKind == MethodKind.LocalFunction) &&
_capturedVariables.Contains(localSymbol))
......@@ -1707,43 +1691,6 @@ public override BoundNode VisitLocalDeclaration(BoundLocalDeclaration node)
return result;
}
public override BoundNode VisitCall(BoundCall node)
{
// Always visit the arguments first
var result = base.VisitCall(node);
if (node.Method.MethodKind == MethodKind.LocalFunction)
{
var localFunc = (LocalFunctionSymbol)node.Method.OriginalDefinition;
ReplayReadsAndWrites(localFunc, node.Syntax, writes: true);
}
return result;
}
public override BoundNode VisitConversion(BoundConversion node)
{
if (node.ConversionKind == ConversionKind.MethodGroup
&& node.SymbolOpt?.MethodKind == MethodKind.LocalFunction)
{
var localFunc = (LocalFunctionSymbol)node.SymbolOpt.OriginalDefinition;
var syntax = node.Syntax;
ReplayReadsAndWrites(localFunc, syntax, writes: false);
}
return base.VisitConversion(node);
}
public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationExpression node)
{
if (node.MethodOpt?.MethodKind == MethodKind.LocalFunction)
{
var syntax = node.Syntax;
var localFunc = (LocalFunctionSymbol)node.MethodOpt.OriginalDefinition;
ReplayReadsAndWrites(localFunc, syntax, writes: false);
}
return base.VisitDelegateCreationExpression(node);
}
public override BoundNode VisitMethodGroup(BoundMethodGroup node)
{
foreach (var method in node.Methods)
......@@ -1758,8 +1705,8 @@ public override BoundNode VisitMethodGroup(BoundMethodGroup node)
public override BoundNode VisitLambda(BoundLambda node)
{
var oldSymbol = this.currentSymbol;
this.currentSymbol = node.Symbol;
var oldSymbol = this.CurrentSymbol;
this.CurrentSymbol = node.Symbol;
var oldPending = SavePending(); // we do not support branches into a lambda
......@@ -1795,7 +1742,7 @@ public override BoundNode VisitLambda(BoundLambda node)
this.State = stateAfterLambda;
this.currentSymbol = oldSymbol;
this.CurrentSymbol = oldSymbol;
return null;
}
......@@ -2044,7 +1991,7 @@ public override BoundNode VisitFieldAccess(BoundFieldAccess node)
public override BoundNode VisitPropertyAccess(BoundPropertyAccess node)
{
var result = base.VisitPropertyAccess(node);
if (Binder.AccessingAutoPropertyFromConstructor(node, this.currentSymbol))
if (Binder.AccessingAutoPropertyFromConstructor(node, this.CurrentSymbol))
{
var property = node.PropertySymbol;
var backingField = (property as SourcePropertySymbol)?.BackingField;
......
......@@ -11,8 +11,9 @@ namespace Microsoft.CodeAnalysis.CSharp
/// <summary>
/// Does a data flow analysis for state attached to local variables and fields of struct locals.
/// </summary>
internal abstract partial class LocalDataFlowPass<TLocalState> : AbstractFlowPass<TLocalState>
where TLocalState : AbstractFlowPass<TLocalState>.ILocalState
internal abstract partial class LocalDataFlowPass<TLocalState, TLocalFunctionState> : AbstractFlowPass<TLocalState, TLocalFunctionState>
where TLocalState : AbstractFlowPass<TLocalState, TLocalFunctionState>.ILocalState
where TLocalFunctionState : AbstractFlowPass<TLocalState, TLocalFunctionState>.AbstractLocalFunctionState
{
/// <summary>
/// A mapping from local variables to the index of their slot in a flow analysis local state.
......
......@@ -22,7 +22,8 @@ namespace Microsoft.CodeAnalysis.CSharp
/// Nullability flow analysis.
/// </summary>
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
internal sealed partial class NullableWalker : LocalDataFlowPass<NullableWalker.LocalState>
internal sealed partial class NullableWalker
: LocalDataFlowPass<NullableWalker.LocalState, NullableWalker.LocalFunctionState>
{
/// <summary>
/// Used to copy variable slots and types from the NullableWalker for the containing method
......@@ -7980,6 +7981,16 @@ internal string GetDebuggerDisplay()
}
}
internal sealed class LocalFunctionState : AbstractLocalFunctionState
{
public LocalFunctionState(LocalState unreachableState)
: base(unreachableState)
{
}
}
protected override LocalFunctionState CreateLocalFunctionState() => new LocalFunctionState(UnreachableState());
#nullable enable
private sealed class NullabilityInfoTypeComparer : IEqualityComparer<(NullabilityInfo info, TypeSymbol type)>
{
......
......@@ -66,7 +66,7 @@ internal class ReadWriteWalker : AbstractRegionDataFlowPass
protected override void EnterRegion()
{
for (var m = this.currentSymbol as MethodSymbol; (object)m != null; m = m.ContainingSymbol as MethodSymbol)
for (var m = this.CurrentSymbol as MethodSymbol; (object)m != null; m = m.ContainingSymbol as MethodSymbol)
{
foreach (var p in m.Parameters)
{
......
......@@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp
/// <remarks>
/// https://github.com/dotnet/roslyn/issues/30067 Should
/// UnassignedFieldsWalker inherit from <see
/// cref="LocalDataFlowPass{TLocalState}"/> directly since it has simpler
/// cref="LocalDataFlowPass{TLocalState, TLocalFunctionState}"/> directly since it has simpler
/// requirements than <see cref="DefiniteAssignmentPass"/>?
/// </remarks>
internal sealed class UnassignedFieldsWalker : DefiniteAssignmentPass
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册