Skip to content

Commit

Permalink
Add snapshot types
Browse files Browse the repository at this point in the history
  • Loading branch information
cston committed Jan 15, 2021
1 parent 9a350d3 commit 648b8e4
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 133 deletions.
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ private BoundLambda SuppressIfNeeded(BoundLambda lambda)
public bool HasExplicitlyTypedParameterList { get { return Data.HasExplicitlyTypedParameterList; } }
public int ParameterCount { get { return Data.ParameterCount; } }
public TypeWithAnnotations InferReturnType(ConversionsBase conversions, NamedTypeSymbol delegateType, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
=> BindForReturnTypeInference(delegateType).GetInferredReturnType(conversions, _nullableState?.Clone(), ref useSiteDiagnostics);
=> BindForReturnTypeInference(delegateType).GetInferredReturnType(conversions, _nullableState, ref useSiteDiagnostics);

public RefKind RefKind(int index) { return Data.RefKind(index); }
public void GenerateAnonymousFunctionConversionError(DiagnosticBag diagnostics, TypeSymbol targetType) { Data.GenerateAnonymousFunctionConversionError(diagnostics, targetType); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,11 @@ private SnapshotManager(ImmutableArray<SharedWalkerState> walkerSharedStates, Im
#endif
}

internal (VariableState, Symbol) GetSnapshot(int position)
internal (VariablesSnapshot, LocalStateSnapshot) GetSnapshot(int position)
{
Snapshot incrementalSnapshot = GetSnapshotForPosition(position);
var sharedState = _walkerSharedStates[incrementalSnapshot.SharedStateIndex];
var variableState = new VariableState(sharedState.Variables, incrementalSnapshot.VariableState.Clone());
return (variableState, sharedState.Symbol);
return (sharedState.Variables, incrementalSnapshot.VariableState);
}

internal TypeWithAnnotations? GetUpdatedTypeForLocalSymbol(SourceLocalSymbol symbol)
Expand Down Expand Up @@ -218,7 +217,7 @@ internal void TakeIncrementalSnapshot(BoundNode? node, LocalState currentState)

// Note that we can't use Add here, as this is potentially not the stable
// state of this node and we could get updated states later.
_incrementalSnapshots[node.Syntax.SpanStart] = new Snapshot(currentState.Clone(), _currentWalkerSlot);
_incrementalSnapshots[node.Syntax.SpanStart] = new Snapshot(currentState.CreateSnapshot(), _currentWalkerSlot);
}

internal void SetUpdatedSymbol(BoundNode node, Symbol originalSymbol, Symbol updatedSymbol)
Expand Down Expand Up @@ -253,13 +252,11 @@ private static (BoundNode?, Symbol) GetKey(BoundNode node, Symbol symbol)
/// </summary>
internal struct SharedWalkerState
{
internal readonly Variables Variables;
internal readonly Symbol Symbol;
internal readonly VariablesSnapshot Variables;

internal SharedWalkerState(Variables variables, Symbol symbol)
internal SharedWalkerState(VariablesSnapshot variables)
{
Variables = variables;
Symbol = symbol;
}
}

Expand All @@ -269,10 +266,10 @@ internal SharedWalkerState(Variables variables, Symbol symbol)
/// </summary>
private readonly struct Snapshot
{
internal readonly LocalState VariableState;
internal readonly LocalStateSnapshot VariableState;
internal readonly int SharedStateIndex;

internal Snapshot(LocalState variableState, int sharedStateIndex)
internal Snapshot(LocalStateSnapshot variableState, int sharedStateIndex)
{
VariableState = variableState;
SharedStateIndex = sharedStateIndex;
Expand Down
127 changes: 98 additions & 29 deletions src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.Variables.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
Expand All @@ -11,25 +13,56 @@ namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class NullableWalker
{
/// <summary>
/// An immutable copy of Variables.
/// </summary>
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
internal sealed class Variables
internal sealed class VariablesSnapshot
{
private sealed class Factory
internal readonly int Id;
internal readonly VariablesSnapshot? Container;
internal readonly Symbol? Symbol;
internal readonly ImmutableArray<KeyValuePair<VariableIdentifier, int>> VariableSlot;
internal readonly ImmutableDictionary<Symbol, TypeWithAnnotations> VariableTypes;

internal VariablesSnapshot(int id, VariablesSnapshot? container, Symbol? symbol, ImmutableArray<KeyValuePair<VariableIdentifier, int>> variableSlot, ImmutableDictionary<Symbol, TypeWithAnnotations> variableTypes)
{
internal int NextId;
internal int MaxSlotDepth;
Id = id;
Container = container;
Symbol = symbol;
VariableSlot = variableSlot;
VariableTypes = variableTypes;
}

internal Factory(int maxSlotDepth)
{
MaxSlotDepth = maxSlotDepth;
}
internal bool TryGetType(Symbol symbol, out TypeWithAnnotations type)
{
return VariableTypes.TryGetValue(symbol, out type);
}

private string GetDebuggerDisplay()
{
var symbol = (object?)Symbol ?? "<null>";
return $"Id={Id}, Symbol={symbol}, Count={VariableSlot.Length}";
}
}

[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
internal sealed class Variables
{
private sealed class NextId
{
internal int Value;
}

// Members of variables are tracked up to a fixed depth, to avoid cycles. The
// MaxSlotDepth value is arbitrary but large enough to allow most scenarios.
private const int MaxSlotDepth = 5;

private const int IdOffset = 16;
private const int IdOrIndexMask = (1 << IdOffset) - 1;
private const int IndexMax = IdOrIndexMask;

private readonly Factory _factory;
private readonly NextId _nextId;
internal readonly int Id;
internal readonly Variables? Container;
internal readonly Symbol? Symbol;
Expand All @@ -56,23 +89,52 @@ internal Factory(int maxSlotDepth)
/// </summary>
private readonly ArrayBuilder<VariableIdentifier> _variableBySlot;

/// <summary>
/// Variable slots are allocated to local variables sequentially and never reused. This is
/// the index of the next slot number to use.
/// </summary>
private int _nextVariableIndex = 1;
internal static Variables Create(Symbol? symbol)
{
return new Variables(new NextId() { Value = 1 }, id: 0, container: null, symbol);
}

internal static Variables Create(Symbol? symbol, int maxSlotDepth)
internal static Variables Create(VariablesSnapshot snapshot)
{
return new Variables(new Factory(maxSlotDepth), container: null, symbol);
return CreateInternal(snapshot, new NextId() { Value = snapshot.Id + 1 });
}

private Variables(Factory factory, Variables? container, Symbol? symbol)
private static Variables CreateInternal(VariablesSnapshot snapshot, NextId nextId)
{
Debug.Assert(container is null || container.Id < factory.NextId);
_factory = factory;
var container = snapshot.Container is null ? null : CreateInternal(snapshot.Container, nextId);
var variables = new Variables(nextId, snapshot.Id, container, snapshot.Symbol);
variables.Populate(snapshot);
return variables;
}

private void Populate(VariablesSnapshot snapshot)
{
Debug.Assert(_variableSlot.Count == 0);
Debug.Assert(_variableTypes.Count == 0);
Debug.Assert(_variableBySlot.Count == 1);

_variableBySlot.AddMany(default, snapshot.VariableSlot.Length);
foreach (var pair in snapshot.VariableSlot)
{
var identifier = pair.Key;
var index = pair.Value;
_variableSlot.Add(identifier, index);
_variableBySlot[index] = identifier;
}

foreach (var pair in snapshot.VariableTypes)
{
_variableTypes.Add(pair.Key, pair.Value);
}
}

private Variables(NextId nextId, int id, Variables? container, Symbol? symbol)
{
Debug.Assert(container is null || container.Id < nextId.Value);
Debug.Assert(id < nextId.Value);
_nextId = nextId;
// PROTOTYPE: Handle > 64K nested methods (distinct ids). See NullableStateTooManyNestedFunctions().
Id = _factory.NextId++;
Id = id;
Container = container;
Symbol = symbol;
_variableBySlot = ArrayBuilder<VariableIdentifier>.GetInstance();
Expand All @@ -81,19 +143,26 @@ private Variables(Factory factory, Variables? container, Symbol? symbol)

internal void Free()
{
// PROTOTYPE:
#if false
Container?.Free();
_variableBySlot.Free();
_variableTypes.Free();
_variableSlot.Free();
#endif
}

internal VariablesSnapshot CreateSnapshot()
{
return new VariablesSnapshot(
Id,
Container?.CreateSnapshot(),
Symbol,
ImmutableArray.CreateRange(_variableSlot),
ImmutableDictionary.CreateRange(_variableTypes));
}

internal Variables CreateNestedFunction(MethodSymbol method)
{
Debug.Assert(GetVariablesForSymbol(method) is null or { Symbol: null });
return new Variables(_factory, this, method);
return new Variables(_nextId, id: _nextId.Value++, this, method);
}

internal int RootSlot(int slot)
Expand Down Expand Up @@ -131,11 +200,11 @@ private bool TryGetValueInternal(VariableIdentifier identifier, out int slot)

internal int Add(VariableIdentifier identifier)
{
if (_nextVariableIndex > IndexMax)
if (Count > IndexMax)
{
return -1;
}
if (GetSlotDepth(identifier.ContainingSlot) >= _factory.MaxSlotDepth)
if (GetSlotDepth(identifier.ContainingSlot) >= MaxSlotDepth)
{
return -1;
}
Expand All @@ -145,7 +214,7 @@ internal int Add(VariableIdentifier identifier)

private int AddInternal(VariableIdentifier identifier)
{
int index = _nextVariableIndex++;
int index = Count;
_variableSlot.Add(identifier, index);
while (index >= _variableBySlot.Count)
{
Expand Down Expand Up @@ -178,7 +247,7 @@ internal VariableIdentifier this[int slot]
}
}

internal int Count => _nextVariableIndex;
internal int Count => _variableBySlot.Count;

internal void GetMembers(ArrayBuilder<(VariableIdentifier, int)> builder, int containingSlot)
{
Expand Down Expand Up @@ -286,7 +355,7 @@ internal static (int Id, int Index) DeconstructSlot(int slot)
return slot < 0 ? (0, slot) : ((slot >> IdOffset & IdOrIndexMask), slot & IdOrIndexMask);
}

internal string GetDebuggerDisplay()
private string GetDebuggerDisplay()
{
var symbol = (object?)Symbol ?? "<null>";
return $"Id={Id}, Symbol={symbol}, Count={Count}";
Expand Down
Loading

0 comments on commit 648b8e4

Please sign in to comment.