diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
index 28095859578b..23f65dcb803d 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
@@ -1121,7 +1121,8 @@ private BoundExpression BindTypeOf(TypeOfExpressionSyntax node, DiagnosticBag di
TypeofBinder typeofBinder = new TypeofBinder(typeSyntax, this); //has special handling for unbound types
AliasSymbol alias;
- TypeSymbol type = typeofBinder.BindType(typeSyntax, diagnostics, out alias).Type;
+ TypeWithAnnotations typeWithAnnotations = typeofBinder.BindType(typeSyntax, diagnostics, out alias);
+ TypeSymbol type = typeWithAnnotations.Type;
bool hasError = false;
@@ -1133,7 +1134,7 @@ private BoundExpression BindTypeOf(TypeOfExpressionSyntax node, DiagnosticBag di
hasError = true;
}
- BoundTypeExpression boundType = new BoundTypeExpression(typeSyntax, alias, type, type.IsErrorType());
+ BoundTypeExpression boundType = new BoundTypeExpression(typeSyntax, alias, typeWithAnnotations, type.IsErrorType());
return new BoundTypeOfOperator(node, boundType, null, this.GetWellKnownType(WellKnownType.System_Type, diagnostics, node), hasError);
}
@@ -1141,11 +1142,12 @@ private BoundExpression BindSizeOf(SizeOfExpressionSyntax node, DiagnosticBag di
{
ExpressionSyntax typeSyntax = node.Type;
AliasSymbol alias;
- TypeSymbol type = this.BindType(typeSyntax, diagnostics, out alias).Type;
+ TypeWithAnnotations typeWithAnnotations = this.BindType(typeSyntax, diagnostics, out alias);
+ TypeSymbol type = typeWithAnnotations.Type;
bool typeHasErrors = type.IsErrorType() || CheckManagedAddr(type, node, diagnostics);
- BoundTypeExpression boundType = new BoundTypeExpression(typeSyntax, alias, type, typeHasErrors);
+ BoundTypeExpression boundType = new BoundTypeExpression(typeSyntax, alias, typeWithAnnotations, typeHasErrors);
ConstantValue constantValue = GetConstantSizeOf(type);
bool hasErrors = ReferenceEquals(constantValue, null) && ReportUnsafeIfNotAllowed(node, diagnostics, type);
return new BoundSizeOfOperator(node, boundType, constantValue,
@@ -5716,7 +5718,7 @@ private BoundExpression BindMemberAccessWithBoundLeft(
Error(diagnostics, lookupResult.Error, right);
return new BoundTypeExpression(node, null,
- new ExtendedErrorTypeSymbol(GetContainingNamespaceOrType(symbols[0]), symbols.ToImmutable(), lookupResult.Kind, lookupResult.Error, rightArity));
+ new ExtendedErrorTypeSymbol(GetContainingNamespaceOrType(symbols[0]), symbols.ToImmutable(), lookupResult.Kind, lookupResult.Error, rightArity));
}
else if (lookupResult.Kind == LookupResultKind.Empty)
{
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
index 258b717842e4..8d1deb82f2d6 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
@@ -2783,7 +2783,7 @@ private BoundExpression BindIsOperator(BinaryExpressionSyntax node, DiagnosticBa
operandHasErrors = true;
}
- var typeExpression = new BoundTypeExpression(node.Right, alias, targetType);
+ var typeExpression = new BoundTypeExpression(node.Right, alias, targetTypeWithAnnotations);
var targetTypeKind = targetType.TypeKind;
if (operandHasErrors || IsOperatorErrors(node, operand.Type, typeExpression, diagnostics))
{
@@ -3151,7 +3151,7 @@ private BoundExpression BindAsOperator(BinaryExpressionSyntax node, DiagnosticBa
AliasSymbol alias;
TypeWithAnnotations targetTypeWithAnnotations = BindType(node.Right, diagnostics, out alias);
TypeSymbol targetType = targetTypeWithAnnotations.Type;
- var typeExpression = new BoundTypeExpression(node.Right, alias, targetType);
+ var typeExpression = new BoundTypeExpression(node.Right, alias, targetTypeWithAnnotations);
var targetTypeKind = targetType.TypeKind;
var resultType = targetType;
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
index 09467d79a56c..c420becf6606 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
@@ -363,7 +363,7 @@ private BoundPattern BindDeclarationPattern(
TypeSymbol declType = boundDeclType.Type;
inputValEscape = GetValEscape(declType, inputValEscape);
BindPatternDesignation(
- node.Designation, boundDeclType.Type, inputValEscape, typeSyntax, diagnostics,
+ node.Designation, boundDeclType.TypeWithAnnotations, inputValEscape, typeSyntax, diagnostics,
ref hasErrors, out Symbol variableSymbol, out BoundExpression variableAccess);
return new BoundDeclarationPattern(node, variableSymbol, variableAccess, boundDeclType, isVar: false, inputType, hasErrors);
}
@@ -379,14 +379,14 @@ private BoundTypeExpression BindPatternType(
TypeWithAnnotations declType = BindType(typeSyntax, diagnostics, out AliasSymbol aliasOpt);
Debug.Assert(declType.HasType);
Debug.Assert(typeSyntax.Kind() != SyntaxKind.NullableType); // the syntax does not permit nullable annotations
- BoundTypeExpression boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: false, type: declType.Type);
+ BoundTypeExpression boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: false, type: declType);
hasErrors |= CheckValidPatternType(typeSyntax, inputType, declType.Type, patternTypeWasInSource: true, diagnostics: diagnostics);
return boundDeclType;
}
private void BindPatternDesignation(
VariableDesignationSyntax designation,
- TypeSymbol declType,
+ TypeWithAnnotations declType,
uint inputValEscape,
TypeSyntax typeSyntax,
DiagnosticBag diagnostics,
@@ -405,18 +405,18 @@ private void BindPatternDesignation(
if ((InConstructorInitializer || InFieldInitializer) && ContainingMemberOrLambda.ContainingSymbol.Kind == SymbolKind.NamedType)
CheckFeatureAvailability(designation, MessageID.IDS_FeatureExpressionVariablesInQueriesAndInitializers, diagnostics);
- localSymbol.SetTypeWithAnnotations(TypeWithAnnotations.Create(declType));
- localSymbol.SetValEscape(GetValEscape(declType, inputValEscape));
+ localSymbol.SetTypeWithAnnotations(declType);
+ localSymbol.SetValEscape(GetValEscape(declType.Type, inputValEscape));
// Check for variable declaration errors.
hasErrors |= localSymbol.ScopeBinder.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics);
if (!hasErrors)
- hasErrors = CheckRestrictedTypeInAsync(this.ContainingMemberOrLambda, declType, diagnostics, typeSyntax ?? (SyntaxNode)designation);
+ hasErrors = CheckRestrictedTypeInAsync(this.ContainingMemberOrLambda, declType.Type, diagnostics, typeSyntax ?? (SyntaxNode)designation);
variableSymbol = localSymbol;
variableAccess = new BoundLocal(
- syntax: designation, localSymbol: localSymbol, constantValueOpt: null, type: declType);
+ syntax: designation, localSymbol: localSymbol, constantValueOpt: null, type: declType.Type);
return;
}
else
@@ -425,7 +425,7 @@ private void BindPatternDesignation(
Debug.Assert(designation.SyntaxTree.Options.Kind != SourceCodeKind.Regular);
GlobalExpressionVariable expressionVariableField = LookupDeclaredField(singleVariableDesignation);
DiagnosticBag tempDiagnostics = DiagnosticBag.GetInstance();
- expressionVariableField.SetTypeWithAnnotations(TypeWithAnnotations.Create(declType), tempDiagnostics);
+ expressionVariableField.SetTypeWithAnnotations(declType, tempDiagnostics);
tempDiagnostics.Free();
BoundExpression receiver = SynthesizeReceiver(designation, expressionVariableField, diagnostics);
@@ -454,7 +454,7 @@ private static uint GetValEscape(TypeSymbol type, uint possibleValEscape)
return type.IsRefLikeType ? possibleValEscape : Binder.ExternalScope;
}
- TypeSymbol BindRecursivePatternType(
+ TypeWithAnnotations BindRecursivePatternType(
TypeSyntax typeSyntax,
TypeSymbol inputType,
DiagnosticBag diagnostics,
@@ -464,12 +464,13 @@ TypeSymbol BindRecursivePatternType(
if (typeSyntax != null)
{
boundDeclType = BindPatternType(typeSyntax, inputType, diagnostics, ref hasErrors);
- return boundDeclType.Type;
+ return boundDeclType.TypeWithAnnotations;
}
else
{
boundDeclType = null;
- return inputType.StrippedType(); // remove the nullable part of the input's type
+ // remove the nullable part of the input's type; e.g. a nullable int becomes an int in a recursive pattern
+ return new TypeWithState(inputType.StrippedType(), NullableFlowState.MaybeNull).ToTypeWithAnnotations();
}
}
@@ -493,7 +494,8 @@ private BoundPattern BindRecursivePattern(RecursivePatternSyntax node, TypeSymbo
}
TypeSyntax typeSyntax = node.Type;
- TypeSymbol declType = BindRecursivePatternType(typeSyntax, inputType, diagnostics, ref hasErrors, out BoundTypeExpression boundDeclType);
+ TypeWithAnnotations declTypeWithAnnotations = BindRecursivePatternType(typeSyntax, inputType, diagnostics, ref hasErrors, out BoundTypeExpression boundDeclType);
+ TypeSymbol declType = declTypeWithAnnotations.Type;
inputValEscape = GetValEscape(declType, inputValEscape);
MethodSymbol deconstructMethod = null;
@@ -553,7 +555,7 @@ private BoundPattern BindRecursivePattern(RecursivePatternSyntax node, TypeSymbo
}
BindPatternDesignation(
- node.Designation, declType, inputValEscape, typeSyntax, diagnostics,
+ node.Designation, declTypeWithAnnotations, inputValEscape, typeSyntax, diagnostics,
ref hasErrors, out Symbol variableSymbol, out BoundExpression variableAccess);
return new BoundRecursivePattern(
syntax: node, declaredType: boundDeclType, inputType: inputType, deconstructMethod: deconstructMethod,
@@ -839,10 +841,11 @@ private BoundPattern BindVarDesignation(
}
case SyntaxKind.SingleVariableDesignation:
{
+ var declType = new TypeWithState(inputType, NullableFlowState.MaybeNull).ToTypeWithAnnotations();
BindPatternDesignation(
- designation: node, declType: inputType, inputValEscape: inputValEscape, typeSyntax: null, diagnostics: diagnostics, hasErrors: ref hasErrors,
+ designation: node, declType: declType, inputValEscape: inputValEscape, typeSyntax: null, diagnostics: diagnostics, hasErrors: ref hasErrors,
variableSymbol: out Symbol variableSymbol, variableAccess: out BoundExpression variableAccess);
- var boundOperandType = new BoundTypeExpression(syntax: node, aliasOpt: null, type: inputType); // fake a type expression for the variable's type
+ var boundOperandType = new BoundTypeExpression(syntax: node, aliasOpt: null, type: declType); // fake a type expression for the variable's type
// We continue to use a BoundDeclarationPattern for the var pattern, as they have more in common.
return new BoundDeclarationPattern(
node.Parent.Kind() == SyntaxKind.VarPattern ? node.Parent : node, // for `var x` use whole pattern, otherwise use designation for the syntax
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
index 32a13b4858cf..b602dbcc1610 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
@@ -1076,7 +1076,7 @@ protected BoundLocalDeclaration BindVariableDeclaration(
}
diagnostics.AddRangeAndFree(localDiagnostics);
- var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declTypeOpt.Type);
+ var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declTypeOpt);
return new BoundLocalDeclaration(associatedSyntaxNode, localSymbol, boundDeclType, initializerOpt, arguments, hasErrors);
}
diff --git a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs
index 8cf706319517..46d3524b71bf 100644
--- a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs
+++ b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs
@@ -24,7 +24,7 @@ namespace Microsoft.CodeAnalysis.CSharp
/// represents the test performed by evaluating the expression of the
/// when-clause of a switch case; and represents a leaf node when we
/// have finally determined exactly which case matches. Each test processes a single input, and there are
- /// four kinds: tests a value for null;
+ /// four kinds: tests a value for null;
/// tests that a value is not null; checks if the value is of a given type;
/// and checks if the value is equal to a given constant. Of the evaluations,
/// there are which represents an invocation of a type's
@@ -121,7 +121,7 @@ private BoundDecisionDag CreateDecisionDagForIsPattern(
BoundPattern pattern,
LabelSymbol whenTrueLabel)
{
- var rootIdentifier = new BoundDagTemp(inputExpression.Syntax, inputExpression.Type, source: null, index: 0);
+ var rootIdentifier = BoundDagTemp.ForOriginalInput(inputExpression);
return MakeDecisionDag(syntax, ImmutableArray.Create(MakeTestsForPattern(index: 1, pattern.Syntax, rootIdentifier, pattern, whenClause: null, whenTrueLabel)));
}
@@ -130,7 +130,7 @@ private BoundDecisionDag CreateDecisionDagForSwitchStatement(
BoundExpression switchGoverningExpression,
ImmutableArray switchSections)
{
- var rootIdentifier = new BoundDagTemp(switchGoverningExpression.Syntax, switchGoverningExpression.Type, source: null, index: 0);
+ var rootIdentifier = BoundDagTemp.ForOriginalInput(switchGoverningExpression);
int i = 0;
var builder = ArrayBuilder.GetInstance(switchSections.Length);
foreach (BoundSwitchSection section in switchSections)
@@ -155,7 +155,7 @@ private BoundDecisionDag CreateDecisionDagForSwitchExpression(
BoundExpression switchExpressionInput,
ImmutableArray switchArms)
{
- var rootIdentifier = new BoundDagTemp(switchExpressionInput.Syntax, switchExpressionInput.Type, source: null, index: 0);
+ var rootIdentifier = BoundDagTemp.ForOriginalInput(switchExpressionInput);
int i = 0;
var builder = ArrayBuilder.GetInstance(switchArms.Length);
foreach (BoundSwitchExpressionArm arm in switchArms)
@@ -311,18 +311,18 @@ private void MakeTestsAndBindings(
tests.Add(new BoundDagTypeTest(syntax, iTupleType, input));
var valueAsITupleEvaluation = new BoundDagTypeEvaluation(syntax, iTupleType, input);
tests.Add(valueAsITupleEvaluation);
- var valueAsITuple = new BoundDagTemp(syntax, iTupleType, valueAsITupleEvaluation, 0);
+ var valueAsITuple = new BoundDagTemp(syntax, iTupleType, valueAsITupleEvaluation);
var lengthEvaluation = new BoundDagPropertyEvaluation(syntax, getLengthProperty, valueAsITuple);
tests.Add(lengthEvaluation);
- var lengthTemp = new BoundDagTemp(syntax, this._compilation.GetSpecialType(SpecialType.System_Int32), lengthEvaluation, 0);
+ var lengthTemp = new BoundDagTemp(syntax, this._compilation.GetSpecialType(SpecialType.System_Int32), lengthEvaluation);
tests.Add(new BoundDagValueTest(syntax, ConstantValue.Create(patternLength), lengthTemp));
for (int i = 0; i < patternLength; i++)
{
var indexEvaluation = new BoundDagIndexEvaluation(syntax, getItemProperty, i, valueAsITuple);
tests.Add(indexEvaluation);
- var indexTemp = new BoundDagTemp(syntax, objectType, indexEvaluation, 0);
+ var indexTemp = new BoundDagTemp(syntax, objectType, indexEvaluation);
MakeTestsAndBindings(indexTemp, pattern.Subpatterns[i].Pattern, tests, bindings);
}
}
@@ -393,7 +393,7 @@ private BoundDagTemp MakeConvertToType(
}
var evaluation = new BoundDagTypeEvaluation(syntax, type, input);
- input = new BoundDagTemp(syntax, type, evaluation, index: 0);
+ input = new BoundDagTemp(syntax, type, evaluation);
tests.Add(evaluation);
}
@@ -408,7 +408,7 @@ private void MakeTestsAndBindings(
{
if (constant.ConstantValue == ConstantValue.Null)
{
- tests.Add(new BoundDagNullTest(constant.Syntax, input));
+ tests.Add(new BoundDagExplicitNullTest(constant.Syntax, input));
}
else
{
@@ -479,7 +479,7 @@ private void MakeTestsAndBindings(
FieldSymbol field = elements[i];
var evaluation = new BoundDagFieldEvaluation(syntax, field, input); // fetch the ItemN field
tests.Add(evaluation);
- var output = new BoundDagTemp(syntax, field.Type, evaluation, index: 0);
+ var output = new BoundDagTemp(syntax, field.Type, evaluation);
MakeTestsAndBindings(output, pattern, tests, bindings);
}
}
@@ -516,7 +516,7 @@ private void MakeTestsAndBindings(
}
tests.Add(evaluation);
- var output = new BoundDagTemp(pattern.Syntax, symbol.GetTypeOrReturnType().Type, evaluation, index: 0);
+ var output = new BoundDagTemp(pattern.Syntax, symbol.GetTypeOrReturnType().Type, evaluation);
MakeTestsAndBindings(output, pattern, tests, bindings);
}
}
@@ -620,9 +620,20 @@ DagState uniqifyState(ImmutableArray cases)
// An evaluation is considered to always succeed, so there is no false branch
break;
case BoundDagTest d:
- SplitCases(state.Cases, d, out ImmutableArray whenTrueDecisions, out ImmutableArray whenFalseDecisions);
+ bool foundExplicitNullTest = false;
+ SplitCases(
+ state.Cases, d,
+ out ImmutableArray whenTrueDecisions,
+ out ImmutableArray whenFalseDecisions,
+ ref foundExplicitNullTest);
state.TrueBranch = uniqifyState(whenTrueDecisions);
state.FalseBranch = uniqifyState(whenFalseDecisions);
+ if (foundExplicitNullTest && d is BoundDagNonNullTest t)
+ {
+ // Turn an "implicit" non-null test into an explicit null test to preserve its explicitness
+ state.SelectedTest = new BoundDagExplicitNullTest(t.Syntax, t.Input, t.HasErrors);
+ (state.TrueBranch, state.FalseBranch) = (state.FalseBranch, state.TrueBranch);
+ }
break;
case var n:
throw ExceptionUtilities.UnexpectedValue(n.Kind);
@@ -721,13 +732,14 @@ private void SplitCases(
ImmutableArray cases,
BoundDagTest d,
out ImmutableArray whenTrue,
- out ImmutableArray whenFalse)
+ out ImmutableArray whenFalse,
+ ref bool foundExplicitNullTest)
{
var whenTrueBuilder = ArrayBuilder.GetInstance();
var whenFalseBuilder = ArrayBuilder.GetInstance();
foreach (RemainingTestsForCase c in cases)
{
- FilterCase(c, d, whenTrueBuilder, whenFalseBuilder);
+ FilterCase(c, d, whenTrueBuilder, whenFalseBuilder, ref foundExplicitNullTest);
}
whenTrue = whenTrueBuilder.ToImmutableAndFree();
@@ -738,7 +750,8 @@ private void FilterCase(
RemainingTestsForCase testsForCase,
BoundDagTest test,
ArrayBuilder whenTrueBuilder,
- ArrayBuilder whenFalseBuilder)
+ ArrayBuilder whenFalseBuilder,
+ ref bool foundExplicitNullTest)
{
var trueBuilder = ArrayBuilder.GetInstance();
var falseBuilder = ArrayBuilder.GetInstance();
@@ -751,7 +764,8 @@ private void FilterCase(
trueTestPermitsTrueOther: out bool trueDecisionPermitsTrueOther,
falseTestPermitsTrueOther: out bool falseDecisionPermitsTrueOther,
trueTestImpliesTrueOther: out bool trueDecisionImpliesTrueOther,
- falseTestImpliesTrueOther: out bool falseDecisionImpliesTrueOther);
+ falseTestImpliesTrueOther: out bool falseDecisionImpliesTrueOther,
+ foundExplicitNullTest: ref foundExplicitNullTest);
if (trueDecisionPermitsTrueOther)
{
if (!trueDecisionImpliesTrueOther)
@@ -827,7 +841,8 @@ private void CheckConsistentDecision(
out bool trueTestPermitsTrueOther,
out bool falseTestPermitsTrueOther,
out bool trueTestImpliesTrueOther,
- out bool falseTestImpliesTrueOther)
+ out bool falseTestImpliesTrueOther,
+ ref bool foundExplicitNullTest)
{
// innocent until proven guilty
trueTestPermitsTrueOther = true;
@@ -858,7 +873,8 @@ private void CheckConsistentDecision(
// !(v != null) --> !(v == K)
falseTestPermitsTrueOther = false;
break;
- case BoundDagNullTest v2:
+ case BoundDagExplicitNullTest v2:
+ foundExplicitNullTest = true;
// v != null --> !(v == null)
trueTestPermitsTrueOther = false;
// !(v != null) --> v == null
@@ -914,7 +930,8 @@ private void CheckConsistentDecision(
break;
case BoundDagValueTest v2:
break;
- case BoundDagNullTest v2:
+ case BoundDagExplicitNullTest v2:
+ foundExplicitNullTest = true;
// v is T --> !(v == null)
trueTestPermitsTrueOther = false;
break;
@@ -929,7 +946,8 @@ private void CheckConsistentDecision(
break;
case BoundDagTypeTest t2:
break;
- case BoundDagNullTest v2:
+ case BoundDagExplicitNullTest v2:
+ foundExplicitNullTest = true;
// v == K --> !(v == null)
trueTestPermitsTrueOther = false;
break;
@@ -959,7 +977,8 @@ private void CheckConsistentDecision(
break;
}
break;
- case BoundDagNullTest v1:
+ case BoundDagExplicitNullTest v1:
+ foundExplicitNullTest = true;
switch (other)
{
case BoundDagNonNullTest n2:
@@ -972,7 +991,8 @@ private void CheckConsistentDecision(
// v == null --> !(v is T)
trueTestPermitsTrueOther = false;
break;
- case BoundDagNullTest v2:
+ case BoundDagExplicitNullTest v2:
+ foundExplicitNullTest = true;
// v == null --> v == null
trueTestImpliesTrueOther = true;
// !(v == null) --> !(v == null)
@@ -1067,7 +1087,7 @@ int tempIdentifier(BoundDagEvaluation e)
string tempName(BoundDagTemp t)
{
- return $"t{tempIdentifier(t.Source)}{(t.Index != 0 ? $".{t.Index.ToString()}" : "")}";
+ return $"t{tempIdentifier(t.Source)}";
}
var resultBuilder = PooledStringBuilder.GetInstance();
@@ -1264,7 +1284,7 @@ private static bool SameTest(BoundDagTest x, BoundDagTest y)
case BoundKind.DagValueTest:
return ((BoundDagValueTest)x).Value == ((BoundDagValueTest)y).Value;
- case BoundKind.DagNullTest:
+ case BoundKind.DagExplicitNullTest:
case BoundKind.DagNonNullTest:
return true;
diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
index f804fcbda8cc..f88a7d13853d 100644
--- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
+++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
@@ -219,7 +219,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
}
}
- TypeSymbol iterationVariableType;
+ TypeWithAnnotations iterationVariableType;
BoundTypeExpression boundIterationVariableType;
bool hasNameConflicts = false;
BoundForEachDeconstructStep deconstructStep = null;
@@ -251,7 +251,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
Debug.Assert(declType.HasType);
}
- iterationVariableType = declType.Type;
+ iterationVariableType = declType;
boundIterationVariableType = new BoundTypeExpression(typeSyntax, alias, iterationVariableType);
SourceLocalSymbol local = this.IterationVariable;
@@ -309,12 +309,12 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
case SyntaxKind.ForEachVariableStatement:
{
var node = (ForEachVariableStatementSyntax)_syntax;
- iterationVariableType = inferredType.Type ?? CreateErrorType("var");
+ iterationVariableType = inferredType.HasType ? inferredType : TypeWithAnnotations.Create(CreateErrorType("var"));
var variables = node.Variable;
if (variables.IsDeconstructionLeft())
{
- var valuePlaceholder = new BoundDeconstructValuePlaceholder(_syntax.Expression, collectionEscape, iterationVariableType).MakeCompilerGenerated();
+ var valuePlaceholder = new BoundDeconstructValuePlaceholder(_syntax.Expression, collectionEscape, iterationVariableType.Type).MakeCompilerGenerated();
DeclarationExpressionSyntax declaration = null;
ExpressionSyntax expression = null;
BoundDeconstructionAssignmentOperator deconstruction = BindDeconstruction(
@@ -370,7 +370,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
iterationVariables.All(local => local.DeclarationKind == LocalDeclarationKind.ForEachIterationVariable),
"Should not have iteration variables that are not ForEachIterationVariable in valid code");
- hasErrors = hasErrors || boundIterationVariableType.HasErrors || iterationVariableType.IsErrorType();
+ hasErrors = hasErrors || boundIterationVariableType.HasErrors || iterationVariableType.Type.IsErrorType();
// Skip the conversion checks and array/enumerator differentiation if we know we have an error (except local name conflicts).
if (hasErrors)
@@ -404,7 +404,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
// but it turns out that these are equivalent (when both are available).
HashSet useSiteDiagnostics = null;
- Conversion elementConversion = this.Conversions.ClassifyConversionFromType(inferredType.Type, iterationVariableType, ref useSiteDiagnostics, forCast: true);
+ Conversion elementConversion = this.Conversions.ClassifyConversionFromType(inferredType.Type, iterationVariableType.Type, ref useSiteDiagnostics, forCast: true);
if (!elementConversion.IsValid)
{
@@ -415,7 +415,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
}
else
{
- SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType.Type, iterationVariableType);
+ SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType.Type, iterationVariableType.Type);
diagnostics.Add(ErrorCode.ERR_NoExplicitConv, foreachKeyword.GetLocation(), distinguisher.First, distinguisher.Second);
}
hasErrors = true;
diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs
index 723358f899f4..0bd067ae4562 100644
--- a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs
+++ b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs
@@ -80,7 +80,7 @@ private bool CheckSwitchExpressionExhaustive(
}
// We only report exhaustive warnings when the default label is reachable through some series of
- // tests that do not include a test in which the value is know to be null. Handling paths with
+ // tests that do not include a test in which the value is known to be null. Handling paths with
// nulls is the job of the nullable walker.
foreach (var n in TopologicalSort.IterativeSort(new[] { decisionDag.RootNode }, nonNullSuccessors))
{
@@ -102,7 +102,7 @@ ImmutableArray nonNullSuccessors(BoundDecisionDagNode n)
{
case BoundDagNonNullTest t: // checks that the input is not null
return ImmutableArray.Create(p.WhenTrue);
- case BoundDagNullTest t: // checks that the input is null
+ case BoundDagExplicitNullTest t: // checks that the input is null
return ImmutableArray.Create(p.WhenFalse);
default:
return BoundDecisionDag.Successors(n);
diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs
index e3f4c9c1c2e7..19a37fc28c84 100644
--- a/src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs
+++ b/src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs
@@ -170,7 +170,7 @@ BoundDecisionDagNode makeReplacement(BoundDecisionDagNode dag, Func
-
-
-
@@ -206,6 +203,9 @@
identify the scenarios in which this property is populated.
-->
+
+
+
+
-
-
+
+
diff --git a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs
index cbce0c9df1af..7b4821928e2a 100644
--- a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs
+++ b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs
@@ -424,20 +424,46 @@ public BoundParameter(SyntaxNode syntax, ParameterSymbol parameterSymbol)
internal sealed partial class BoundTypeExpression
{
- public BoundTypeExpression(SyntaxNode syntax, AliasSymbol aliasOpt, TypeSymbol type, bool hasErrors = false)
+ public BoundTypeExpression(SyntaxNode syntax, AliasSymbol aliasOpt, bool inferredType, BoundTypeExpression boundContainingTypeOpt, TypeWithAnnotations typeWithAnnotations, bool hasErrors = false)
+ : this(syntax, aliasOpt, inferredType, boundContainingTypeOpt, typeWithAnnotations, typeWithAnnotations.Type, hasErrors)
+ {
+ Debug.Assert((object)typeWithAnnotations.Type != null, "Field 'type' cannot be null");
+ }
+
+ public BoundTypeExpression(SyntaxNode syntax, AliasSymbol aliasOpt, TypeWithAnnotations type, bool hasErrors = false)
: this(syntax, aliasOpt, false, null, type, hasErrors)
{
}
- public BoundTypeExpression(SyntaxNode syntax, AliasSymbol aliasOpt, TypeSymbol type)
+ public BoundTypeExpression(SyntaxNode syntax, AliasSymbol aliasOpt, TypeWithAnnotations type)
: this(syntax, aliasOpt, false, null, type)
{
}
- public BoundTypeExpression(SyntaxNode syntax, AliasSymbol aliasOpt, bool inferredType, TypeSymbol type, bool hasErrors = false)
+ public BoundTypeExpression(SyntaxNode syntax, AliasSymbol aliasOpt, bool inferredType, TypeWithAnnotations type, bool hasErrors = false)
: this(syntax, aliasOpt, inferredType, null, type, hasErrors)
{
}
+
+ public BoundTypeExpression(SyntaxNode syntax, AliasSymbol aliasOpt, bool inferredType, BoundTypeExpression boundContainingTypeOpt, TypeSymbol type, bool hasErrors = false)
+ : this(syntax, aliasOpt, inferredType, boundContainingTypeOpt, TypeWithAnnotations.Create(type), hasErrors)
+ {
+ }
+
+ public BoundTypeExpression(SyntaxNode syntax, AliasSymbol aliasOpt, TypeSymbol type, bool hasErrors = false)
+ : this(syntax, aliasOpt, TypeWithAnnotations.Create(type), hasErrors)
+ {
+ }
+
+ public BoundTypeExpression(SyntaxNode syntax, AliasSymbol aliasOpt, TypeSymbol type)
+ : this(syntax, aliasOpt, TypeWithAnnotations.Create(type))
+ {
+ }
+
+ public BoundTypeExpression(SyntaxNode syntax, AliasSymbol aliasOpt, bool inferredType, TypeSymbol type, bool hasErrors = false)
+ : this(syntax, aliasOpt, inferredType, TypeWithAnnotations.Create(type), hasErrors)
+ {
+ }
}
internal sealed partial class BoundNamespaceExpression
@@ -569,4 +595,14 @@ public BoundAddressOfOperator(SyntaxNode syntax, BoundExpression operand, TypeSy
{
}
}
+
+ internal partial class BoundDagTemp
+ {
+ public BoundDagTemp(SyntaxNode syntax, TypeSymbol type, BoundDagEvaluation source)
+ : this(syntax, type, source, index: 0, hasErrors: false)
+ {
+ }
+
+ public static BoundDagTemp ForOriginalInput(BoundExpression expr) => new BoundDagTemp(expr.Syntax, expr.Type, source: null);
+ }
}
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
index dee162725469..ff650c03ebe6 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
+++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
@@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.CSharp {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class CSharpResources {
@@ -15801,6 +15801,24 @@ internal static string WRN_SwitchExpressionNotExhaustive_Title {
}
}
+ ///
+ /// Looks up a localized string similar to The switch expression does not handle some null inputs (it is not exhaustive)..
+ ///
+ internal static string WRN_SwitchExpressionNotExhaustiveForNull {
+ get {
+ return ResourceManager.GetString("WRN_SwitchExpressionNotExhaustiveForNull", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The switch expression does not handle some null inputs..
+ ///
+ internal static string WRN_SwitchExpressionNotExhaustiveForNull_Title {
+ get {
+ return ResourceManager.GetString("WRN_SwitchExpressionNotExhaustiveForNull_Title", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Source file has exceeded the limit of 16,707,565 lines representable in the PDB; debug information will be incorrect.
///
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index 0962f512fb8f..b83ae1f359d6 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -5796,4 +5796,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
The feature '{0}' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+
+ The switch expression does not handle some null inputs (it is not exhaustive).
+
+
+ The switch expression does not handle some null inputs.
+
diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
index 14282f970663..14de10528650 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
@@ -1244,7 +1244,7 @@ internal enum ErrorCode
ERR_InvalidDebugInfo = 7103,
#endregion diagnostics introduced in C# 6
- // huge gap here; unused 7104-8000
+ // unused 7104-8000
#region more diagnostics introduced in Roslyn (C# 6)
WRN_UnimplementedCommandLineSwitch = 8001,
@@ -1356,7 +1356,7 @@ internal enum ErrorCode
ERR_LocalFunctionMissingBody = 8112,
ERR_InvalidHashAlgorithmName = 8113,
- // Available = 8113, 8114, 8115
+ // Unused 8113, 8114, 8115
#region diagnostics for pattern-matching introduced in C# 7
ERR_ThrowMisplaced = 8115,
@@ -1450,7 +1450,7 @@ internal enum ErrorCode
ERR_BadLanguageVersion = 8192,
#endregion
- // Available = 8193-8195
+ // Unused 8193-8195
#region diagnostics for out var
ERR_ImplicitlyTypedOutVariableUsedInTheSameArgumentList = 8196,
@@ -1633,9 +1633,7 @@ internal enum ErrorCode
WRN_NullReferenceReceiver = 8602,
WRN_NullReferenceReturn = 8603,
WRN_NullReferenceArgument = 8604,
- // Available = 8605,
- // Available = 8606,
- // Available = 8607,
+ // Unused 8605-8607
WRN_NullabilityMismatchInTypeOnOverride = 8608,
WRN_NullabilityMismatchInReturnTypeOnOverride = 8609,
WRN_NullabilityMismatchInParameterTypeOnOverride = 8610,
@@ -1667,7 +1665,7 @@ internal enum ErrorCode
ERR_BadNullableContextOption = 8636,
ERR_NullableDirectiveQualifierExpected = 8637,
WRN_ConditionalAccessMayReturnNull = 8638,
- // Available = 8639,
+ // Unused 8639
ERR_ExpressionTreeCantContainRefStruct = 8640,
ERR_ElseCannotStartStatement = 8641,
ERR_ExpressionTreeCantContainNullCoalescingAssignment = 8642,
@@ -1683,6 +1681,7 @@ internal enum ErrorCode
ERR_FeatureInPreview = 8652,
WRN_DefaultExpressionMayIntroduceNullT = 8653,
WRN_NullLiteralMayIntroduceNullT = 8654,
+ WRN_SwitchExpressionNotExhaustiveForNull = 8655,
#endregion diagnostics introduced for C# 8.0
diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
index 272aace97be3..b175cae43fe2 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
@@ -40,6 +40,7 @@ static ErrorFacts()
builder.Add(getId(ErrorCode.WRN_NullLiteralMayIntroduceNullT));
builder.Add(getId(ErrorCode.WRN_ConditionalAccessMayReturnNull));
builder.Add(getId(ErrorCode.WRN_AsOperatorMayReturnNull));
+ builder.Add(getId(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull));
NullableFlowAnalysisSafetyWarnings = builder.ToImmutable();
@@ -404,6 +405,7 @@ internal static int GetWarningLevel(ErrorCode code)
case ErrorCode.WRN_NullLiteralMayIntroduceNullT:
case ErrorCode.WRN_ConditionalAccessMayReturnNull:
case ErrorCode.WRN_AsOperatorMayReturnNull:
+ case ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull:
return 1;
default:
return 0;
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs
index 6cef15b88541..33490ac1274c 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_Switch.cs
@@ -13,18 +13,38 @@ internal abstract partial class AbstractFlowPass
{
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node)
{
- // visit switch header
- VisitRvalue(node.Expression);
+ // dispatch to the switch sections
+ var initialState = VisitSwitchStatementDispatch(node);
+
+ // visit switch sections
+ var afterSwitchState = UnreachableState();
+ var switchSections = node.SwitchSections;
+ var iLastSection = (switchSections.Length - 1);
+ for (var iSection = 0; iSection <= iLastSection; iSection++)
+ {
+ VisitSwitchSection(switchSections[iSection], iSection == iLastSection);
+ // Even though it is illegal for the end of a switch section to be reachable, in erroneous
+ // code it may be reachable. We treat that as an implicit break (branch to afterSwitchState).
+ Join(ref afterSwitchState, ref this.State);
+ }
+
+ if (node.DecisionDag.ReachableLabels.Contains(node.BreakLabel) || node.DefaultLabel == null && IsTraditionalSwitch(node))
+ {
+ Join(ref afterSwitchState, ref initialState);
+ }
- // visit switch block
- VisitSwitchBlock(node);
+ ResolveBreaks(afterSwitchState, node.BreakLabel);
return null;
}
- private void VisitSwitchBlock(BoundSwitchStatement node)
+ protected virtual TLocalState VisitSwitchStatementDispatch(BoundSwitchStatement node)
{
- var initialState = State.Clone();
+ // visit switch header
+ VisitRvalue(node.Expression);
+
+ TLocalState initialState = this.State.Clone();
+
var reachableLabels = node.DecisionDag.ReachableLabels;
foreach (var section in node.SwitchSections)
{
@@ -52,24 +72,7 @@ private void VisitSwitchBlock(BoundSwitchStatement node)
}
}
- // visit switch sections
- var afterSwitchState = UnreachableState();
- var switchSections = node.SwitchSections;
- var iLastSection = (switchSections.Length - 1);
- for (var iSection = 0; iSection <= iLastSection; iSection++)
- {
- VisitSwitchSection(switchSections[iSection], iSection == iLastSection);
- // Even though it is illegal for the end of a switch section to be reachable, in erroneous
- // code it may be reachable. We treat that as an implicit break (branch to afterSwitchState).
- Join(ref afterSwitchState, ref this.State);
- }
-
- if (reachableLabels.Contains(node.BreakLabel) || node.DefaultLabel == null && IsTraditionalSwitch(node))
- {
- Join(ref afterSwitchState, ref initialState);
- }
-
- ResolveBreaks(afterSwitchState, node.BreakLabel);
+ return initialState;
}
///
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.LocalFunctions.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.LocalFunctions.cs
index 3295def19eee..fc0b730c9e93 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.LocalFunctions.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.LocalFunctions.cs
@@ -222,7 +222,7 @@ private void RecordReadInLocalFunction(int slot)
// fields we need to record each field assignment separately,
// since some fields may be assigned when this read is replayed
VariableIdentifier id = variableBySlot[slot];
- var type = VariableTypeWithAnnotations(id.Symbol).Type;
+ var type = id.Symbol.GetTypeOrReturnType().Type;
Debug.Assert(!_emptyStructTypeCache.IsEmptyStructType(type));
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.VariableIdentifier.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.VariableIdentifier.cs
index 3aac75c3c78f..9e7d3727ea4e 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.VariableIdentifier.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.VariableIdentifier.cs
@@ -16,9 +16,17 @@ internal partial class LocalDataFlowPass
public VariableIdentifier(Symbol symbol, int containingSlot = 0)
{
- Debug.Assert(symbol.Kind == SymbolKind.Local || symbol.Kind == SymbolKind.Field || symbol.Kind == SymbolKind.Parameter ||
- (symbol as MethodSymbol)?.MethodKind == MethodKind.LocalFunction ||
- symbol.Kind == SymbolKind.Property || symbol.Kind == SymbolKind.Event);
+ Debug.Assert(symbol.Kind switch
+ {
+ SymbolKind.Local => true,
+ SymbolKind.Parameter => true,
+ SymbolKind.Field => true,
+ SymbolKind.Property => true,
+ SymbolKind.Event => true,
+ SymbolKind.ErrorType => true,
+ SymbolKind.Method when symbol is MethodSymbol m && m.MethodKind == MethodKind.LocalFunction => true,
+ _ => false
+ });
Symbol = symbol;
ContainingSlot = containingSlot;
}
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs
index fa3e034cfc36..5fb7edf8d3f4 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs
@@ -906,7 +906,7 @@ protected virtual void ReportUnassigned(Symbol symbol, SyntaxNode node, int slot
// We've already reported the use of a local before its declaration. No need to emit
// another diagnostic for the same issue.
}
- else if (!_alreadyReported[slot] && !VariableTypeWithAnnotations(symbol).Type.IsErrorType())
+ else if (!_alreadyReported[slot] && !symbol.GetTypeOrReturnType().Type.IsErrorType())
{
// CONSIDER: could suppress this diagnostic in cases where the local was declared in a using
// or fixed statement because there's a special error code for not initializing those.
@@ -1231,7 +1231,7 @@ private bool FieldsAllSet(int containingSlot, LocalState state)
Debug.Assert(containingSlot != -1);
Debug.Assert(!state.IsAssigned(containingSlot));
VariableIdentifier variable = variableBySlot[containingSlot];
- TypeSymbol structType = VariableTypeWithAnnotations(variable.Symbol).Type;
+ TypeSymbol structType = variable.Symbol.GetTypeOrReturnType().Type;
foreach (var field in _emptyStructTypeCache.GetStructInstanceFields(structType))
{
if (_emptyStructTypeCache.IsEmptyStructType(field.Type)) continue;
@@ -1259,7 +1259,7 @@ private void SetSlotAssigned(int slot, ref LocalState state)
{
if (slot < 0) return;
VariableIdentifier id = variableBySlot[slot];
- TypeSymbol type = VariableTypeWithAnnotations(id.Symbol).Type;
+ TypeSymbol type = id.Symbol.GetTypeOrReturnType().Type;
Debug.Assert(!_emptyStructTypeCache.IsEmptyStructType(type));
if (slot >= state.Assigned.Capacity) Normalize(ref state);
if (state.IsAssigned(slot)) return; // was already fully assigned.
@@ -1295,7 +1295,7 @@ private void SetSlotUnassigned(int slot, ref LocalState state)
{
if (slot < 0) return;
VariableIdentifier id = variableBySlot[slot];
- TypeSymbol type = VariableTypeWithAnnotations(id.Symbol).Type;
+ TypeSymbol type = id.Symbol.GetTypeOrReturnType().Type;
Debug.Assert(!_emptyStructTypeCache.IsEmptyStructType(type));
if (!state.IsAssigned(slot)) return; // was already unassigned
state.Unassign(slot);
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/LocalDataFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/LocalDataFlowPass.cs
index f1b01a399cd3..9d0e1b5461e9 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/LocalDataFlowPass.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/LocalDataFlowPass.cs
@@ -103,7 +103,7 @@ protected virtual int GetOrCreateSlot(Symbol symbol, int containingSlot = 0)
// Since analysis may proceed in multiple passes, it is possible the slot is already assigned.
if (!_variableSlot.TryGetValue(identifier, out slot))
{
- var variableType = VariableTypeWithAnnotations(symbol).Type;
+ var variableType = symbol.GetTypeOrReturnType().Type;
if (_emptyStructTypeCache.IsEmptyStructType(variableType))
{
return -1;
@@ -244,27 +244,5 @@ protected int MakeMemberSlot(BoundExpression receiverOpt, Symbol member)
}
return GetOrCreateSlot(member, containingSlot);
}
-
- protected static TypeWithAnnotations VariableTypeWithAnnotations(Symbol s)
- {
- switch (s.Kind)
- {
- case SymbolKind.Local:
- return ((LocalSymbol)s).TypeWithAnnotations;
- case SymbolKind.Field:
- return ((FieldSymbol)s).TypeWithAnnotations;
- case SymbolKind.Parameter:
- return ((ParameterSymbol)s).TypeWithAnnotations;
- case SymbolKind.Method:
- Debug.Assert(((MethodSymbol)s).MethodKind == MethodKind.LocalFunction);
- return default;
- case SymbolKind.Property:
- return ((PropertySymbol)s).TypeWithAnnotations;
- case SymbolKind.Event:
- return ((EventSymbol)s).TypeWithAnnotations;
- default:
- throw ExceptionUtilities.UnexpectedValue(s.Kind);
- }
- }
}
}
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.ObjectCreationPlaceholderLocal.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.ObjectCreationPlaceholderLocal.cs
deleted file mode 100644
index 47f58c15490f..000000000000
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.ObjectCreationPlaceholderLocal.cs
+++ /dev/null
@@ -1,168 +0,0 @@
-// 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;
-using System.Collections.Immutable;
-using Microsoft.CodeAnalysis.CSharp.Symbols;
-using Roslyn.Utilities;
-
-namespace Microsoft.CodeAnalysis.CSharp
-{
- internal partial class NullableWalker
- {
- ///
- /// A symbol to represent a placeholder for an instance being constructed by
- /// . It is used to track the state
- /// of members being initialized.
- ///
- private sealed class ObjectCreationPlaceholderLocal : LocalSymbol
- {
- private readonly Symbol _containingSymbol;
- private readonly TypeWithAnnotations _type;
- private readonly BoundExpression _objectCreationExpression;
-
- public ObjectCreationPlaceholderLocal(Symbol containingSymbol, BoundExpression objectCreationExpression)
- {
- _containingSymbol = containingSymbol;
- _type = TypeWithAnnotations.Create(objectCreationExpression.Type, NullableAnnotation.NotAnnotated);
- _objectCreationExpression = objectCreationExpression;
- }
-
- public override bool Equals(object obj)
- {
- if ((object)this == obj)
- {
- return true;
- }
-
- var other = obj as ObjectCreationPlaceholderLocal;
-
- return (object)other != null && (object)_objectCreationExpression == other._objectCreationExpression;
- }
-
- public override int GetHashCode()
- {
- return _objectCreationExpression.GetHashCode();
- }
-
- internal override SyntaxNode ScopeDesignatorOpt
- {
- get
- {
- return null;
- }
- }
-
- public override Symbol ContainingSymbol
- {
- get
- {
- return _containingSymbol;
- }
- }
-
- public override ImmutableArray DeclaringSyntaxReferences
- {
- get
- {
- return ImmutableArray.Empty;
- }
- }
-
- public override ImmutableArray Locations
- {
- get
- {
- return ImmutableArray.Empty;
- }
- }
-
- public override TypeWithAnnotations TypeWithAnnotations
- {
- get
- {
- return _type;
- }
- }
-
- internal override LocalDeclarationKind DeclarationKind
- {
- get
- {
- return LocalDeclarationKind.None;
- }
- }
-
- internal override SyntaxToken IdentifierToken
- {
- get
- {
- throw ExceptionUtilities.Unreachable;
- }
- }
-
- internal override bool IsCompilerGenerated
- {
- get
- {
- return true;
- }
- }
-
- internal override bool IsImportedFromMetadata
- {
- get
- {
- return false;
- }
- }
-
- internal override bool IsPinned
- {
- get
- {
- return false;
- }
- }
-
- public override RefKind RefKind
- {
- get
- {
- return RefKind.None;
- }
- }
-
- internal override SynthesizedLocalKind SynthesizedKind
- {
- get
- {
- throw ExceptionUtilities.Unreachable;
- }
- }
-
- internal override ConstantValue GetConstantValue(SyntaxNode node, LocalSymbol inProgress, DiagnosticBag diagnostics = null)
- {
- return null;
- }
-
- internal override ImmutableArray GetConstantValueDiagnostics(BoundExpression boundInitValue)
- {
- return ImmutableArray.Empty;
- }
-
- internal override SyntaxNode GetDeclaratorSyntax()
- {
- throw ExceptionUtilities.Unreachable;
- }
-
- internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax)
- {
- throw ExceptionUtilities.Unreachable;
- }
-
- internal override uint ValEscapeScope => throw ExceptionUtilities.Unreachable;
-
- internal override uint RefEscapeScope => throw ExceptionUtilities.Unreachable;
- }
- }
-}
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs
new file mode 100644
index 000000000000..08ebf088a1fa
--- /dev/null
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs
@@ -0,0 +1,64 @@
+// 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 System.Diagnostics;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis.CSharp
+{
+ internal partial class NullableWalker
+ {
+ ///
+ /// A symbol to be used as a placeholder for an instance being constructed by
+ /// , or the input expression of a pattern-matching operation.
+ /// It is used to track the state of an expression, such as members being initialized.
+ ///
+ private sealed class PlaceholderLocal : LocalSymbol
+ {
+ private readonly Symbol _containingSymbol;
+ private readonly TypeWithAnnotations _type;
+ private readonly object _identifier;
+
+ public PlaceholderLocal(Symbol containingSymbol, object identifier, TypeWithAnnotations type)
+ {
+ Debug.Assert(identifier != null);
+ _containingSymbol = containingSymbol;
+ _type = type;
+ _identifier = identifier;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if ((object)this == obj)
+ {
+ return true;
+ }
+
+ var other = obj as PlaceholderLocal;
+
+ return (object)other != null && _identifier.Equals(other._identifier);
+ }
+
+ public override int GetHashCode() => _identifier.GetHashCode();
+ internal override SyntaxNode ScopeDesignatorOpt => null;
+ public override Symbol ContainingSymbol => _containingSymbol;
+ public override ImmutableArray DeclaringSyntaxReferences => ImmutableArray.Empty;
+ public override ImmutableArray Locations => ImmutableArray.Empty;
+ public override TypeWithAnnotations TypeWithAnnotations => _type;
+ internal override LocalDeclarationKind DeclarationKind => LocalDeclarationKind.None;
+ internal override SyntaxToken IdentifierToken => throw ExceptionUtilities.Unreachable;
+ internal override bool IsCompilerGenerated => true;
+ internal override bool IsImportedFromMetadata => false;
+ internal override bool IsPinned => false;
+ public override RefKind RefKind => RefKind.None;
+ internal override SynthesizedLocalKind SynthesizedKind => throw ExceptionUtilities.Unreachable;
+ internal override ConstantValue GetConstantValue(SyntaxNode node, LocalSymbol inProgress, DiagnosticBag diagnostics = null) => null;
+ internal override ImmutableArray GetConstantValueDiagnostics(BoundExpression boundInitValue) => ImmutableArray.Empty;
+ internal override SyntaxNode GetDeclaratorSyntax() => throw ExceptionUtilities.Unreachable;
+ internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax) => throw ExceptionUtilities.Unreachable;
+ internal override uint ValEscapeScope => throw ExceptionUtilities.Unreachable;
+ internal override uint RefEscapeScope => throw ExceptionUtilities.Unreachable;
+ }
+ }
+}
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
index 7d8434efb9f7..ac6ee08e0273 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
@@ -174,9 +174,9 @@ private void SetResult(TypeWithState rvalueType, TypeWithAnnotations lvalueType)
}
///
- /// Instances being constructed.
+ /// Placeholder locals, e.g. for objects being constructed.
///
- private PooledDictionary _placeholderLocalsOpt;
+ private PooledDictionary