Skip to content

Commit

Permalink
Fix ternary expression with T : T?
Browse files Browse the repository at this point in the history
We allow non-standard T? -> T implicit conversion, which leads to
ambiguity when T : T? are in conditional expression.
Special case for this has been added.
  • Loading branch information
exyi committed Nov 29, 2023
1 parent ba34ac9 commit 046206c
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ protected override Expression VisitConditionalExpression(ConditionalExpressionBi
Debug.Assert(trueConverted.Type != falseConverted.Type);
// 1. We represent some "typeless expressions" as expression of type object
// 2. We allow conversion to string and also have the implicit conversion from string literal to enum
// 3. For some reason, we allow T? -> T implicit conversion as well as T -> T?, this will prefer the nullable type
// -> if we have an ambiguity, try to solve it by preferring the more specific type

if (trueConverted.Type == typeof(object))
Expand All @@ -335,6 +336,13 @@ protected override Expression VisitConditionalExpression(ConditionalExpressionBi
falseExpr = falseConverted;
else if (falseConverted.Type == typeof(string))
trueExpr = trueConverted;
else if (trueConverted.Type.UnwrapNullableType() == falseConverted.Type.UnwrapNullableType())
{
if (trueConverted.Type.IsNullable())
falseExpr = falseConverted;
else
trueExpr = trueConverted;
}
else
throw new BindingCompilationException($"Type of conditional expression '{node.ToDisplayString()}' cannot be determined because because '{trueExpr.Type.ToCode()}' and '{falseExpr.Type.ToCode()}' implicitly convert to one another", node);
}
Expand Down
14 changes: 13 additions & 1 deletion src/Tests/Binding/BindingCompilationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -842,8 +842,20 @@ public void BindingCompiler_ImplicitConversion_ConditionalNullableAndNonNullable
Assert.AreEqual(11.1, resultNotNull);
resultNotNull = ExecuteBinding("!_this.BoolProp ? _this.NullableDoubleProp : _this.DoubleProp", new[] { new TestViewModel { NullableDoubleProp = 11.1, DoubleProp = 22.2, BoolProp = true } }, expectedType: typeof(double?));
Assert.AreEqual(22.2, resultNotNull);
var resultNull = ExecuteBinding("_this.BoolProp ? null : _this.DoubleProp", new[] { new TestViewModel { NullableDoubleProp = 11.1, DoubleProp = 22.2, BoolProp = true } }, expectedType: typeof(double?));
var resultNull = ExecuteBinding("_this.BoolProp ? null : _this.DoubleProp", new[] { new TestViewModel { NullableDoubleProp = 11.1, DoubleProp = 22.2, BoolProp = true } }, expectedType: typeof(object));
Assert.IsNull(resultNull);

}

[TestMethod]
public void BindingCompiler_ImplicitConversion_ConditionalNullableAndNonNullable_IntDouble()
{
var resultNotNull = ExecuteBinding("_this.BoolProp ? _this.NullableDoubleProp : _this.IntProp", new[] { new TestViewModel { NullableDoubleProp = 11.1, IntProp = 1, BoolProp = true } });
Assert.AreEqual(11.1, resultNotNull);
resultNotNull = ExecuteBinding("!_this.BoolProp ? _this.NullableDoubleProp : _this.IntProp", new[] { new TestViewModel { NullableDoubleProp = 11.1, IntProp = 1, BoolProp = true } }, expectedType: typeof(double?));
Assert.AreEqual(1.0, resultNotNull);
resultNotNull = ExecuteBinding("_this.BoolProp ? _this.NullableIntProp : _this.DoubleProp", new[] { new TestViewModel { DoubleProp = 11.1, NullableIntProp = 1, BoolProp = true } }, expectedType: typeof(double?));
Assert.AreEqual(1.0, resultNotNull);
}

[TestMethod]
Expand Down

0 comments on commit 046206c

Please sign in to comment.