Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge dev16.0-preview2 to dev16.0-preview2-vs-deps #31149

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,27 +176,37 @@ internal override TypeSymbol GetIteratorElementType(YieldStatementSyntax node, D
}

private TypeSymbol GetIteratorElementTypeFromReturnType(RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics)
{
return GetIteratorElementTypeFromReturnType(Compilation, refKind, returnType, errorLocationNode, diagnostics).TypeSymbol;
}

internal static TypeSymbolWithAnnotations GetIteratorElementTypeFromReturnType(CSharpCompilation compilation, RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics)
{
if (refKind == RefKind.None && returnType.Kind == SymbolKind.NamedType)
{
switch (returnType.OriginalDefinition.SpecialType)
{
case SpecialType.System_Collections_IEnumerable:
case SpecialType.System_Collections_IEnumerator:
return GetSpecialType(SpecialType.System_Object, diagnostics, errorLocationNode);
var objectType = compilation.GetSpecialType(SpecialType.System_Object);
if (diagnostics != null)
{
ReportUseSiteDiagnostics(objectType, diagnostics, errorLocationNode);
}
return TypeSymbolWithAnnotations.Create(objectType);

case SpecialType.System_Collections_Generic_IEnumerable_T:
case SpecialType.System_Collections_Generic_IEnumerator_T:
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0].TypeSymbol;
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0];
}

if (returnType.OriginalDefinition == Compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T))
if (returnType.OriginalDefinition == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T))
{
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0].TypeSymbol;
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0];
}
}

return null;
return default;
}

