/// <summary> /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will return this expression. /// </summary> /// <param name="arguments">The <see cref="Arguments" /> property of the result.</param> /// <returns>This expression if no children changed, or an expression with the updated children.</returns> public TupleLiteralCSharpExpression Update(IEnumerable <Expression> arguments) { if (arguments == this.Arguments) { return(this); } return(CSharpExpression.TupleLiteral(Type, arguments, ArgumentNames)); }
/// <summary> /// Reduces the expression node to a simpler expression. /// </summary> /// <returns>The reduced expression.</returns> public override Expression Reduce() { var operand = Operand; var temps = new List <ParameterExpression>(); var stmts = new List <Expression>(); // // NB: We can't check IsPure with the readOnly parameter set to true, because the user conversions may assign to a variable. // To mitigate this, we'd have to check all element conversions to make sure they don't assign to the variable, which is // very unlikely, though a hand-written conversion lambda could have such a side-effect. One could argue that a side- // effecting conversion that mutates the parent tuple yields undefined behavior. // if (!Helpers.IsPure(Operand)) { var operandVariable = Expression.Parameter(Operand.Type, "__t"); temps.Add(operandVariable); stmts.Add(Expression.Assign(operandVariable, Operand)); operand = operandVariable; } Expression res; if (operand.Type.IsNullableType()) { var nonNullOperand = MakeNullableGetValue(operand); // NB: Use of Nullable<T>.Value to ensure proper exception is thrown if null. var nonNullOperandVariable = Expression.Parameter(nonNullOperand.Type, "__nonNull"); var args = GetConversions(nonNullOperandVariable); if (Type.IsNullableType()) { // // T? -> U? // // var t = operand; // // if (t.HasValue) // { // var v = t.Value; // return (U?)new U(...); // } // else // { // return default(U?); // } // var hasValueTest = MakeNullableHasValue(operand); var nullValue = Expression.Default(Type); var convertedTuple = Expression.Convert(CSharpExpression.TupleLiteral(Type.GetNonNullableType(), args, argumentNames: null), Type); var nonNullValue = Expression.Block(new[] { nonNullOperandVariable }, Expression.Assign(nonNullOperandVariable, nonNullOperand), convertedTuple); res = Expression.Condition(hasValueTest, nonNullValue, nullValue); } else { // // T? -> U // // var t = operand; // var v = operand.Value; // NB: May throw // return new U(...); temps.Add(nonNullOperandVariable); stmts.Add(Expression.Assign(nonNullOperandVariable, nonNullOperand)); res = CSharpExpression.TupleLiteral(Type, args, argumentNames: null); } } else { var args = GetConversions(operand); var targetType = Type.GetNonNullableType(); var convertedTuple = CSharpExpression.TupleLiteral(targetType, args, argumentNames: null); if (Type.IsNullableType()) { // // T -> U? // // var t = operand; // return (U?)new U(...); res = Expression.Convert(convertedTuple, Type); } else { // // T -> U // // var t = operand; // return new U(...); // res = convertedTuple; } } stmts.Add(res); return(Comma(temps, stmts)); List <Expression> GetConversions(Expression operand) { var n = ElementConversions.Count; var args = new List <Expression>(n); for (int i = 0; i < n; i++) { var conversion = ElementConversions[i]; var item = GetTupleItemAccess(operand, i); var conversionParameter = conversion.Parameters[0]; if (conversion.Body is UnaryExpression { Operand: var unaryOperand } unary&& unaryOperand == conversionParameter && IsConvert(unary.NodeType)) { args.Add(unary.Update(item)); }