/// <summary>
        /// Produces a chain of equality (or inequality) checks combined logically with AND (or OR)
        /// </summary>
        private BoundExpression RewriteNonNullableNestedTupleOperators(TupleBinaryOperatorInfo.Multiple operators,
                                                                       BoundExpression left, BoundExpression right, TypeSymbol type,
                                                                       ArrayBuilder <LocalSymbol> temps, ArrayBuilder <BoundExpression> effects, BinaryOperatorKind operatorKind)
        {
            ImmutableArray <TupleBinaryOperatorInfo> nestedOperators = operators.Operators;

            BoundExpression currentResult = null;

            for (int i = 0; i < nestedOperators.Length; i++)
            {
                BoundExpression leftElement        = GetTuplePart(left, i);
                BoundExpression rightElement       = GetTuplePart(right, i);
                BoundExpression nextLogicalOperand = RewriteTupleOperator(nestedOperators[i], leftElement, rightElement, type, temps, operatorKind);
                if (currentResult is null)
                {
                    currentResult = nextLogicalOperand;
                }
                else
                {
                    var logicalOperator = operatorKind == BinaryOperatorKind.Equal ? BinaryOperatorKind.LogicalBoolAnd : BinaryOperatorKind.LogicalBoolOr;
                    currentResult = _factory.Binary(logicalOperator, type, currentResult, nextLogicalOperand);
                }
            }

            return(currentResult);
        }
        /// <summary>
        /// If the left and right are tuples of matching cardinality, we'll try to bind the operator element-wise.
        /// When that succeeds, the element-wise conversions are collected. We keep them for semantic model.
        /// The element-wise binary operators are collected and stored as a tree for lowering.
        /// </summary>
        private BoundTupleBinaryOperator BindTupleBinaryOperator(BinaryExpressionSyntax node, BinaryOperatorKind kind,
                                                                 BoundExpression left, BoundExpression right, DiagnosticBag diagnostics)
        {
            TupleBinaryOperatorInfo.Multiple operators = BindTupleBinaryOperatorNestedInfo(node, kind, left, right, diagnostics);

            // The converted types are only used for the semantic model, so we don't need the conversion diagnostics
            DiagnosticBag   discardDiagnostics = DiagnosticBag.GetInstance();
            BoundExpression convertedLeft      = ApplyConvertedTypes(left, operators, isRight: false, discardDiagnostics);
            BoundExpression convertedRight     = ApplyConvertedTypes(right, operators, isRight: true, discardDiagnostics);

            discardDiagnostics.Free();

            TypeSymbol resultType = GetSpecialType(SpecialType.System_Boolean, diagnostics, node);

            return(new BoundTupleBinaryOperator(node, left, right, convertedLeft, convertedRight, kind, operators, resultType));
        }
        private BoundExpression RewriteTupleNestedOperators(TupleBinaryOperatorInfo.Multiple operators, BoundExpression left, BoundExpression right,
                                                            TypeSymbol boolType, ArrayBuilder <LocalSymbol> temps, BinaryOperatorKind operatorKind)
        {
            left  = Binder.GiveTupleTypeToDefaultLiteralIfNeeded(left, right.Type);
            right = Binder.GiveTupleTypeToDefaultLiteralIfNeeded(right, left.Type);

            // If either left or right is nullable, produce:
            //
            //      // outer sequence
            //      leftHasValue = left.HasValue; (or true if !leftNullable)
            //      leftHasValue == right.HasValue (or true if !rightNullable)
            //          ? leftHasValue ? ... inner sequence ... : true/false
            //          : false/true
            //
            // where inner sequence is:
            //      leftValue = left.GetValueOrDefault(); (or left if !leftNullable)
            //      rightValue = right.GetValueOrDefault(); (or right if !rightNullable)
            //      ... logical expression using leftValue and rightValue ...
            //
            // and true/false and false/true depend on operatorKind (== vs. !=)
            //
            // But if neither is nullable, then just produce the inner sequence.
            //
            // Note: all the temps are created in a single bucket (rather than different scopes of applicability) for simplicity

            var outerEffects = ArrayBuilder <BoundExpression> .GetInstance();

            var innerEffects = ArrayBuilder <BoundExpression> .GetInstance();

            BoundExpression leftHasValue, leftValue;
            bool            isLeftNullable;

            MakeNullableParts(left, temps, innerEffects, outerEffects, saveHasValue: true, out leftHasValue, out leftValue, out isLeftNullable);

            BoundExpression rightHasValue, rightValue;
            bool            isRightNullable;

            // no need for local for right.HasValue since used once
            MakeNullableParts(right, temps, innerEffects, outerEffects, saveHasValue: false, out rightHasValue, out rightValue, out isRightNullable);

            // Produces:
            //     ... logical expression using leftValue and rightValue ...
            BoundExpression logicalExpression = RewriteNonNullableNestedTupleOperators(operators, leftValue, rightValue, boolType, temps, innerEffects, operatorKind);

            // Produces:
            //     leftValue = left.GetValueOrDefault(); (or left if !leftNullable)
            //     rightValue = right.GetValueOrDefault(); (or right if !rightNullable)
            //     ... logical expression using leftValue and rightValue ...
            BoundExpression innerSequence = MakeSequenceOrResultValue(locals: ImmutableArray <LocalSymbol> .Empty, innerEffects.ToImmutableAndFree(), logicalExpression);

            if (!isLeftNullable && !isRightNullable)
            {
                // The outer sequence degenerates when we know that both `leftHasValue` and `rightHasValue` are true
                return(innerSequence);
            }

            bool boolValue = operatorKind == BinaryOperatorKind.Equal; // true/false

            if (rightHasValue.ConstantValue == ConstantValue.False)
            {
                // The outer sequence degenerates when we known that `rightHasValue` is false
                // Produce: !leftHasValue (or leftHasValue for inequality comparison)
                return(MakeSequenceOrResultValue(ImmutableArray <LocalSymbol> .Empty, outerEffects.ToImmutableAndFree(),
                                                 returnValue: boolValue?_factory.Not(leftHasValue) : leftHasValue));
            }

            if (leftHasValue.ConstantValue == ConstantValue.False)
            {
                // The outer sequence degenerates when we known that `leftHasValue` is false
                // Produce: !rightHasValue (or rightHasValue for inequality comparison)
                return(MakeSequenceOrResultValue(ImmutableArray <LocalSymbol> .Empty, outerEffects.ToImmutableAndFree(),
                                                 returnValue: boolValue?_factory.Not(rightHasValue) : rightHasValue));
            }

            // outer sequence:
            //      leftHasValue == rightHasValue
            //          ? leftHasValue ? ... inner sequence ... : true/false
            //          : false/true
            BoundExpression outerSequence =
                MakeSequenceOrResultValue(ImmutableArray <LocalSymbol> .Empty, outerEffects.ToImmutableAndFree(),
                                          _factory.Conditional(
                                              _factory.Binary(BinaryOperatorKind.Equal, boolType, leftHasValue, rightHasValue),
                                              _factory.Conditional(leftHasValue, innerSequence, MakeBooleanConstant(right.Syntax, boolValue), boolType),
                                              MakeBooleanConstant(right.Syntax, !boolValue),
                                              boolType));

            return(outerSequence);
        }