internal override void LookupSymbolsInSingleBinder(
Expand Down
106 changes: 98 additions & 8 deletions src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.CSharp
internal abstract partial class AbstractFlowPass<TLocalState> : PreciseAbstractFlowPass<TLocalState>
where TLocalState : PreciseAbstractFlowPass<TLocalState>.AbstractLocalState
{
protected readonly bool trackUnassignments; // for the data flows out walker, we track unassignments as well as assignments
private readonly bool _trackUnassignments; // for the data flows out walker, we track unassignments as well as assignments

protected AbstractFlowPass(
CSharpCompilation compilation,
Expand All @@ -26,7 +26,7 @@ protected AbstractFlowPass(
bool trackUnassignments = false)
: base(compilation, member, node)
{
this.trackUnassignments = trackUnassignments;
this._trackUnassignments = trackUnassignments;
}

protected AbstractFlowPass(
Expand All @@ -39,11 +39,25 @@ protected AbstractFlowPass(
bool trackUnassignments = false)
: base(compilation, member, node, firstInRegion, lastInRegion, trackRegions)
{
this.trackUnassignments = trackUnassignments;
this._trackUnassignments = trackUnassignments;
}

protected abstract void UnionWith(ref TLocalState self, ref TLocalState other);

/// <summary>
/// Nontrivial implementation is required for DataFlowsOutWalker or any flow analysis pass that "tracks
/// unassignments" like the nullable walker. The result should be a state, for each variable, that is
/// the strongest result possible (i.e. definitely assigned for the data flow passes, or not null for
/// the nullable analysis). Slightly more formally, this should be a reachable state that won't affect
/// another reachable state when this is intersected with the other state.
/// </summary>
protected virtual TLocalState AllBitsSet()
{
return default(TLocalState);
}

#region TryStatements

public override BoundNode VisitTryStatement(BoundTryStatement node)
{
var oldPending = SavePending(); // we do not allow branches into a try statement
Expand All @@ -52,13 +66,13 @@ public override BoundNode VisitTryStatement(BoundTryStatement node)
// use this state to resolve all the branches introduced and internal to try/catch
var pendingBeforeTry = SavePending();

VisitTryBlock(node.TryBlock, node, ref initialState);
VisitTryBlockWithUnassignments(node.TryBlock, node, ref initialState);
var finallyState = initialState.Clone();
var endState = this.State;
foreach (var catchBlock in node.CatchBlocks)
{
SetState(initialState.Clone());
VisitCatchBlock(catchBlock, ref finallyState);
VisitCatchBlockWithUnassignments(catchBlock, ref finallyState);
IntersectWith(ref endState, ref this.State);
}

Expand All @@ -84,27 +98,101 @@ public override BoundNode VisitTryStatement(BoundTryStatement node)
// we will need pending branches as they were before finally later
var tryAndCatchPending = SavePending();
var unsetInFinally = AllBitsSet();
VisitFinallyBlock(node.FinallyBlockOpt, ref unsetInFinally);
VisitFinallyBlockWithUnassignments(node.FinallyBlockOpt, ref unsetInFinally);
foreach (var pend in tryAndCatchPending.PendingBranches)
{
if (pend.Branch == null) continue; // a tracked exception
if (pend.Branch.Kind != BoundKind.YieldReturnStatement)
{
UnionWith(ref pend.State, ref this.State);
if (trackUnassignments) IntersectWith(ref pend.State, ref unsetInFinally);
if (_trackUnassignments) IntersectWith(ref pend.State, ref unsetInFinally);
}
}

RestorePending(tryAndCatchPending);
UnionWith(ref endState, ref this.State);
if (trackUnassignments) IntersectWith(ref endState, ref unsetInFinally);
if (_trackUnassignments) IntersectWith(ref endState, ref unsetInFinally);
}

SetState(endState);
RestorePending(oldPending);
return null;
}

protected Optional<TLocalState> _tryState;

private void VisitTryBlockWithUnassignments(BoundStatement tryBlock, BoundTryStatement node, ref TLocalState tryState)
{
if (_trackUnassignments)
{
Optional<TLocalState> oldTryState = _tryState;
_tryState = AllBitsSet();
VisitTryBlock(tryBlock, node, ref tryState);
var tempTryStateValue = _tryState.Value;
IntersectWith(ref tryState, ref tempTryStateValue);
if (oldTryState.HasValue)
{
var oldTryStateValue = oldTryState.Value;
IntersectWith(ref oldTryStateValue, ref tempTryStateValue);
oldTryState = oldTryStateValue;
}

_tryState = oldTryState;
}
else
{
VisitTryBlock(tryBlock, node, ref tryState);
}
}

private void VisitCatchBlockWithUnassignments(BoundCatchBlock catchBlock, ref TLocalState finallyState)
{
if (_trackUnassignments)
{
Optional<TLocalState> oldTryState = _tryState;
_tryState = AllBitsSet();
VisitCatchBlock(catchBlock, ref finallyState);
var tempTryStateValue = _tryState.Value;
IntersectWith(ref finallyState, ref tempTryStateValue);
if (oldTryState.HasValue)
{
var oldTryStateValue = oldTryState.Value;
IntersectWith(ref oldTryStateValue, ref tempTryStateValue);
oldTryState = oldTryStateValue;
}

_tryState = oldTryState;
}
else
{
VisitCatchBlock(catchBlock, ref finallyState);
}
}

private void VisitFinallyBlockWithUnassignments(BoundStatement finallyBlock, ref TLocalState unsetInFinally)
{
if (_trackUnassignments)
{
Optional<TLocalState> oldTryState = _tryState;
_tryState = AllBitsSet();
VisitFinallyBlock(finallyBlock, ref unsetInFinally);
var tempTryStateValue = _tryState.Value;
IntersectWith(ref unsetInFinally, ref tempTryStateValue);
if (oldTryState.HasValue)
{
var oldTryStateValue = oldTryState.Value;
IntersectWith(ref oldTryStateValue, ref tempTryStateValue);
oldTryState = oldTryStateValue;
}

_tryState = oldTryState;
}
else
{
VisitFinallyBlock(finallyBlock, ref unsetInFinally);
}
}

protected virtual void VisitTryBlock(BoundStatement tryBlock, BoundTryStatement node, ref TLocalState tryState)
{
VisitStatement(tryBlock);
Expand All @@ -130,5 +218,7 @@ protected virtual void VisitFinallyBlock(BoundStatement finallyBlock, ref TLocal
{
VisitStatement(finallyBlock); // this should generate no pending branches
}

#endregion TryStatements
}
}
83 changes: 1 addition & 82 deletions src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@

namespace Microsoft.CodeAnalysis.CSharp
{
#if REFERENCE_STATE
using OptionalState = Optional<DataFlowPass.LocalState>;
#else
using OptionalState = Nullable<DataFlowPass.LocalState>;
#endif

/// <summary>
/// Implement C# data flow analysis (definite assignment).
/// </summary>
Expand Down Expand Up @@ -1332,7 +1326,7 @@ protected override LocalState ReachableState()
protected override LocalState AllBitsSet()
{
var result = new LocalState(BitVector.AllSet(nextVariableSlot));
result.Assigned[0] = false;
result.Assigned[0] = false; // make the state reachable
return result;
}

Expand Down Expand Up @@ -1952,56 +1946,7 @@ public override BoundNode VisitBaseReference(BoundBaseReference node)
return null;
}

#region TryStatements
private OptionalState _tryState;

protected override void VisitTryBlock(BoundStatement tryBlock, BoundTryStatement node, ref LocalState tryState)
{
if (trackUnassignments)
{
OptionalState oldTryState = _tryState;
_tryState = AllBitsSet();
base.VisitTryBlock(tryBlock, node, ref tryState);
var tts = _tryState.Value;
IntersectWith(ref tryState, ref tts);
if (oldTryState.HasValue)
{
var ots = oldTryState.Value;
IntersectWith(ref ots, ref tts);
oldTryState = ots;
}
_tryState = oldTryState;
}
else
{
base.VisitTryBlock(tryBlock, node, ref tryState);
}
}

protected override void VisitCatchBlock(BoundCatchBlock catchBlock, ref LocalState finallyState)
{
if (trackUnassignments)
{
OptionalState oldTryState = _tryState;
_tryState = AllBitsSet();
VisitCatchBlockInternal(catchBlock, ref finallyState);
var tts = _tryState.Value;
IntersectWith(ref finallyState, ref tts);
if (oldTryState.HasValue)
{
var ots = oldTryState.Value;
IntersectWith(ref ots, ref tts);
oldTryState = ots;
}
_tryState = oldTryState;
}
else
{
VisitCatchBlockInternal(catchBlock, ref finallyState);
}
}

private void VisitCatchBlockInternal(BoundCatchBlock catchBlock, ref LocalState finallyState)
{
DeclareVariables(catchBlock.Locals);

Expand All @@ -2019,32 +1964,6 @@ private void VisitCatchBlockInternal(BoundCatchBlock catchBlock, ref LocalState
}
}

protected override void VisitFinallyBlock(BoundStatement finallyBlock, ref LocalState unsetInFinally)
{
if (trackUnassignments)
{
OptionalState oldTryState = _tryState;
_tryState = AllBitsSet();
base.VisitFinallyBlock(finallyBlock, ref unsetInFinally);
var tts = _tryState.Value;
IntersectWith(ref unsetInFinally, ref tts);
if (oldTryState.HasValue)
{
var ots = oldTryState.Value;
IntersectWith(ref ots, ref tts);
oldTryState = ots;
}

_tryState = oldTryState;
}
else
{
base.VisitFinallyBlock(finallyBlock, ref unsetInFinally);
}
}

#endregion TryStatements

public override BoundNode VisitFieldAccess(BoundFieldAccess node)
{
var result = base.VisitFieldAccess(node);
Expand Down
29 changes: 26 additions & 3 deletions src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ private NullableWalker(
ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> returnTypes,
VariableState initialState,
Action<BoundExpression, TypeSymbolWithAnnotations> callbackOpt)
: base(compilation, method, node, new EmptyStructTypeCache(compilation, dev12CompilerCompatibility: false), trackUnassignments: false)
: base(compilation, method, node, new EmptyStructTypeCache(compilation, dev12CompilerCompatibility: false), trackUnassignments: true)
{
_sourceAssembly = (method is null) ? null : (SourceAssemblySymbol)method.ContainingAssembly;
_callbackOpt = callbackOpt;
Expand Down Expand Up @@ -629,11 +629,18 @@ private void TrackNullableStateForAssignment(BoundExpression value, TypeSymbolWi
if (targetSlot >= this.State.Capacity) Normalize(ref this.State);

// https://github.com/dotnet/roslyn/issues/29968 Remove isByRefTarget check?
this.State[targetSlot] = isByRefTarget ?
var newState = isByRefTarget ?
// Since reference can point to the heap, we cannot assume the value is not null after this assignment,
// regardless of what value is being assigned.
(targetType.IsNullable == true) ? targetType.NullableAnnotation : NullableAnnotation.Unknown :
valueType.NullableAnnotation;
this.State[targetSlot] = newState;
if (newState.IsAnyNullable() && _tryState.HasValue)
{
var state = _tryState.Value;
state[targetSlot] = NullableAnnotation.NullableBasedOnAnalysis;
_tryState = state;
}

// https://github.com/dotnet/roslyn/issues/29968 Might this clear state that
// should be copied in InheritNullableStateOfTrackableType?
Expand Down Expand Up @@ -826,7 +833,10 @@ protected override LocalState UnreachableState()

protected override LocalState AllBitsSet()
{
return new LocalState(reachable: true, new ArrayBuilder<NullableAnnotation>(nextVariableSlot));
// Create a reachable state in which all variables are known to be non-null.
var builder = new ArrayBuilder<NullableAnnotation>(nextVariableSlot);
builder.AddMany(NullableAnnotation.NotNullableBasedOnAnalysis, nextVariableSlot);
return new LocalState(reachable: true, builder);
}

private void EnterParameters()
Expand Down Expand Up @@ -4592,6 +4602,19 @@ public override BoundNode VisitThrowExpression(BoundThrowExpression node)
return result;
}

public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement node)
{
BoundExpression expr = node.Expression;
if (expr == null)
{
return null;
}
var method = (MethodSymbol)_member;
TypeSymbolWithAnnotations elementType = InMethodBinder.GetIteratorElementTypeFromReturnType(compilation, RefKind.None, method.ReturnType.TypeSymbol, errorLocationNode: null, diagnostics: null);
VisitOptionalImplicitConversion(expr, elementType, useLegacyWarnings: false, AssignmentKind.Return);
return null;
}

#endregion Visitors

protected override string Dump(LocalState state)
Expand Down
Loading