Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions src/FastExpressionCompiler/FastExpressionCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3878,17 +3878,13 @@ private static bool TryEmitConvert(UnaryExpression expr, ILGenerator il, ref Com
return il.EmitPopIfIgnoreResult(parent);
}

if (targetType == typeof(object) && sourceType.IsValueType)
if (sourceType.IsValueType && (targetType == typeof(object) ||
underlyingNullableSourceType != null && !targetType.IsValueType &&
(targetType.IsInterface || targetType.IsAssignableFrom(underlyingNullableSourceType))))
{
il.Demit(OpCodes.Box, sourceType);
return il.EmitPopIfIgnoreResult(parent);
}

if (underlyingNullableSourceType != null && !targetType.IsValueType &&
targetType.IsAssignableFrom(underlyingNullableSourceType))
{
il.Demit(OpCodes.Box, sourceType);
il.Demit(OpCodes.Castclass, targetType);
if (targetType.IsInterface && !targetType.IsAssignableFrom(underlyingNullableSourceType ?? sourceType))
il.Demit(OpCodes.Castclass, targetType);
return il.EmitPopIfIgnoreResult(parent);
}

Expand Down
2 changes: 2 additions & 0 deletions test/FastExpressionCompiler.TestsRunner.Net472/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ static void RunLightExpressionTests(object state)

var t = (LightExpression.TestRun)state;
t.Run(new LightExpression.UnitTests.InvokeRefArgumentRegressionTests());
t.Run(new LightExpression.UnitTests.NullableBoxingRegressionTests());
t.Run(new LightExpression.UnitTests.RuntimeSwitchCaseRegressionTests());
t.Run(new LightExpression.UnitTests.StaticFieldAddressRegressionTests());
t.Run(new LightExpression.UnitTests.MultidimensionalGetStorageRegressionTests());
Expand Down Expand Up @@ -84,6 +85,7 @@ static void RunLightExpressionTests(object state)

var fecTests = new TestRun();
fecTests.Run(new InvokeRefArgumentRegressionTests());
fecTests.Run(new NullableBoxingRegressionTests());
fecTests.Run(new LiftedRefConversionRegressionTests());
fecTests.Run(new RuntimeSwitchCaseRegressionTests());
fecTests.Run(new StaticFieldAddressRegressionTests());
Expand Down
2 changes: 2 additions & 0 deletions test/FastExpressionCompiler.TestsRunner/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public static void Main(string[] args)
{
var ltStopwatch = Stopwatch.StartNew();
lt.Run(new LightExpression.UnitTests.InvokeRefArgumentRegressionTests());
lt.Run(new LightExpression.UnitTests.NullableBoxingRegressionTests());
lt.Run(new LightExpression.UnitTests.RuntimeSwitchCaseRegressionTests());
lt.Run(new LightExpression.UnitTests.StaticFieldAddressRegressionTests());
lt.Run(new LightExpression.UnitTests.MultidimensionalGetStorageRegressionTests());
Expand Down Expand Up @@ -110,6 +111,7 @@ public static void Main(string[] args)

var stStopwatch = Stopwatch.StartNew();
st.Run(new InvokeRefArgumentRegressionTests());
st.Run(new NullableBoxingRegressionTests());
st.Run(new LiftedRefConversionRegressionTests());
st.Run(new RuntimeSwitchCaseRegressionTests());
st.Run(new StaticFieldAddressRegressionTests());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Reflection;
using SysExpr = System.Linq.Expressions.Expression;

#if LIGHT_EXPRESSION
using Expr = FastExpressionCompiler.LightExpression.Expression;
namespace FastExpressionCompiler.LightExpression.UnitTests;
#else
using Expr = System.Linq.Expressions.Expression;
namespace FastExpressionCompiler.UnitTests;
#endif

public struct NullableBoxingRegressionTests : ITestX
{
public void Run(TestRun t)
{
Nullable_parameters_box_to_reference_types(t);
Nullable_constants_box_to_reference_types(t);
Nullable_locals_box_to_reference_types(t);

}

public void Nullable_parameters_box_to_reference_types(TestContext t) => Check(t, 0);
public void Nullable_constants_box_to_reference_types(TestContext t) => Check(t, 1);
public void Nullable_locals_box_to_reference_types(TestContext t) => Check(t, 2);

private static void Check(TestContext t, int shape)
{
Type[] types = { typeof(int), typeof(DayOfWeek), typeof(DateTime), typeof(Tagged) };
object[] values = { 42, DayOfWeek.Friday, DateTime.MinValue, new Tagged { Text = "value" } };
Type[] targets = { typeof(object), typeof(ValueType), typeof(Enum), typeof(IComparable), typeof(ITagged) };
for (var i = 0; i < types.Length; ++i)
foreach (var target in targets)
foreach (var value in new[] { null, values[i] })
foreach (var isChecked in new[] { false, true })
{
var nullable = typeof(Nullable<>).MakeGenericType(types[i]);
var parameter = SysExpr.Parameter(nullable);
System.Linq.Expressions.UnaryExpression conversion;
try { conversion = isChecked ? SysExpr.ConvertChecked(parameter, target) : SysExpr.Convert(parameter, target); }
catch (InvalidOperationException) { continue; }
var constant = SysExpr.Constant(value, nullable);
var body = shape == 1 ? conversion.Update(constant)
: shape == 2 ? (SysExpr)SysExpr.Block(new[] { parameter }, SysExpr.Assign(parameter, constant), conversion)
: conversion;
var expression = shape == 0 ? SysExpr.Lambda(body, parameter) : SysExpr.Lambda(body);
var fast = expression.FromSysExpression().CompileFast(true);
if (!t.IsNotNull(fast)) return;
var args = shape == 0 ? new[] { value } : Array.Empty<object>();
t.AreEqual(Invoke(expression.Compile(), args), Invoke(fast, args));
}
}

private static object Invoke(Delegate function, object[] arguments)
{
try { return function.DynamicInvoke(arguments); }
catch (TargetInvocationException e) { return e.InnerException.GetType(); }
}

public interface ITagged { }
public struct Tagged : ITagged { public string Text; }
public class Box { public int Value; }
public struct Convertible
{
public int Value;
public static implicit operator Box(Convertible value) => new Box { Value = value.Value };
}
}
Loading