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

Add nullability info field into discard expression #66309

Merged
merged 10 commits into from
Feb 23, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ private BoundDiscardExpression BindDiscardExpression(
SyntaxNode syntax,
TypeWithAnnotations declTypeWithAnnotations)
{
return new BoundDiscardExpression(syntax, declTypeWithAnnotations.Type);
return new BoundDiscardExpression(syntax, declTypeWithAnnotations.NullableAnnotation, declTypeWithAnnotations.Type);
}

/// <summary>
Expand Down
7 changes: 4 additions & 3 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1589,7 +1589,7 @@ private BoundExpression BindIdentifier(
}
else if (FallBackOnDiscard(identifier, diagnostics))
{
expression = new BoundDiscardExpression(node, type: null);
expression = new BoundDiscardExpression(node, NullableAnnotation.Oblivious, type: null);
}
}

Expand Down Expand Up @@ -2825,7 +2825,7 @@ private BoundExpression BindOutDeclarationArgument(DeclarationExpressionSyntax d
var declType = BindVariableTypeWithAnnotations(designation, diagnostics, typeSyntax, ref isConst, out isVar, out alias);
Debug.Assert(isVar != declType.HasType);

return new BoundDiscardExpression(declarationExpression, declType.Type);
return new BoundDiscardExpression(declarationExpression, declType.NullableAnnotation, declType.Type);
}
case SyntaxKind.SingleVariableDesignation:
return BindOutVariableDeclarationArgument(declarationExpression, diagnostics);
Expand Down Expand Up @@ -3113,7 +3113,8 @@ private void CoerceArguments<TMember>(
{
TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg);
Debug.Assert(parameterTypeWithAnnotations.HasType);
arguments[arg] = ((BoundDiscardExpression)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations);
// Discard expression should be nullable annoted since we don't care about the warnings here
arguments[arg] = ((BoundDiscardExpression)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations.AsAnnotated());
}
else if (argument.NeedsToBeConverted())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal partial class BoundDiscardExpression
public BoundExpression SetInferredTypeWithAnnotations(TypeWithAnnotations type)
{
Debug.Assert(Type is null && type.HasType);
return this.Update(type.Type);
return this.Update(type.NullableAnnotation, type.Type);
}

public BoundDiscardExpression FailInference(Binder binder, BindingDiagnosticBag? diagnosticsOpt)
Expand All @@ -21,7 +21,7 @@ public BoundDiscardExpression FailInference(Binder binder, BindingDiagnosticBag?
{
Binder.Error(diagnosticsOpt, ErrorCode.ERR_DiscardTypeInferenceFailed, this.Syntax);
}
return this.Update(binder.CreateErrorType("var"));
return this.Update(NullableAnnotation.Oblivious, binder.CreateErrorType("var"));
}

public override Symbol ExpressionSymbol
Expand Down
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2359,6 +2359,7 @@
is null before type inference, and it is replaced by a BoundDiscardExpression with a non-null
type after inference. -->
<Field Name="Type" Type="TypeSymbol?" Override="true"/>
<Field Name="NullableAnnotation" Type="NullableAnnotation"/>
</Node>

<Node Name="BoundThrowExpression" Base="BoundExpression">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10995,7 +10995,7 @@ protected override void VisitInterpolatedStringHandlerConstructor(BoundExpressio

public override BoundNode? VisitDiscardExpression(BoundDiscardExpression node)
{
var result = TypeWithAnnotations.Create(node.Type);
var result = TypeWithAnnotations.Create(node.Type, node.NullableAnnotation);
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
var rValueType = TypeWithState.ForType(node.Type);
SetResult(node, rValueType, result);
return null;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,64 @@ public static int Goo<T, U>(T t, U u)
Diagnostic(ErrorCode.ERR_BadArity, "Goo<>").WithArguments("Program.Goo<T, U>(T, U)", "method", "2"));
}

[Fact]
public void Bug50782()
{
string source = @"
using System.Diagnostics.CodeAnalysis;
#nullable enable
interface IOperation<T> { }
class StringOperation : IOperation<string?> { }
class C {
void M() {
TestA(new StringOperation(), out string? discardA);
TestA(new StringOperation(), out string? _);
TestA(new StringOperation(), out var _);
TestA(new StringOperation(), out _);

TestB(new StringOperation(), out string? discardB);
TestB(new StringOperation(), out string? _);
TestB(new StringOperation(), out var _);
TestB(new StringOperation(), out _);

TestC<string?>(out string? discardC);
TestC<string?>(out string? _);
TestC<string?>(out var _);
TestC<string?>(out _);

TestD<string?>(out string? discardD);
TestD<string?>(out string? _);
TestD<string?>(out var _);
TestD<string?>(out _);

TestE(out string? discardE);
TestE(out string? _);
TestE(out var _);
TestE(out _);

TestF(out string? discardF);
TestF(out string? _);
TestF(out var _);
TestF(out _);
}

void TestA<T>(IOperation<T> operation, [MaybeNull] out T result) => result = default;
void TestB<T>(IOperation<T> operation, out T result) => result = default;

void TestC<T>([MaybeNull] out T result) => result = default;
void TestD<T>(out T result) => result = default;

void TestE([MaybeNull] out string result) => result = default;
void TestF(out string result) => result = default;
}
";
CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(new[] {
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(40, 70), // We assume that T will be non-nullable.
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(43, 45), // We assume that T will be non-nullable.
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "default").WithLocation(46, 47) // We assume that T will be non-nullable.
});
}
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved

[WorkItem(541887, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541887")]
[Fact]
public void Bug8785_2()
Expand Down