diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index fd61f68cd2ae926e490918147f3dd5bf3f207878..0d48f18d0d45e9189c46468592e5ad6d886e6297 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -130,6 +130,14 @@ private TypeWithState ResultType } } + /// + /// Force the inference of the LValueResultType from ResultType. + /// + private void UseRvalueOnly() + { + ResultType = ResultType; + } + private TypeSymbolWithAnnotations LvalueResultType { get => _visitResult.LValueType; @@ -139,6 +147,14 @@ private TypeSymbolWithAnnotations LvalueResultType } } + /// + /// Force the inference of the ResultType from LValueResultType. + /// + private void UseLvalueOnly() + { + LvalueResultType = LvalueResultType; + } + private void SetResult(TypeWithState resultType, TypeSymbolWithAnnotations lvalueType) { _visitResult = new VisitResult(resultType, lvalueType); @@ -455,7 +471,7 @@ private Symbol GetBackingFieldIfStructProperty(Symbol symbol) protected override int GetOrCreateSlot(Symbol symbol, int containingSlot = 0) { symbol = GetBackingFieldIfStructProperty(symbol); - if ((object)symbol == null) + if (symbol is null) { return -1; } @@ -605,7 +621,7 @@ protected override void VisitRvalue(BoundExpression node) { Visit(node); Unsplit(); - ResultType = ResultType; // drop lvalue part + UseRvalueOnly(); // drop lvalue part } private TypeWithState VisitRvalueWithState(BoundExpression node) @@ -1434,7 +1450,7 @@ private int GetOrCreateObjectCreationPlaceholderSlot(BoundExpression node) _placeholderLocalsOpt.TryGetValue(node, out placeholder); } - if ((object)placeholder == null) + if (placeholder is null) { placeholder = new ObjectCreationPlaceholderLocal(_symbol, node); _placeholderLocalsOpt.Add(node, placeholder); @@ -2632,7 +2648,7 @@ private VisitResult VisitArgumentEvaluate(ImmutableArray argume { Visit(argument); // No Unsplit - ResultType = ResultType; // force use of flow result + UseRvalueOnly(); // force use of flow result } else { @@ -2645,7 +2661,7 @@ private VisitResult VisitArgumentEvaluate(ImmutableArray argume Visit(argument); // We'll want to use the l-value type, rather than the result type, for method re-inference - LvalueResultType = LvalueResultType; + UseLvalueOnly(); break; } @@ -3241,7 +3257,7 @@ private static Symbol AsMemberOfType(TypeSymbol type, Symbol symbol) Debug.Assert((object)symbol != null); var containingType = type as NamedTypeSymbol; - if ((object)containingType == null || containingType.IsErrorType() || symbol is ErrorMethodSymbol) + if (containingType is null || containingType.IsErrorType() || symbol is ErrorMethodSymbol) { return symbol; } @@ -3281,7 +3297,7 @@ private static Symbol AsMemberOfType(TypeSymbol type, Symbol symbol) return result; } containingType = containingType.BaseTypeNoUseSiteDiagnostics; - if ((object)containingType == null) + if (containingType is null) { break; } @@ -3430,7 +3446,7 @@ private void VisitTupleExpression(BoundTupleExpression node) { var arguments = node.Arguments; ImmutableArray elementTypes = arguments.SelectAsArray((a, w) => w.VisitRvalueWithState(a), this); - ImmutableArray elementTypesWithAnnotations = elementTypes.SelectAsArray((a, w) => a.ToTypeSymbolWithAnnotations(), this); + ImmutableArray elementTypesWithAnnotations = elementTypes.SelectAsArray(a => a.ToTypeSymbolWithAnnotations()); var tupleOpt = (TupleTypeSymbol)node.Type; if (tupleOpt is null) { @@ -4401,7 +4417,7 @@ public override BoundNode VisitIncrementOperator(BoundIncrementOperator node) } TypeWithState resultOfIncrementType; - if ((object)incrementOperator == null) + if (incrementOperator is null) { resultOfIncrementType = resultOfOperandConversionType; } @@ -4918,7 +4934,7 @@ protected override void AfterLeftChildOfBinaryLogicalOperatorHasBeenVisited(Boun throw ExceptionUtilities.UnexpectedValue(node.Kind); } - Debug.Assert((object)trueFalseOperator == null || ((object)logicalOperator != null && left != null)); + Debug.Assert(trueFalseOperator is null || ((object)logicalOperator != null && left != null)); if ((object)trueFalseOperator != null) { @@ -4961,7 +4977,7 @@ public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { var result = base.VisitAwaitExpression(node); CheckPossibleNullReceiver(node.Expression); - if (node.Type.IsValueType || node.HasErrors || (object)node.AwaitableInfo.GetResult == null) + if (node.Type.IsValueType || node.HasErrors || node.AwaitableInfo.GetResult is null) { SetNotNullResult(node); } @@ -5058,28 +5074,13 @@ public override BoundNode VisitAsOperator(BoundAsOperator node) if (type.CanContainNull()) { - var operandType = argumentType; switch (node.Conversion.Kind) { case ConversionKind.Identity: - // Inherit nullability from the operand - resultState = operandType.State; - break; - case ConversionKind.ImplicitReference: - // Inherit nullability from the operand - resultState = operandType.State; - break; - case ConversionKind.Boxing: - // Inherit nullability from the operand - resultState = operandType.State; - break; - case ConversionKind.ImplicitNullable: - // conversion of a value of type `X` to the type `Nullable` - // conversion of a value of type `int?` to the type `long?` - resultState = operandType.State; + resultState = argumentType.State; break; default: @@ -5114,7 +5115,7 @@ public override BoundNode VisitArgList(BoundArgList node) public override BoundNode VisitArgListOperator(BoundArgListOperator node) { VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt); - Debug.Assert((object)node.Type == null); + Debug.Assert(node.Type is null); SetNotNullResult(node); return null; } @@ -5293,7 +5294,7 @@ private void SetUnknownResultNullability() public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreation node) { var result = base.VisitStackAllocArrayCreation(node); - Debug.Assert((object)node.Type == null || node.Type.IsPointerType() || node.Type.IsRefLikeType); + Debug.Assert(node.Type is null || node.Type.IsPointerType() || node.Type.IsRefLikeType); SetNotNullResult(node); return result; } @@ -5313,8 +5314,7 @@ public override BoundNode VisitDynamicIndexerAccess(BoundDynamicIndexerAccess no NullableAnnotation nullableAnnotation = (object)node.Type != null && !node.Type.IsValueType ? InferResultNullabilityFromApplicableCandidates(StaticCast.From(node.ApplicableIndexers)) : NullableAnnotation.Unknown; - var result = TypeSymbolWithAnnotations.Create(node.Type, nullableAnnotation); - LvalueResultType = result; + LvalueResultType = TypeSymbolWithAnnotations.Create(node.Type, nullableAnnotation); return null; } @@ -5323,18 +5323,18 @@ private void CheckPossibleNullReceiver(BoundExpression receiverOpt, bool checkNu Debug.Assert(!this.IsConditionalState); if (receiverOpt != null && this.State.Reachable) { - var resultType = ResultType.Type; - if (resultType is null) + var resultTypeSymbol = ResultType.Type; + if (resultTypeSymbol is null) { return; } #if DEBUG - Debug.Assert(receiverOpt.Type is null || AreCloseEnough(receiverOpt.Type, resultType)); + Debug.Assert(receiverOpt.Type is null || AreCloseEnough(receiverOpt.Type, resultTypeSymbol)); #endif if (ResultType.MaybeNull) { - bool isValueType = resultType.IsValueType; - if (isValueType && (!checkNullableValueType || !resultType.IsNullableTypeOrTypeParameter() || resultType.GetNullableUnderlyingType().IsErrorType())) + bool isValueType = resultTypeSymbol.IsValueType; + if (isValueType && (!checkNullableValueType || !resultTypeSymbol.IsNullableTypeOrTypeParameter() || resultTypeSymbol.GetNullableUnderlyingType().IsErrorType())) { return; } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 9468a3ab72a256021289514937b89ece509f5cbe..0dee767e9823008933dbd6a7edef6b9ca273be17 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -38351,6 +38351,9 @@ static void F1(T1 t1) }"; var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); comp.VerifyDiagnostics( + // (5,9): warning CS8653: A default expression introduces a null value when 'T1' is a non-nullable reference type. + // default(T1).ToString(); // 1 + Diagnostic(ErrorCode.WRN_DefaultExpressionMayIntroduceNullT, "default(T1)").WithArguments("T1").WithLocation(5, 9), // (5,9): warning CS8602: Possible dereference of a null reference. // default(T1).ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "default(T1)").WithLocation(5, 9), @@ -65919,7 +65922,7 @@ class Outer void M0(T x0, T y0) { if (y0 == null) return; - (x0 ?? y0)?.ToString(); + (x0 ?? y0).ToString(); } } "; @@ -77949,8 +77952,9 @@ public void BestType_DifferentTupleNullability_10() comp.VerifyTypes(); } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/33344")] + [Fact] [WorkItem(32575, "https://github.com/dotnet/roslyn/issues/32575")] + [WorkItem(33344, "https://github.com/dotnet/roslyn/issues/33344")] public void BestType_DifferentTupleNullability_11() { var source = @@ -77959,23 +77963,19 @@ public void BestType_DifferentTupleNullability_11() static void F(T t, U u) where U : class { - var x = new[] { (t, u), default }[0]/*T:(T t, U? u)*/; + var x = new[] { (t, u), default }[0]/*T:(T t, U u)*/; // should be (T t, U? u) x.Item1.ToString(); // 1 x.Item2.ToString(); // 2 - var y = new[] { default, (t, u) }[0]/*T:(T t, U? u)*/; + var y = new[] { default, (t, u) }[0]/*T:(T t, U u)*/; // should be (T t, U? u) y.Item1.ToString(); // 3 y.Item2.ToString(); // 4 } }"; var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); - // https://github.com/dotnet/roslyn/issues/32575: Not handling default for U. comp.VerifyDiagnostics( - // (7,9): warning CS8602: Possible dereference of a null reference. - // x.Item1.ToString(); // 1 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x.Item1").WithLocation(7, 9), - // (10,9): warning CS8602: Possible dereference of a null reference. - // y.Item1.ToString(); // 3 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y.Item1").WithLocation(10, 9)); + // https://github.com/dotnet/roslyn/issues/32575: Not handling default for U. + // SHOULD BE 4 diagnostics. + ); comp.VerifyTypes(); }