/// <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));
                    }