Skip to content

Commit 3cae7a8

Browse files
authored
Add missing Converts when simplifying in funcletizer (#35122)
Fixes #35095
1 parent 3d0b86d commit 3cae7a8

File tree

4 files changed

+61
-16
lines changed

4 files changed

+61
-16
lines changed

src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -367,17 +367,23 @@ protected override Expression VisitBinary(BinaryExpression binary)
367367
case ExpressionType.Coalesce:
368368
leftValue = Evaluate(left);
369369

370+
Expression returnValue;
370371
switch (leftValue)
371372
{
372373
case null:
373-
return Visit(binary.Right, out _state);
374+
returnValue = Visit(binary.Right, out _state);
375+
break;
374376
case bool b:
375377
_state = leftState with { StateType = StateType.EvaluatableWithoutCapturedVariable };
376-
return Constant(b);
378+
returnValue = Constant(b);
379+
break;
377380
default:
378-
return left;
381+
returnValue = left;
382+
break;
379383
}
380384

385+
return ConvertIfNeeded(returnValue, binary.Type);
386+
381387
case ExpressionType.OrElse or ExpressionType.AndAlso when Evaluate(left) is bool leftBoolValue:
382388
{
383389
left = Constant(leftBoolValue);
@@ -499,9 +505,11 @@ protected override Expression VisitConditional(ConditionalExpression conditional
499505
// If the test evaluates, simplify the conditional away by bubbling up the leg that remains
500506
if (testState.IsEvaluatable && Evaluate(test) is bool testBoolValue)
501507
{
502-
return testBoolValue
503-
? Visit(conditional.IfTrue, out _state)
504-
: Visit(conditional.IfFalse, out _state);
508+
return ConvertIfNeeded(
509+
testBoolValue
510+
? Visit(conditional.IfTrue, out _state)
511+
: Visit(conditional.IfFalse, out _state),
512+
conditional.Type);
505513
}
506514

507515
var ifTrue = Visit(conditional.IfTrue, out var ifTrueState);
@@ -1937,12 +1945,9 @@ private static StateType CombineStateTypes(StateType stateType1, StateType state
19371945
return evaluatableRoot;
19381946
}
19391947

1940-
var returnType = evaluatableRoot.Type;
1941-
var constantExpression = Constant(value, value?.GetType() ?? returnType);
1942-
1943-
return constantExpression.Type != returnType
1944-
? Convert(constantExpression, returnType)
1945-
: constantExpression;
1948+
return ConvertIfNeeded(
1949+
Constant(value, value is null ? evaluatableRoot.Type : value.GetType()),
1950+
evaluatableRoot.Type);
19461951

19471952
bool TryHandleNonEvaluatableAsRoot(Expression root, State state, bool asParameter, [NotNullWhen(true)] out Expression? result)
19481953
{
@@ -2103,6 +2108,9 @@ static Expression RemoveConvert(Expression expression)
21032108
}
21042109
}
21052110

2111+
private static Expression ConvertIfNeeded(Expression expression, Type type)
2112+
=> expression.Type == type ? expression : Convert(expression, type);
2113+
21062114
private bool IsGenerallyEvaluatable(Expression expression)
21072115
=> _evaluatableExpressionFilter.IsEvaluatableExpression(expression, _model)
21082116
&& (_parameterize

test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3303,6 +3303,18 @@ FROM root c
33033303
SELECT VALUE c["OrderID"]
33043304
FROM root c
33053305
WHERE ((c["$type"] = "Order") AND (c["OrderID"] = 10252))
3306+
""");
3307+
});
3308+
3309+
public override Task Simplifiable_coalesce_over_nullable(bool async)
3310+
=> Fixture.NoSyncTest(
3311+
async, async a =>
3312+
{
3313+
await base.Simplifiable_coalesce_over_nullable(a);
3314+
3315+
AssertSql(
3316+
"""
3317+
ReadItem(None, Order|10248)
33063318
""");
33073319
});
33083320

test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2535,7 +2535,18 @@ await AssertQuery(
25352535
elementAsserter: (e, a) => AssertEqual(e.Id, a.Id));
25362536
}
25372537

2538-
#region Evaluation order of predicates
2538+
[ConditionalTheory] // #35095
2539+
[MemberData(nameof(IsAsyncData))]
2540+
public virtual Task Simplifiable_coalesce_over_nullable(bool async)
2541+
{
2542+
int? orderId = 10248;
2543+
2544+
return AssertQuery(
2545+
async,
2546+
ss => ss.Set<Order>().Where(o => o.OrderID == (orderId ?? 0)));
2547+
}
2548+
2549+
#region Evaluation order of operators
25392550

25402551
[ConditionalTheory]
25412552
[MemberData(nameof(IsAsyncData))]
@@ -2558,5 +2569,5 @@ public virtual Task Take_and_Distinct_evaluation_order(bool async)
25582569
async,
25592570
ss => ss.Set<Customer>().Select(c => c.ContactTitle).OrderBy(t => t).Take(3).Distinct());
25602571

2561-
#endregion Evaluation order of predicates
2572+
#endregion Evaluation order of operators
25622573
}

test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3425,7 +3425,21 @@ FROM [Orders] AS [o]
34253425
""");
34263426
}
34273427

3428-
#region Evaluation order of predicates
3428+
public override async Task Simplifiable_coalesce_over_nullable(bool async)
3429+
{
3430+
await base.Simplifiable_coalesce_over_nullable(async);
3431+
3432+
AssertSql(
3433+
"""
3434+
@__p_0='10248'
3435+
3436+
SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
3437+
FROM [Orders] AS [o]
3438+
WHERE [o].[OrderID] = @__p_0
3439+
""");
3440+
}
3441+
3442+
#region Evaluation order of operators
34293443

34303444
public override async Task Take_and_Where_evaluation_order(bool async)
34313445
{
@@ -3483,7 +3497,7 @@ ORDER BY [c].[ContactTitle]
34833497
""");
34843498
}
34853499

3486-
#endregion Evaluation order of predicates
3500+
#endregion Evaluation order of operators
34873501

34883502
private void AssertSql(params string[] expected)
34893503
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);

0 commit comments

Comments
 (0)