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