diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
index 92efec57d2812b3c46a592754e8d34b8b7c1550c..3c1c09609feba3a2cf94d740cf3766520e92c082 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
@@ -79,7 +79,7 @@ internal sealed class VariableState
///
/// Types from return expressions. Used when inferring lambda return type in MethodTypeInferrer.
///
- private readonly ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> _returnTypes;
+ private readonly ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> _returnTypesOpt;
///
/// An optional callback for callers to receive notification of the inferred type and nullability
@@ -100,7 +100,7 @@ internal sealed class VariableState
///
/// Instances being constructed.
///
- private PooledDictionary _placeholderLocals;
+ private PooledDictionary _placeholderLocalsOpt;
///
/// For methods with annotations, we'll need to visit the arguments twice.
@@ -117,7 +117,7 @@ internal sealed class VariableState
protected override void Free()
{
_variableTypes.Free();
- _placeholderLocals?.Free();
+ _placeholderLocalsOpt?.Free();
base.Free();
}
@@ -128,7 +128,7 @@ protected override void Free()
bool useMethodSignatureParameterTypes,
MethodSymbol methodSignatureOpt,
BoundNode node,
- ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> returnTypes,
+ ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> returnTypesOpt,
VariableState initialState,
Action callbackOpt)
: base(compilation, method, node, new EmptyStructTypeCache(compilation, dev12CompilerCompatibility: false), trackUnassignments: true)
@@ -140,7 +140,7 @@ protected override void Free()
_useMethodSignatureReturnType = (object)methodSignatureOpt != null && useMethodSignatureReturnType;
_useMethodSignatureParameterTypes = (object)methodSignatureOpt != null && useMethodSignatureParameterTypes;
_methodSignatureOpt = methodSignatureOpt;
- _returnTypes = returnTypes;
+ _returnTypesOpt = returnTypesOpt;
if (initialState != null)
{
var variableBySlot = initialState.VariableBySlot;
@@ -168,9 +168,9 @@ protected override bool ConvertInsufficientExecutionStackExceptionToCancelledByS
protected override ImmutableArray Scan(ref bool badRegion)
{
- if (_returnTypes != null)
+ if (_returnTypesOpt != null)
{
- _returnTypes.Clear();
+ _returnTypesOpt.Clear();
}
this.Diagnostics.Clear();
ParameterSymbol methodThisParameter = MethodThisParameter;
@@ -439,7 +439,7 @@ protected override int MakeSlot(BoundExpression node)
case BoundKind.ObjectCreationExpression:
case BoundKind.DynamicObjectCreationExpression:
case BoundKind.AnonymousObjectCreationExpression:
- if (_placeholderLocals != null && _placeholderLocals.TryGetValue(node, out ObjectCreationPlaceholderLocal placeholder))
+ if (_placeholderLocalsOpt != null && _placeholderLocalsOpt.TryGetValue(node, out ObjectCreationPlaceholderLocal placeholder))
{
return GetOrCreateSlot(placeholder);
}
@@ -534,6 +534,8 @@ private enum AssignmentKind
///
private bool ReportNullableAssignmentIfNecessary(BoundExpression value, TypeSymbolWithAnnotations targetType, TypeSymbolWithAnnotations valueType, bool useLegacyWarnings, AssignmentKind assignmentKind = AssignmentKind.Assignment, Symbol target = null)
{
+ Debug.Assert((object)target != null || assignmentKind != AssignmentKind.Argument);
+
if (value == null)
{
return false;
@@ -554,7 +556,7 @@ private bool ReportNullableAssignmentIfNecessary(BoundExpression value, TypeSymb
return false;
}
- if (reportNullLiteralAssignmentIfNecessary())
+ if (reportNullLiteralAssignmentIfNecessary(value))
{
return true;
}
@@ -583,20 +585,20 @@ private bool ReportNullableAssignmentIfNecessary(BoundExpression value, TypeSymb
// Report warning converting null literal to non-nullable reference type.
// target (e.g.: `object x = null;` or calling `void F(object y)` with `F(null)`).
- bool reportNullLiteralAssignmentIfNecessary()
+ bool reportNullLiteralAssignmentIfNecessary(BoundExpression expr)
{
- if (value.ConstantValue?.IsNull != true && !isDefaultOfUnconstrainedTypeParameter(value))
+ if (expr.ConstantValue?.IsNull != true && !isDefaultOfUnconstrainedTypeParameter(expr))
{
return false;
}
if (useLegacyWarnings)
{
- ReportWWarning(value.Syntax);
+ ReportWWarning(expr.Syntax);
}
else
{
- ReportDiagnostic(assignmentKind == AssignmentKind.Return ? ErrorCode.WRN_NullReferenceReturn : ErrorCode.WRN_NullAsNonNullable, value.Syntax);
+ ReportDiagnostic(assignmentKind == AssignmentKind.Return ? ErrorCode.WRN_NullReferenceReturn : ErrorCode.WRN_NullAsNonNullable, expr.Syntax);
}
return true;
}
@@ -1019,11 +1021,11 @@ protected override BoundNode VisitReturnStatementNoAdjust(BoundReturnStatement n
return null;
}
- if (_returnTypes != null)
+ if (_returnTypesOpt != null)
{
// Inferring return type. Should not convert to method return type.
TypeSymbolWithAnnotations result = VisitRvalueWithResult(expr);
- _returnTypes.Add((node.RefKind, result));
+ _returnTypesOpt.Add((node.RefKind, result));
return null;
}
@@ -1256,20 +1258,20 @@ private void SetResult(BoundExpression node)
private ObjectCreationPlaceholderLocal GetOrCreateObjectCreationPlaceholder(BoundExpression node)
{
ObjectCreationPlaceholderLocal placeholder;
- if (_placeholderLocals == null)
+ if (_placeholderLocalsOpt == null)
{
- _placeholderLocals = PooledDictionary.GetInstance();
+ _placeholderLocalsOpt = PooledDictionary.GetInstance();
placeholder = null;
}
else
{
- _placeholderLocals.TryGetValue(node, out placeholder);
+ _placeholderLocalsOpt.TryGetValue(node, out placeholder);
}
if ((object)placeholder == null)
{
placeholder = new ObjectCreationPlaceholderLocal(_symbol, node);
- _placeholderLocals.Add(node, placeholder);
+ _placeholderLocalsOpt.Add(node, placeholder);
}
return placeholder;
@@ -3055,10 +3057,7 @@ private static Symbol AsMemberOfResultType(TypeSymbolWithAnnotations resultType,
private static Symbol AsMemberOfType(NamedTypeSymbol containingType, Symbol symbol)
{
- if (symbol is null)
- {
- return null;
- }
+ Debug.Assert((object)symbol != null);
if (symbol.Kind == SymbolKind.Method)
{
if (((MethodSymbol)symbol).MethodKind == MethodKind.LocalFunction)
@@ -3330,6 +3329,7 @@ private bool HasTopLevelNullabilityConversion(TypeSymbolWithAnnotations source,
Debug.Assert(node != null);
Debug.Assert(operandOpt != null || !operandType.IsNull);
Debug.Assert(!targetTypeWithNullability.IsNull);
+ Debug.Assert((object)target != null || assignmentKind != AssignmentKind.Argument);
NullableAnnotation resultAnnotation = NullableAnnotation.Unknown;
bool forceOperandAnnotationForResult = false;
@@ -3385,10 +3385,11 @@ private bool HasTopLevelNullabilityConversion(TypeSymbolWithAnnotations source,
conversion.UserDefinedFromConversion,
TypeSymbolWithAnnotations.Create(conversion.BestUserDefinedConversionAnalysis.FromType),
operandType,
- checkConversion: false,
+ checkConversion: true,
fromExplicitCast: false,
useLegacyWarnings,
- assignmentKind);
+ assignmentKind,
+ target);
// Update method based on operandType: see https://github.com/dotnet/roslyn/issues/29605.
// (see NullableReferenceTypesTests.ImplicitConversions_07).
@@ -3665,6 +3666,7 @@ private bool HasTopLevelNullabilityConversion(TypeSymbolWithAnnotations source,
private TypeSymbolWithAnnotations ClassifyAndApplyConversion(BoundExpression node, TypeSymbolWithAnnotations targetType, TypeSymbolWithAnnotations operandType, bool useLegacyWarnings, AssignmentKind assignmentKind, ParameterSymbol target)
{
+ Debug.Assert((object)target != null || assignmentKind != AssignmentKind.Argument);
HashSet useSiteDiagnostics = null;
var conversion = _conversions.ClassifyStandardConversion(null, operandType.TypeSymbol, targetType.TypeSymbol, ref useSiteDiagnostics);
if (!conversion.Exists)
@@ -4728,7 +4730,7 @@ public override BoundNode VisitEventAssignmentOperator(BoundEventAssignmentOpera
Debug.Assert(!IsConditionalState);
var receiverOpt = node.ReceiverOpt;
var @event = node.Event;
- if (!node.Event.IsStatic)
+ if (!@event.IsStatic)
{
@event = (EventSymbol)AsMemberOfResultType(_resultType, @event);
// https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null
diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
index dd379807813a96f331c01562da8d9f1f104e3103..acd07fb1467c4c8c2280f1ba41a527a510faf509 100644
--- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
+++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
@@ -65747,5 +65747,66 @@ static void F3(int? z, int? w)
var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics();
}
+
+ [WorkItem(31770, "https://github.com/dotnet/roslyn/issues/31770")]
+ [Fact]
+ public void UserDefinedConversion_NestedNullability_01()
+ {
+ var source =
+@"class A { }
+class B
+{
+ public static implicit operator B(A