diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 624f7eaa2d0e2ba4dd234dfcaef67efdc7effe56..28095859578bb3e784862ec11377a380b0678829 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -981,9 +981,9 @@ private BoundExpression BindRefValue(RefValueExpressionSyntax node, DiagnosticBa argument = CreateConversion(argument, conversion, typedReferenceType, diagnostics); - TypeSymbol type = BindType(node.Type, diagnostics).Type; + TypeWithAnnotations typeWithAnnotations = BindType(node.Type, diagnostics); - return new BoundRefValueOperator(node, argument, type, hasErrors); + return new BoundRefValueOperator(node, typeWithAnnotations.NullableAnnotation, argument, typeWithAnnotations.Type, hasErrors); } private BoundExpression BindMakeRef(MakeRefExpressionSyntax node, DiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 0b9cb5d54b3707457b92045236ddb7c4f2fa676b..931bf35481fd51685c1152677979e50ff695797c 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -329,6 +329,7 @@ + diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 14043538940cde60e07c5967fc214409186e459b..e79c637f4006da119994acaebee3c7047227e313 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -5157,7 +5157,7 @@ public override BoundNode VisitMakeRefOperator(BoundMakeRefOperator node) public override BoundNode VisitRefValueOperator(BoundRefValueOperator node) { var result = base.VisitRefValueOperator(node); - var type = TypeWithAnnotations.Create(node.Type); + var type = TypeWithAnnotations.Create(node.Type, node.NullableAnnotation); LvalueResultType = type; return result; } diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 69f8447c2de8fadec56a6f15327221b56285c9fd..a38952999e6749a23e0d5b606f85f18164a51f24 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -1237,17 +1237,20 @@ protected override BoundExpression ShallowClone() internal sealed partial class BoundRefValueOperator : BoundExpression { - public BoundRefValueOperator(SyntaxNode syntax, BoundExpression operand, TypeSymbol type, bool hasErrors = false) + public BoundRefValueOperator(SyntaxNode syntax, NullableAnnotation nullableAnnotation, BoundExpression operand, TypeSymbol type, bool hasErrors = false) : base(BoundKind.RefValueOperator, syntax, type, hasErrors || operand.HasErrors()) { Debug.Assert((object)operand != null, "Field 'operand' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); Debug.Assert((object)type != null, "Field 'type' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + this.NullableAnnotation = nullableAnnotation; this.Operand = operand; } + public NullableAnnotation NullableAnnotation { get; } + public BoundExpression Operand { get; } public override BoundNode Accept(BoundTreeVisitor visitor) @@ -1255,11 +1258,11 @@ public override BoundNode Accept(BoundTreeVisitor visitor) return visitor.VisitRefValueOperator(this); } - public BoundRefValueOperator Update(BoundExpression operand, TypeSymbol type) + public BoundRefValueOperator Update(NullableAnnotation nullableAnnotation, BoundExpression operand, TypeSymbol type) { - if (operand != this.Operand || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (nullableAnnotation != this.NullableAnnotation || operand != this.Operand || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundRefValueOperator(this.Syntax, operand, type, this.HasErrors); + var result = new BoundRefValueOperator(this.Syntax, nullableAnnotation, operand, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -1268,7 +1271,7 @@ public BoundRefValueOperator Update(BoundExpression operand, TypeSymbol type) protected override BoundExpression ShallowClone() { - var result = new BoundRefValueOperator(this.Syntax, this.Operand, this.Type, this.HasErrors); + var result = new BoundRefValueOperator(this.Syntax, this.NullableAnnotation, this.Operand, this.Type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -11164,7 +11167,7 @@ public override BoundNode VisitRefValueOperator(BoundRefValueOperator node) { BoundExpression operand = (BoundExpression)this.Visit(node.Operand); TypeSymbol type = this.VisitType(node.Type); - return node.Update(operand, type); + return node.Update(node.NullableAnnotation, operand, type); } public override BoundNode VisitFromEndIndexExpression(BoundFromEndIndexExpression node) { @@ -12366,6 +12369,7 @@ public override TreeDumperNode VisitRefValueOperator(BoundRefValueOperator node, { return new TreeDumperNode("refValueOperator", null, new TreeDumperNode[] { + new TreeDumperNode("nullableAnnotation", node.NullableAnnotation, null), new TreeDumperNode("operand", null, new TreeDumperNode[] { Visit(node.Operand, null) }), new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index be307b87d47533748239ac5e3ee1bfa4eb968771..ae4c6552a3ad5012ecb15cd9ef11abcac20d5d42 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -85835,5 +85835,28 @@ public struct Foo var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); comp.VerifyDiagnostics(); } + + [Fact, WorkItem(32446, "https://github.com/dotnet/roslyn/issues/32446")] + public void RefValueOfNullableType() + { + var source = +@" +using System; +public class C +{ + public void M(TypedReference r) + { + _ = __refvalue(r, string?).Length; // 1 + _ = __refvalue(r, string).Length; + } +} +"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (7,13): warning CS8602: Possible dereference of a null reference. + // _ = __refvalue(r, string?).Length; // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "__refvalue(r, string?)").WithLocation(7, 13) + ); + } } }