-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Current Behavior
The reproduction code shows an expression were I assign a constant to a variable before an Await call is made. This expression raises an exception when being lowered.
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.ArgumentException: Expression of type 'System.Int32' cannot be used for assignment to type 'System.Object'
at System.Linq.Expressions.Expression.Assign(Expression left, Expression right)
at Hyperbee.Expressions.CompilerServices.Transitions.Transition.SetResult(List`1 expressions, StateMachineContext context)
at Hyperbee.Expressions.CompilerServices.Transitions.Transition.AddExpressions(List`1 expressions, StateMachineContext context)
at Hyperbee.Expressions.CompilerServices.Transitions.AwaitTransition.AddExpressions(List`1 expressions, StateMachineContext context)
at Hyperbee.Expressions.CompilerServices.StateNode.GetExpression(StateMachineContext context)
at Hyperbee.Expressions.CompilerServices.StateContext.Scope.GetExpressions(StateMachineContext context)
at Hyperbee.Expressions.CompilerServices.AsyncStateMachineBuilder`1.CreateBody(FieldInfo[] fields, StateMachineContext context, Expression[] antecedents)
at Hyperbee.Expressions.CompilerServices.AsyncStateMachineBuilder`1.CreateMoveNextBody(Int32 id, StateMachineContext context, Type stateMachineType, FieldInfo[] fields)
at Hyperbee.Expressions.CompilerServices.AsyncStateMachineBuilder`1.CreateStateMachine(AsyncLoweringTransformer loweringTransformer, Int32 id)
at Hyperbee.Expressions.CompilerServices.AsyncStateMachineBuilder.Create[TResult](AsyncLoweringTransformer loweringTransformer)
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
--- End of inner exception stack trace ---
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at Hyperbee.Expressions.CompilerServices.AsyncStateMachineBuilder.Create(Type resultType, AsyncLoweringTransformer loweringTransformer)
at Hyperbee.Expressions.AsyncBlockExpression.Reduce()
at System.Linq.Expressions.Expression.ReduceAndCheck()
at System.Linq.Expressions.Expression.ReduceExtensions()
at System.Linq.Expressions.Compiler.StackSpiller.RewriteExtensionExpression(Expression expr, Stack stack)
at System.Linq.Expressions.Compiler.StackSpiller.RewriteExpression(Expression node, Stack stack)
at System.Linq.Expressions.Compiler.StackSpiller.Rewrite[T](Expression`1 lambda)
at System.Linq.Expressions.Compiler.LambdaCompiler.Compile(LambdaExpression lambda)
at System.Linq.Expressions.Expression`1.Compile()
at Program.Main() in C:\something\Program.cs:line 25
at Program.<Main>()
Expected Behavior
No expression is thrown and the value is either converted or ignored (the example does not need to have the result of the assign).
Steps To Reproduce
var variable = Variable(typeof(int));
var lambda = Lambda<Func<Task<object>>>(
BlockAsync(
[variable],
[
Assign(variable, Constant(0)),
Await(Constant(Task.FromResult(new object())))
]));
var method = lambda.Compile();
Console.WriteLine(method());Exception is raied when Compile is called.
Anything else?
It seems that this line is causing the issue:
hyperbee.expressions/src/Hyperbee.Expressions/CompilerServices/Transitions/Transition.cs
Line 31 in 0eddf5a
| if ( variable.Type.IsAssignableFrom( lastExpression.Type ) ) |
This line returns
true when variable.Type == typeof(object) and lastExpression.Type == typeof(int). Assigning an int to an object is perfectly valid in C#, but require boxing in the expression trees. My guess would be that this check needs to be stricter. Maybe something like this should be added:
if ( variable.Type.IsAssignableFrom( lastExpression.Type ) && ( variable.Type != typeof( object ) || !lastExpression.Type.IsValueType )) This will only assign the result of the expression to the variable if it's truly compatible. If the user wanted to return an int were the return type is object, a Convert call would be required anyways as the value type (int) needs to be boxed.
I'll try to fix it in my fork of this repo. If the other tests remain working I'll submit the PR.