public static BinaryOperatorKind WithOverflowChecksIfApplicable(this BinaryOperatorKind kind, bool enabled) { if (enabled) { // If it's a dynamic binop then make it checked. Let the lowering // pass sort out what to do with it. if (kind.IsDynamic()) { return(kind | BinaryOperatorKind.Checked); } if (kind.IsIntegral()) { switch (kind.Operator()) { case BinaryOperatorKind.Addition: case BinaryOperatorKind.Subtraction: case BinaryOperatorKind.Multiplication: case BinaryOperatorKind.Division: return(kind | BinaryOperatorKind.Checked); } } return(kind); } else { return(kind & ~BinaryOperatorKind.Checked); } }
private static string GetBinaryOperatorName(BinaryOperatorKind opKind, out bool isChecked, out bool isLifted, out bool requiresLifted) { isChecked = opKind.IsChecked(); isLifted = opKind.IsLifted(); requiresLifted = opKind.IsComparison(); switch (opKind.Operator()) { case BinaryOperatorKind.Addition: return(isChecked ? "AddChecked" : "Add"); case BinaryOperatorKind.Multiplication: return(isChecked ? "MultiplyChecked" : "Multiply"); case BinaryOperatorKind.Subtraction: return(isChecked ? "SubtractChecked" : "Subtract"); case BinaryOperatorKind.Division: return("Divide"); case BinaryOperatorKind.Remainder: return("Modulo"); case BinaryOperatorKind.And: return(opKind.IsLogical() ? "AndAlso" : "And"); case BinaryOperatorKind.Xor: return("ExclusiveOr"); case BinaryOperatorKind.Or: return(opKind.IsLogical() ? "OrElse" : "Or"); case BinaryOperatorKind.LeftShift: return("LeftShift"); case BinaryOperatorKind.RightShift: return("RightShift"); case BinaryOperatorKind.Equal: return("Equal"); case BinaryOperatorKind.NotEqual: return("NotEqual"); case BinaryOperatorKind.LessThan: return("LessThan"); case BinaryOperatorKind.LessThanOrEqual: return("LessThanOrEqual"); case BinaryOperatorKind.GreaterThan: return("GreaterThan"); case BinaryOperatorKind.GreaterThanOrEqual: return("GreaterThanOrEqual"); default: throw ExceptionUtilities.UnexpectedValue(opKind.Operator()); } }
private MethodSymbol GetDecimalIncDecOperator(BinaryOperatorKind oper) { SpecialMember member; switch (oper.Operator()) { case BinaryOperatorKind.Addition: member = SpecialMember.System_Decimal__op_Increment; break; case BinaryOperatorKind.Subtraction: member = SpecialMember.System_Decimal__op_Decrement; break; default: throw ExceptionUtilities.UnexpectedValue(oper.Operator()); } var method = (MethodSymbol)_compilation.Assembly.GetSpecialTypeMember(member); Debug.Assert((object)method != null); // Should have been checked during Warnings pass return(method); }
private static bool OperatorHasSideEffects(BinaryOperatorKind kind) { switch (kind.Operator()) { case BinaryOperatorKind.Division: case BinaryOperatorKind.Remainder: return(true); default: return(kind.IsChecked()); } }
public static bool IsComparison(this BinaryOperatorKind kind) { switch (kind.Operator()) { case BinaryOperatorKind.Equal: case BinaryOperatorKind.NotEqual: case BinaryOperatorKind.GreaterThan: case BinaryOperatorKind.GreaterThanOrEqual: case BinaryOperatorKind.LessThan: case BinaryOperatorKind.LessThanOrEqual: return(true); } return(false); }
internal BinaryOperatorSignature GetSignature(BinaryOperatorKind kind) { var left = LeftType(kind); switch (kind.Operator()) { case BinaryOperatorKind.Multiplication: case BinaryOperatorKind.Division: case BinaryOperatorKind.Subtraction: return(new BinaryOperatorSignature(kind, left, left, left)); case BinaryOperatorKind.Addition: return(new BinaryOperatorSignature(kind, LeftType(kind), RightType(kind), ReturnType(kind))); } return(new BinaryOperatorSignature(kind, LeftType(kind), RightType(kind), ReturnType(kind))); }
public static bool EmitsAsCheckedInstruction(this BinaryOperatorKind kind) { if (!kind.IsChecked()) { return(false); } switch (kind.Operator()) { case BinaryOperatorKind.Addition: case BinaryOperatorKind.Subtraction: case BinaryOperatorKind.Multiplication: return(true); } return(false); }
private MethodSymbol GetDecimalIncDecOperator(BinaryOperatorKind oper) { SpecialMember member; switch (oper.Operator()) { case BinaryOperatorKind.Addition: member = SpecialMember.System_Decimal__op_Increment; break; case BinaryOperatorKind.Subtraction: member = SpecialMember.System_Decimal__op_Decrement; break; default: Debug.Assert(false); // Cannot reach here return(null); } var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(member); Debug.Assert((object)method != null); // Should have been checked during Warnings pass return(method); }
internal BinaryOperatorSignature GetSignature(BinaryOperatorKind kind) { var left = LeftType(kind); switch (kind.Operator()) { case BinaryOperatorKind.Multiplication: case BinaryOperatorKind.Division: case BinaryOperatorKind.Subtraction: case BinaryOperatorKind.Remainder: case BinaryOperatorKind.And: case BinaryOperatorKind.Or: case BinaryOperatorKind.Xor: return(new BinaryOperatorSignature(kind, left, left, left)); case BinaryOperatorKind.Addition: return(new BinaryOperatorSignature(kind, LeftType(kind), RightType(kind), ReturnType(kind))); case BinaryOperatorKind.LeftShift: case BinaryOperatorKind.RightShift: TypeSymbol returnType = Compilation.GetSpecialType(SpecialType.System_Int32); if (kind.IsLifted()) { returnType = Compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(returnType); } return(new BinaryOperatorSignature(kind, left, returnType, left)); case BinaryOperatorKind.Equal: case BinaryOperatorKind.NotEqual: case BinaryOperatorKind.GreaterThan: case BinaryOperatorKind.LessThan: case BinaryOperatorKind.GreaterThanOrEqual: case BinaryOperatorKind.LessThanOrEqual: return(new BinaryOperatorSignature(kind, left, left, Compilation.GetSpecialType(SpecialType.System_Boolean))); } return(new BinaryOperatorSignature(kind, LeftType(kind), RightType(kind), ReturnType(kind))); }
public static BinaryOperatorKind WithOverflowChecksIfApplicable(this BinaryOperatorKind kind, bool enabled) { if (enabled) { if (kind.IsIntegral()) { switch (kind.Operator()) { case BinaryOperatorKind.Addition: case BinaryOperatorKind.Subtraction: case BinaryOperatorKind.Multiplication: case BinaryOperatorKind.Division: return(kind | BinaryOperatorKind.Checked); } } return(kind); } else { return(kind & ~BinaryOperatorKind.Checked); } }
private static bool IsUnsignedBinaryOperator(BoundBinaryOperator op) { BinaryOperatorKind opKind = op.OperatorKind; Debug.Assert(opKind.Operator() != BinaryOperatorKind.UnsignedRightShift); BinaryOperatorKind type = opKind.OperandTypes(); switch (type) { case BinaryOperatorKind.Enum: case BinaryOperatorKind.EnumAndUnderlying: return(IsUnsigned(Binder.GetEnumPromotedType(op.Left.Type.GetEnumUnderlyingType().SpecialType))); case BinaryOperatorKind.UnderlyingAndEnum: return(IsUnsigned(Binder.GetEnumPromotedType(op.Right.Type.GetEnumUnderlyingType().SpecialType))); case BinaryOperatorKind.UInt: case BinaryOperatorKind.NUInt: case BinaryOperatorKind.ULong: case BinaryOperatorKind.ULongAndPointer: case BinaryOperatorKind.PointerAndInt: case BinaryOperatorKind.PointerAndUInt: case BinaryOperatorKind.PointerAndLong: case BinaryOperatorKind.PointerAndULong: case BinaryOperatorKind.Pointer: return(true); // Dev10 bases signedness on the first operand (see ILGENREC::genOperatorExpr). case BinaryOperatorKind.IntAndPointer: case BinaryOperatorKind.LongAndPointer: // Dev10 converts the uint to a native int, so it counts as signed. case BinaryOperatorKind.UIntAndPointer: default: return(false); } }
private string GetBinaryOperatorName(BinaryOperatorKind opKind, out bool isChecked, out bool isLifted, out bool requiresLifted) { isChecked = opKind.IsChecked(); isLifted = opKind.IsLifted(); requiresLifted = opKind.IsComparison(); switch (opKind.Operator()) { case BinaryOperatorKind.Addition: return isChecked ? "AddChecked" : "Add"; case BinaryOperatorKind.Multiplication: return isChecked ? "MultiplyChecked" : "Multiply"; case BinaryOperatorKind.Subtraction: return isChecked ? "SubtractChecked" : "Subtract"; case BinaryOperatorKind.Division: return "Divide"; case BinaryOperatorKind.Remainder: return "Modulo"; case BinaryOperatorKind.And: return opKind.IsLogical() ? "AndAlso" : "And"; case BinaryOperatorKind.Xor: return "ExclusiveOr"; case BinaryOperatorKind.Or: return opKind.IsLogical() ? "OrElse" : "Or"; case BinaryOperatorKind.LeftShift: return "LeftShift"; case BinaryOperatorKind.RightShift: return "RightShift"; case BinaryOperatorKind.Equal: return "Equal"; case BinaryOperatorKind.NotEqual: return "NotEqual"; case BinaryOperatorKind.LessThan: return "LessThan"; case BinaryOperatorKind.LessThanOrEqual: return "LessThanOrEqual"; case BinaryOperatorKind.GreaterThan: return "GreaterThan"; case BinaryOperatorKind.GreaterThanOrEqual: return "GreaterThanOrEqual"; default: throw ExceptionUtilities.UnexpectedValue(opKind.Operator()); } }
/// <summary> /// If one of the (unconverted) operands has constant value null and the other has /// a null constant value other than null, then they are definitely not equal /// and we can give a constant value for either == or !=. This is a spec violation /// that we retain from Dev10. /// </summary> /// <param name="kind">The operator kind. Nothing will happen if it is not a lifted equality operator.</param> /// <param name="left">The left-hand operand of the operation (possibly wrapped in a conversion).</param> /// <param name="right">The right-hand operand of the operation (possibly wrapped in a conversion).</param> /// <returns> /// If the operator represents lifted equality, then constant value true if both arguments have constant /// value null, constant value false if exactly one argument has constant value null, and null otherwise. /// If the operator represents lifted inequality, then constant value false if both arguments have constant /// value null, constant value true if exactly one argument has constant value null, and null otherwise. /// </returns> /// <remarks> /// SPEC VIOLATION: according to the spec (section 7.19) constant expressions cannot /// include implicit nullable conversions or nullable subexpressions. However, Dev10 /// specifically folds over lifted == and != (see ExpressionBinder::TryFoldingNullableEquality). /// Dev 10 does do compile-time evaluation of simple lifted operators, but it does so /// in a rewriting pass (see NullableRewriter) - they are not treated as constant values. /// </remarks> private static ConstantValue TryFoldingNullableEquality(BinaryOperatorKind kind, BoundExpression left, BoundExpression right) { if (kind.IsLifted()) { BinaryOperatorKind op = kind.Operator(); if (op == BinaryOperatorKind.Equal || op == BinaryOperatorKind.NotEqual) { if (left.Kind == BoundKind.Conversion && right.Kind == BoundKind.Conversion) { BoundConversion leftConv = (BoundConversion)left; BoundConversion rightConv = (BoundConversion)right; ConstantValue leftConstant = leftConv.Operand.ConstantValue; ConstantValue rightConstant = rightConv.Operand.ConstantValue; if (leftConstant != null && rightConstant != null) { bool leftIsNull = leftConstant.IsNull; bool rightIsNull = rightConstant.IsNull; if (leftIsNull || rightIsNull) { // IMPL CHANGE: Dev10 raises WRN_NubExprIsConstBool in some cases, but that really doesn't // make sense (why warn that a constant has a constant value?). return (leftIsNull == rightIsNull) == (op == BinaryOperatorKind.Equal) ? ConstantValue.True : ConstantValue.False; } } } } } return null; }
private ConstantValue FoldEnumBinaryOperator( CSharpSyntaxNode syntax, BinaryOperatorKind kind, BoundExpression left, BoundExpression right, DiagnosticBag diagnostics) { Debug.Assert(left != null); Debug.Assert(right != null); Debug.Assert(kind.IsEnum()); Debug.Assert(!kind.IsLifted()); // A built-in binary operation on constant enum operands is evaluated into an operation on // constants of the underlying type U of the enum type E. Comparison operators are lowered as // simply computing U<U. All other operators are computed as (E)(U op U) or in the case of // E-E, (U)(U-U). TypeSymbol enumType = GetEnumType(kind, left, right); TypeSymbol underlyingType = enumType.GetEnumUnderlyingType(); BoundExpression newLeftOperand = CreateConversion(left, underlyingType, diagnostics); BoundExpression newRightOperand = CreateConversion(right, underlyingType, diagnostics); // If the underlying type is byte, sbyte, short, ushort or nullables of those then we'll need // to convert it up to int or int? because there are no + - * & | ^ < > <= >= == != operators // on byte, sbyte, short or ushort. They all convert to int. SpecialType operandSpecialType = GetEnumPromotedType(underlyingType.SpecialType); TypeSymbol operandType = (operandSpecialType == underlyingType.SpecialType) ? underlyingType : GetSpecialType(operandSpecialType, diagnostics, syntax); newLeftOperand = CreateConversion(newLeftOperand, operandType, diagnostics); newRightOperand = CreateConversion(newRightOperand, operandType, diagnostics); BinaryOperatorKind newKind = kind.Operator().WithType(newLeftOperand.Type.SpecialType); SpecialType operatorType = SpecialType.None; switch (newKind.Operator()) { case BinaryOperatorKind.Addition: case BinaryOperatorKind.Subtraction: case BinaryOperatorKind.And: case BinaryOperatorKind.Or: case BinaryOperatorKind.Xor: operatorType = operandType.SpecialType; break; case BinaryOperatorKind.LessThan: case BinaryOperatorKind.LessThanOrEqual: case BinaryOperatorKind.GreaterThan: case BinaryOperatorKind.GreaterThanOrEqual: case BinaryOperatorKind.Equal: case BinaryOperatorKind.NotEqual: operatorType = SpecialType.System_Boolean; break; default: throw ExceptionUtilities.UnexpectedValue(newKind.Operator()); } var constantValue = FoldBinaryOperator(syntax, newKind, newLeftOperand, newRightOperand, operatorType, diagnostics); if (operatorType != SpecialType.System_Boolean && constantValue != null && !constantValue.IsBad) { TypeSymbol resultType = kind == BinaryOperatorKind.EnumSubtraction ? underlyingType : enumType; // We might need to convert back to the underlying type. return FoldConstantNumericConversion(syntax, constantValue, resultType, diagnostics); } return constantValue; }
private MethodSymbol GetDecimalIncDecOperator(BinaryOperatorKind oper) { SpecialMember member; switch (oper.Operator()) { case BinaryOperatorKind.Addition: member = SpecialMember.System_Decimal__op_Increment; break; case BinaryOperatorKind.Subtraction: member = SpecialMember.System_Decimal__op_Decrement; break; default: Debug.Assert(false); // Cannot reach here return null; } var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(member); Debug.Assert((object)method != null); // Should have been checked during Warnings pass return method; }
public override string ToString() => $"RelationalDispatch.{Height}({Left} {Operator.Operator()} {Value} {Right})";
private MethodSymbol GetDecimalIncDecOperator(BinaryOperatorKind oper) { SpecialMember member; switch (oper.Operator()) { case BinaryOperatorKind.Addition: member = SpecialMember.System_Decimal__op_Increment; break; case BinaryOperatorKind.Subtraction: member = SpecialMember.System_Decimal__op_Decrement; break; default: throw ExceptionUtilities.UnexpectedValue(oper.Operator()); } var method = (MethodSymbol)_compilation.Assembly.GetSpecialTypeMember(member); Debug.Assert((object)method != null); // Should have been checked during Warnings pass return method; }
private static bool IsReversed(BinaryOperatorKind op) => op.Operator() switch {
private BoundExpression LowerLiftedBuiltInComparisonOperator( CSharpSyntaxNode syntax, BinaryOperatorKind kind, BoundExpression loweredLeft, BoundExpression loweredRight) { // SPEC: For the equality operators == != : // SPEC: The lifted operator considers two null values equal and a null value unequal to // SPEC: any non-null value. If both operands are non-null the lifted operator unwraps // SPEC: the operands and applies the underlying operator to produce the bool result. // SPEC: // SPEC: For the relational operators < > <= >= : // SPEC: The lifted operator produces the value false if one or both operands // SPEC: are null. Otherwise the lifted operator unwraps the operands and // SPEC: applies the underlying operator to produce the bool result. // Note that this means that x == y is true but x <= y is false if both are null. // x <= y is not the same as (x < y) || (x == y). // Start with some simple optimizations for cases like one side being null. BoundExpression optimized = TrivialLiftedComparisonOperatorOptimizations(syntax, kind, loweredLeft, loweredRight, null); if (optimized != null) { return optimized; } // We rewrite x == y as // // tempx = x; // tempy = y; // result = tempx.GetValueOrDefault() == tempy.GetValueOrDefault() ? // tempx.HasValue == tempy.HasValue : // false; // // and x != y as // // tempx = x; // tempy = y; // result = tempx.GetValueOrDefault() == tempy.GetValueOrDefault() ? // tempx.HasValue != tempy.HasValue : // true; // // Otherwise, we rewrite x OP y as // // tempx = x; // tempy = y; // result = tempx.GetValueOrDefault() OP tempy.GetValueOrDefault() ? // tempx.HasValue & tempy.HasValue : // false; // // Note that there is no reason to generate "&&" over "&"; the cost of // the added code for the conditional branch would be about the same as simply doing // the bitwise & in the first place. // // We have not yet optimized the case where we have a known-not-null value on one side, // and an unknown value on the other. In those cases we will still generate a temp, but // we will not generate the call to the unnecessary nullable ctor or to GetValueOrDefault. // Rather, we will generate the value's temp instead of a call to GetValueOrDefault, and generate // literal true for HasValue. The tree construction methods we call will use those constants // to eliminate unnecessary branches. BoundExpression xNonNull = NullableAlwaysHasValue(loweredLeft); BoundExpression yNonNull = NullableAlwaysHasValue(loweredRight); BoundAssignmentOperator tempAssignmentX; BoundLocal boundTempX = _factory.StoreToTemp(xNonNull ?? loweredLeft, out tempAssignmentX); BoundAssignmentOperator tempAssignmentY; BoundLocal boundTempY = _factory.StoreToTemp(yNonNull ?? loweredRight, out tempAssignmentY); BoundExpression callX_GetValueOrDefault = MakeOptimizedGetValueOrDefault(syntax, boundTempX); BoundExpression callY_GetValueOrDefault = MakeOptimizedGetValueOrDefault(syntax, boundTempY); BoundExpression callX_HasValue = MakeOptimizedHasValue(syntax, boundTempX); BoundExpression callY_HasValue = MakeOptimizedHasValue(syntax, boundTempY); // tempx.GetValueOrDefault() == tempy.GetValueOrDefault() BinaryOperatorKind operatorKind = kind.Operator(); BinaryOperatorKind conditionOperator = operatorKind == BinaryOperatorKind.NotEqual ? BinaryOperatorKind.Equal : operatorKind; TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression condition = MakeBinaryOperator( syntax: syntax, operatorKind: conditionOperator.WithType(kind.OperandTypes()), loweredLeft: callX_GetValueOrDefault, loweredRight: callY_GetValueOrDefault, type: boolType, method: null); BinaryOperatorKind consequenceOperator; switch (operatorKind) { case BinaryOperatorKind.Equal: consequenceOperator = BinaryOperatorKind.BoolEqual; break; case BinaryOperatorKind.NotEqual: consequenceOperator = BinaryOperatorKind.BoolNotEqual; break; default: consequenceOperator = BinaryOperatorKind.BoolAnd; break; } // tempx.HasValue == tempy.HasValue BoundExpression consequence = MakeBinaryOperator( syntax: syntax, operatorKind: consequenceOperator, loweredLeft: callX_HasValue, loweredRight: callY_HasValue, type: boolType, method: null); // false BoundExpression alternative = this.MakeBooleanConstant(syntax, operatorKind == BinaryOperatorKind.NotEqual); // tempx.GetValueOrDefault() == tempy.GetValueOrDefault() ? // tempx.HasValue == tempy.HasValue : // false; BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: boolType); // tempx = x; // tempy = y; // result = tempx.GetValueOrDefault() == tempy.GetValueOrDefault() ? // tempx.HasValue == tempy.HasValue : // false; return new BoundSequence( syntax: syntax, locals: ImmutableArray.Create<LocalSymbol>(boundTempX.LocalSymbol, boundTempY.LocalSymbol), sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignmentX, tempAssignmentY), value: conditionalExpression, type: boolType); }
private BoundExpression MakeDynamicLogicalBinaryOperator( CSharpSyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, MethodSymbol leftTruthOperator, TypeSymbol type, bool isCompoundAssignment, BoundUnaryOperator applyParentUnaryOperator) { Debug.Assert(operatorKind.Operator() == BinaryOperatorKind.And || operatorKind.Operator() == BinaryOperatorKind.Or); // Dynamic logical && and || operators are lowered as follows: // left && right -> IsFalse(left) ? left : And(left, right) // left || right -> IsTrue(left) ? left : Or(left, right) // // Optimization: If the binary AND/OR is directly contained in IsFalse/IsTrue operator (parentUnaryOperator != null) // we can avoid calling IsFalse/IsTrue twice on the same object. // IsFalse(left && right) -> IsFalse(left) || IsFalse(And(left, right)) // IsTrue(left || right) -> IsTrue(left) || IsTrue(Or(left, right)) bool isAnd = operatorKind.Operator() == BinaryOperatorKind.And; // Operator to be used to test the left operand: var testOperator = isAnd ? UnaryOperatorKind.DynamicFalse : UnaryOperatorKind.DynamicTrue; // VisitUnaryOperator ensures we are never called with parentUnaryOperator != null when we can't perform the optimization. Debug.Assert(applyParentUnaryOperator == null || applyParentUnaryOperator.OperatorKind == testOperator); ConstantValue constantLeft = loweredLeft.ConstantValue ?? UnboxConstant(loweredLeft); if (testOperator == UnaryOperatorKind.DynamicFalse && constantLeft == ConstantValue.False || testOperator == UnaryOperatorKind.DynamicTrue && constantLeft == ConstantValue.True) { Debug.Assert(leftTruthOperator == null); if (applyParentUnaryOperator != null) { // IsFalse(false && right) -> true // IsTrue(true || right) -> true return _factory.Literal(true); } else { // false && right -> box(false) // true || right -> box(true) return MakeConversionNode(loweredLeft, type, @checked: false); } } BoundExpression result; var boolean = _compilation.GetSpecialType(SpecialType.System_Boolean); // Store left to local if needed. If constant or already local we don't need a temp // since the value of left can't change until right is evaluated. BoundAssignmentOperator tempAssignment; BoundLocal temp; if (constantLeft == null && loweredLeft.Kind != BoundKind.Local && loweredLeft.Kind != BoundKind.Parameter) { BoundAssignmentOperator assignment; var local = _factory.StoreToTemp(loweredLeft, out assignment); loweredLeft = local; tempAssignment = assignment; temp = local; } else { tempAssignment = null; temp = null; } var op = _dynamicFactory.MakeDynamicBinaryOperator(operatorKind, loweredLeft, loweredRight, isCompoundAssignment, type).ToExpression(); // IsFalse(true) or IsTrue(false) are always false: bool leftTestIsConstantFalse = testOperator == UnaryOperatorKind.DynamicFalse && constantLeft == ConstantValue.True || testOperator == UnaryOperatorKind.DynamicTrue && constantLeft == ConstantValue.False; if (applyParentUnaryOperator != null) { // IsFalse(left && right) -> IsFalse(left) || IsFalse(And(left, right)) // IsTrue(left || right) -> IsTrue(left) || IsTrue(Or(left, right)) result = _dynamicFactory.MakeDynamicUnaryOperator(testOperator, op, boolean).ToExpression(); if (!leftTestIsConstantFalse) { BoundExpression leftTest = MakeTruthTestForDynamicLogicalOperator(syntax, loweredLeft, boolean, leftTruthOperator, negative: isAnd); result = _factory.Binary(BinaryOperatorKind.LogicalOr, boolean, leftTest, result); } } else { // left && right -> IsFalse(left) ? left : And(left, right) // left || right -> IsTrue(left) ? left : Or(left, right) if (leftTestIsConstantFalse) { result = op; } else { // We might need to box. BoundExpression leftTest = MakeTruthTestForDynamicLogicalOperator(syntax, loweredLeft, boolean, leftTruthOperator, negative: isAnd); var convertedLeft = MakeConversionNode(loweredLeft, type, @checked: false); result = _factory.Conditional(leftTest, convertedLeft, op, type); } } if (tempAssignment != null) { return _factory.Sequence(ImmutableArray.Create(temp.LocalSymbol), ImmutableArray.Create<BoundExpression>(tempAssignment), result); } return result; }
private BoundExpression RewritePointerNumericOperator( CSharpSyntaxNode syntax, BinaryOperatorKind kind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol returnType, bool isPointerElementAccess, bool isLeftPointer) { if (isLeftPointer) { loweredRight = MakeSizeOfMultiplication(loweredRight, (PointerTypeSymbol)loweredLeft.Type, kind.IsChecked()); } else { loweredLeft = MakeSizeOfMultiplication(loweredLeft, (PointerTypeSymbol)loweredRight.Type, kind.IsChecked()); } if (isPointerElementAccess) { Debug.Assert(kind.Operator() == BinaryOperatorKind.Addition); // NOTE: This is here to persist a bug in Dev10. checked(p[n]) should be equivalent to checked(*(p + n)), // but Dev10 omits the check on the addition (though it retains the check on the multiplication of n by // the size). kind = kind & ~BinaryOperatorKind.Checked; } return new BoundBinaryOperator( syntax, kind, loweredLeft, loweredRight, ConstantValue.NotAvailable, null, LookupResultKind.Viable, returnType); }
private static bool OperatorHasSideEffects(BinaryOperatorKind kind) { switch (kind.Operator()) { case BinaryOperatorKind.Division: case BinaryOperatorKind.Remainder: return true; default: return kind.IsChecked(); } }
public static int OperatorIndex(this BinaryOperatorKind kind) { return(((int)kind.Operator() >> 8) - 16); }
private BoundExpression MakeBinaryOperator( BoundBinaryOperator oldNode, CSharpSyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type, MethodSymbol method, bool isPointerElementAccess = false, bool isCompoundAssignment = false, BoundUnaryOperator applyParentUnaryOperator = null) { Debug.Assert(oldNode == null || (oldNode.Syntax == syntax)); if (_inExpressionLambda) { switch (operatorKind.Operator() | operatorKind.OperandTypes()) { case BinaryOperatorKind.ObjectAndStringConcatenation: case BinaryOperatorKind.StringAndObjectConcatenation: case BinaryOperatorKind.StringConcatenation: return RewriteStringConcatenation(syntax, operatorKind, loweredLeft, loweredRight, type); case BinaryOperatorKind.DelegateCombination: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__Combine); case BinaryOperatorKind.DelegateRemoval: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__Remove); case BinaryOperatorKind.DelegateEqual: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__op_Equality); case BinaryOperatorKind.DelegateNotEqual: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__op_Inequality); } } else // try to lower the expression. { if (operatorKind.IsDynamic()) { Debug.Assert(!isPointerElementAccess); if (operatorKind.IsLogical()) { return MakeDynamicLogicalBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, method, type, isCompoundAssignment, applyParentUnaryOperator); } else { Debug.Assert((object)method == null); return _dynamicFactory.MakeDynamicBinaryOperator(operatorKind, loweredLeft, loweredRight, isCompoundAssignment, type).ToExpression(); } } if (operatorKind.IsLifted()) { return RewriteLiftedBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method); } if (operatorKind.IsUserDefined()) { return LowerUserDefinedBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method); } switch (operatorKind.OperatorWithLogical() | operatorKind.OperandTypes()) { case BinaryOperatorKind.NullableNullEqual: case BinaryOperatorKind.NullableNullNotEqual: return RewriteNullableNullEquality(syntax, operatorKind, loweredLeft, loweredRight, type); case BinaryOperatorKind.ObjectAndStringConcatenation: case BinaryOperatorKind.StringAndObjectConcatenation: case BinaryOperatorKind.StringConcatenation: return RewriteStringConcatenation(syntax, operatorKind, loweredLeft, loweredRight, type); case BinaryOperatorKind.StringEqual: return RewriteStringEquality(oldNode, syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_String__op_Equality); case BinaryOperatorKind.StringNotEqual: return RewriteStringEquality(oldNode, syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_String__op_Inequality); case BinaryOperatorKind.DelegateCombination: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__Combine); case BinaryOperatorKind.DelegateRemoval: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__Remove); case BinaryOperatorKind.DelegateEqual: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__op_Equality); case BinaryOperatorKind.DelegateNotEqual: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__op_Inequality); case BinaryOperatorKind.LogicalBoolAnd: if (loweredRight.ConstantValue == ConstantValue.True) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.True) return loweredRight; if (loweredLeft.ConstantValue == ConstantValue.False) return loweredLeft; if (loweredRight.Kind == BoundKind.Local || loweredRight.Kind == BoundKind.Parameter) { operatorKind &= ~BinaryOperatorKind.Logical; } goto default; case BinaryOperatorKind.LogicalBoolOr: if (loweredRight.ConstantValue == ConstantValue.False) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.False) return loweredRight; if (loweredLeft.ConstantValue == ConstantValue.True) return loweredLeft; if (loweredRight.Kind == BoundKind.Local || loweredRight.Kind == BoundKind.Parameter) { operatorKind &= ~BinaryOperatorKind.Logical; } goto default; case BinaryOperatorKind.BoolAnd: if (loweredRight.ConstantValue == ConstantValue.True) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.True) return loweredRight; goto default; case BinaryOperatorKind.BoolOr: if (loweredRight.ConstantValue == ConstantValue.False) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.False) return loweredRight; goto default; case BinaryOperatorKind.BoolEqual: if (loweredLeft.ConstantValue == ConstantValue.True) return loweredRight; if (loweredRight.ConstantValue == ConstantValue.True) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.False) return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredRight, loweredRight.Type); if (loweredRight.ConstantValue == ConstantValue.False) return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredLeft, loweredLeft.Type); goto default; case BinaryOperatorKind.BoolNotEqual: if (loweredLeft.ConstantValue == ConstantValue.False) return loweredRight; if (loweredRight.ConstantValue == ConstantValue.False) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.True) return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredRight, loweredRight.Type); if (loweredRight.ConstantValue == ConstantValue.True) return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredLeft, loweredLeft.Type); goto default; case BinaryOperatorKind.BoolXor: if (loweredLeft.ConstantValue == ConstantValue.False) return loweredRight; if (loweredRight.ConstantValue == ConstantValue.False) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.True) return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredRight, loweredRight.Type); if (loweredRight.ConstantValue == ConstantValue.True) return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredLeft, loweredLeft.Type); goto default; case BinaryOperatorKind.IntLeftShift: case BinaryOperatorKind.UIntLeftShift: case BinaryOperatorKind.IntRightShift: case BinaryOperatorKind.UIntRightShift: return RewriteBuiltInShiftOperation(oldNode, syntax, operatorKind, loweredLeft, loweredRight, type, 0x1F); case BinaryOperatorKind.LongLeftShift: case BinaryOperatorKind.ULongLeftShift: case BinaryOperatorKind.LongRightShift: case BinaryOperatorKind.ULongRightShift: return RewriteBuiltInShiftOperation(oldNode, syntax, operatorKind, loweredLeft, loweredRight, type, 0x3F); case BinaryOperatorKind.DecimalAddition: case BinaryOperatorKind.DecimalSubtraction: case BinaryOperatorKind.DecimalMultiplication: case BinaryOperatorKind.DecimalDivision: case BinaryOperatorKind.DecimalRemainder: case BinaryOperatorKind.DecimalEqual: case BinaryOperatorKind.DecimalNotEqual: case BinaryOperatorKind.DecimalLessThan: case BinaryOperatorKind.DecimalLessThanOrEqual: case BinaryOperatorKind.DecimalGreaterThan: case BinaryOperatorKind.DecimalGreaterThanOrEqual: return RewriteDecimalBinaryOperation(syntax, loweredLeft, loweredRight, operatorKind); case BinaryOperatorKind.PointerAndIntAddition: case BinaryOperatorKind.PointerAndUIntAddition: case BinaryOperatorKind.PointerAndLongAddition: case BinaryOperatorKind.PointerAndULongAddition: case BinaryOperatorKind.PointerAndIntSubtraction: case BinaryOperatorKind.PointerAndUIntSubtraction: case BinaryOperatorKind.PointerAndLongSubtraction: case BinaryOperatorKind.PointerAndULongSubtraction: if (loweredRight.IsDefaultValue()) { return loweredLeft; } return RewritePointerNumericOperator(syntax, operatorKind, loweredLeft, loweredRight, type, isPointerElementAccess, isLeftPointer: true); case BinaryOperatorKind.IntAndPointerAddition: case BinaryOperatorKind.UIntAndPointerAddition: case BinaryOperatorKind.LongAndPointerAddition: case BinaryOperatorKind.ULongAndPointerAddition: if (loweredLeft.IsDefaultValue()) { return loweredRight; } return RewritePointerNumericOperator(syntax, operatorKind, loweredLeft, loweredRight, type, isPointerElementAccess, isLeftPointer: false); case BinaryOperatorKind.PointerSubtraction: return RewritePointerSubtraction(operatorKind, loweredLeft, loweredRight, type); case BinaryOperatorKind.IntAddition: case BinaryOperatorKind.UIntAddition: case BinaryOperatorKind.LongAddition: case BinaryOperatorKind.ULongAddition: if (loweredLeft.IsDefaultValue()) { return loweredRight; } if (loweredRight.IsDefaultValue()) { return loweredLeft; } goto default; case BinaryOperatorKind.IntSubtraction: case BinaryOperatorKind.LongSubtraction: case BinaryOperatorKind.UIntSubtraction: case BinaryOperatorKind.ULongSubtraction: if (loweredRight.IsDefaultValue()) { return loweredLeft; } goto default; case BinaryOperatorKind.IntMultiplication: case BinaryOperatorKind.LongMultiplication: case BinaryOperatorKind.UIntMultiplication: case BinaryOperatorKind.ULongMultiplication: if (loweredLeft.IsDefaultValue()) { return loweredLeft; } if (loweredRight.IsDefaultValue()) { return loweredRight; } if (loweredLeft.ConstantValue?.UInt64Value == 1) { return loweredRight; } if (loweredRight.ConstantValue?.UInt64Value == 1) { return loweredLeft; } goto default; case BinaryOperatorKind.IntGreaterThan: case BinaryOperatorKind.IntLessThanOrEqual: if (loweredLeft.Kind == BoundKind.ArrayLength && loweredRight.IsDefaultValue()) { //array length is never negative var newOp = operatorKind == BinaryOperatorKind.IntGreaterThan ? BinaryOperatorKind.NotEqual : BinaryOperatorKind.Equal; operatorKind &= ~BinaryOperatorKind.OpMask; operatorKind |= newOp; loweredLeft = UnconvertArrayLength((BoundArrayLength)loweredLeft); } goto default; case BinaryOperatorKind.IntLessThan: case BinaryOperatorKind.IntGreaterThanOrEqual: if (loweredRight.Kind == BoundKind.ArrayLength && loweredLeft.IsDefaultValue()) { //array length is never negative var newOp = operatorKind == BinaryOperatorKind.IntLessThan ? BinaryOperatorKind.NotEqual : BinaryOperatorKind.Equal; operatorKind &= ~BinaryOperatorKind.OpMask; operatorKind |= newOp; loweredRight = UnconvertArrayLength((BoundArrayLength)loweredRight); } goto default; case BinaryOperatorKind.IntEqual: case BinaryOperatorKind.IntNotEqual: if (loweredLeft.Kind == BoundKind.ArrayLength && loweredRight.IsDefaultValue()) { loweredLeft = UnconvertArrayLength((BoundArrayLength)loweredLeft); } else if (loweredRight.Kind == BoundKind.ArrayLength && loweredLeft.IsDefaultValue()) { loweredRight = UnconvertArrayLength((BoundArrayLength)loweredRight); } goto default; default: break; } } return (oldNode != null) ? oldNode.Update(operatorKind, loweredLeft, loweredRight, oldNode.ConstantValueOpt, oldNode.MethodOpt, oldNode.ResultKind, type) : new BoundBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, null, null, LookupResultKind.Viable, type); }
public static bool IsShift(this BinaryOperatorKind kind) { BinaryOperatorKind type = kind.Operator(); return(type == BinaryOperatorKind.LeftShift || type == BinaryOperatorKind.RightShift); }
private BoundExpression RewriteLiftedBinaryOperator(CSharpSyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type, MethodSymbol method) { var conditionalLeft = loweredLeft as BoundLoweredConditionalAccess; // NOTE: we could in theory handle side-effecting loweredRight here too // by including it as a part of whenNull, but there is a concern // that it can lead to code duplication var optimize = conditionalLeft != null && !ReadIsSideeffecting(loweredRight) && (conditionalLeft.WhenNullOpt == null || conditionalLeft.WhenNullOpt.IsDefaultValue()); if (optimize) { loweredLeft = conditionalLeft.WhenNotNull; } var result = operatorKind.IsComparison() ? operatorKind.IsUserDefined() ? LowerLiftedUserDefinedComparisonOperator(syntax, operatorKind, loweredLeft, loweredRight, method) : LowerLiftedBuiltInComparisonOperator(syntax, operatorKind, loweredLeft, loweredRight) : LowerLiftedBinaryArithmeticOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method); if (optimize) { BoundExpression whenNullOpt = null; // for all operators null-in means null-out // except for the Equal/NotEqual since null == null ==> true if (operatorKind.Operator() == BinaryOperatorKind.NotEqual || operatorKind.Operator() == BinaryOperatorKind.Equal) { whenNullOpt = RewriteLiftedBinaryOperator(syntax, operatorKind, _factory.Default(loweredLeft.Type), loweredRight, type, method); } result = conditionalLeft.Update( conditionalLeft.Receiver, conditionalLeft.HasValueMethodOpt, whenNotNull: result, whenNullOpt: whenNullOpt, id: conditionalLeft.Id, type: result.Type ); } return result; }
public static ExpressionType ToExpressionType(this BinaryOperatorKind kind, bool isCompoundAssignment) { if (isCompoundAssignment) { switch (kind.Operator()) { case BinaryOperatorKind.Multiplication: return(ExpressionType.MultiplyAssign); case BinaryOperatorKind.Addition: return(ExpressionType.AddAssign); case BinaryOperatorKind.Subtraction: return(ExpressionType.SubtractAssign); case BinaryOperatorKind.Division: return(ExpressionType.DivideAssign); case BinaryOperatorKind.Remainder: return(ExpressionType.ModuloAssign); case BinaryOperatorKind.LeftShift: return(ExpressionType.LeftShiftAssign); case BinaryOperatorKind.RightShift: return(ExpressionType.RightShiftAssign); case BinaryOperatorKind.And: return(ExpressionType.AndAssign); case BinaryOperatorKind.Xor: return(ExpressionType.ExclusiveOrAssign); case BinaryOperatorKind.Or: return(ExpressionType.OrAssign); } } else { switch (kind.Operator()) { case BinaryOperatorKind.Multiplication: return(ExpressionType.Multiply); case BinaryOperatorKind.Addition: return(ExpressionType.Add); case BinaryOperatorKind.Subtraction: return(ExpressionType.Subtract); case BinaryOperatorKind.Division: return(ExpressionType.Divide); case BinaryOperatorKind.Remainder: return(ExpressionType.Modulo); case BinaryOperatorKind.LeftShift: return(ExpressionType.LeftShift); case BinaryOperatorKind.RightShift: return(ExpressionType.RightShift); case BinaryOperatorKind.Equal: return(ExpressionType.Equal); case BinaryOperatorKind.NotEqual: return(ExpressionType.NotEqual); case BinaryOperatorKind.GreaterThan: return(ExpressionType.GreaterThan); case BinaryOperatorKind.LessThan: return(ExpressionType.LessThan); case BinaryOperatorKind.GreaterThanOrEqual: return(ExpressionType.GreaterThanOrEqual); case BinaryOperatorKind.LessThanOrEqual: return(ExpressionType.LessThanOrEqual); case BinaryOperatorKind.And: return(ExpressionType.And); case BinaryOperatorKind.Xor: return(ExpressionType.ExclusiveOr); case BinaryOperatorKind.Or: return(ExpressionType.Or); } } throw ExceptionUtilities.UnexpectedValue(kind.Operator()); }
private BoundExpression TrivialLiftedComparisonOperatorOptimizations( CSharpSyntaxNode syntax, BinaryOperatorKind kind, BoundExpression left, BoundExpression right, MethodSymbol method) { Debug.Assert(left != null); Debug.Assert(right != null); // Optimization #1: if both sides are null then the result // is either true (for equality) or false (for everything else.) bool leftAlwaysNull = NullableNeverHasValue(left); bool rightAlwaysNull = NullableNeverHasValue(right); TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); if (leftAlwaysNull && rightAlwaysNull) { return MakeLiteral(syntax, ConstantValue.Create(kind.Operator() == BinaryOperatorKind.Equal), boolType); } // Optimization #2: If both sides are non-null then we can again eliminate the lifting entirely. BoundExpression leftNonNull = NullableAlwaysHasValue(left); BoundExpression rightNonNull = NullableAlwaysHasValue(right); if (leftNonNull != null && rightNonNull != null) { return MakeBinaryOperator( syntax: syntax, operatorKind: kind.Unlifted(), loweredLeft: leftNonNull, loweredRight: rightNonNull, type: boolType, method: method); } // Optimization #3: If one side is null and the other is definitely not, then we generate the side effects // of the non-null side and result in true (for not-equals) or false (for everything else.) BinaryOperatorKind operatorKind = kind.Operator(); if (leftAlwaysNull && rightNonNull != null || rightAlwaysNull && leftNonNull != null) { BoundExpression result = MakeLiteral(syntax, ConstantValue.Create(operatorKind == BinaryOperatorKind.NotEqual), boolType); BoundExpression nonNull = leftAlwaysNull ? rightNonNull : leftNonNull; if (ReadIsSideeffecting(nonNull)) { result = new BoundSequence( syntax: syntax, locals: ImmutableArray<LocalSymbol>.Empty, sideEffects: ImmutableArray.Create<BoundExpression>(nonNull), value: result, type: boolType); } return result; } // Optimization #4: If one side is null and the other is unknown, then we have three cases: // #4a: If we have x == null then that becomes !x.HasValue. // #4b: If we have x != null then that becomes x.HasValue. // #4c: If we have x OP null then that becomes side effects of x, result in false. if (leftAlwaysNull || rightAlwaysNull) { BoundExpression maybeNull = leftAlwaysNull ? right : left; if (operatorKind == BinaryOperatorKind.Equal || operatorKind == BinaryOperatorKind.NotEqual) { BoundExpression callHasValue = MakeNullableHasValue(syntax, maybeNull); BoundExpression result = operatorKind == BinaryOperatorKind.Equal ? MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, callHasValue, boolType) : callHasValue; return result; } else { BoundExpression falseExpr = MakeBooleanConstant(syntax, operatorKind == BinaryOperatorKind.NotEqual); return _factory.Sequence(maybeNull, falseExpr); } } return null; }
private BoundExpression LowerUserDefinedLogicalOperator( CSharpSyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type, MethodSymbol method) { Debug.Assert((object)method != null); // See comments in method IsValidUserDefinedConditionalLogicalOperator for information // on some subtle aspects of this lowering. // We generate one of: // // x || y --> temp = x; T.true(temp) ? temp : T.|(temp, y); // x && y --> temp = x; T.false(temp) ? temp : T.&(temp, y); // // For the ease of naming locals, we'll assume we're doing an &&. // TODO: We generate every one of these as "temp = x; T.false(temp) ? temp : T.&(temp, y)" even // TODO: when x has no side effects. We can optimize away the temporary if there are no side effects. BoundAssignmentOperator tempAssignment; var boundTemp = factory.StoreToTemp(loweredLeft, out tempAssignment); // T.false(temp) var falseOperatorCall = BoundCall.Synthesized(syntax, null, GetTruthOperator(type, negative: operatorKind.Operator() == BinaryOperatorKind.And), boundTemp); // T.&(temp, y) var andOperatorCall = LowerUserDefinedBinaryOperator(syntax, operatorKind & ~BinaryOperatorKind.Logical, boundTemp, loweredRight, type, method); // T.false(temp) ? temp : T.&(temp, y) BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: falseOperatorCall, rewrittenConsequence: boundTemp, rewrittenAlternative: andOperatorCall, constantValueOpt: null, rewrittenType: type); // temp = x; T.false(temp) ? temp : T.&(temp, y) return new BoundSequence( syntax: syntax, locals: ImmutableArray.Create(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignment), value: conditionalExpression, type: type); }
private BoundExpression LowerLiftedUserDefinedComparisonOperator( CSharpSyntaxNode syntax, BinaryOperatorKind kind, BoundExpression loweredLeft, BoundExpression loweredRight, MethodSymbol method) { // If both sides are null, or neither side is null, then we can do some simple optimizations. BoundExpression optimized = TrivialLiftedComparisonOperatorOptimizations(syntax, kind, loweredLeft, loweredRight, method); if (optimized != null) { return optimized; } // Otherwise, the expression // // x == y // // becomes // // tempX = x; // tempY = y; // result = tempX.HasValue == tempY.HasValue ? // (tempX.HasValue ? // tempX.GetValueOrDefault() == tempY.GetValueOrDefault() : // true) : // false; // // // the expression // // x != y // // becomes // // tempX = x; // tempY = y; // result = tempX.HasValue == tempY.HasValue ? // (tempX.HasValue ? // tempX.GetValueOrDefault() != tempY.GetValueOrDefault() : // false) : // true; // // // For the other comparison operators <, <=, >, >=, // // x OP y // // becomes // // tempX = x; // tempY = y; // result = tempX.HasValue & tempY.HasValue ? // tempX.GetValueOrDefault() OP tempY.GetValueOrDefault() : // false; // // We have not yet optimized the case where we have a known-not-null value on one side, // and an unknown value on the other. In those cases we will still generate a temp, but // we will not generate the call to the unnecessary nullable ctor or to GetValueOrDefault. // Rather, we will generate the value's temp instead of a call to GetValueOrDefault, and generate // literal true for HasValue. The tree construction methods we call will use those constants // to eliminate unnecessary branches. BoundExpression xNonNull = NullableAlwaysHasValue(loweredLeft); BoundExpression yNonNull = NullableAlwaysHasValue(loweredRight); // TODO: (This TODO applies throughout this file, not just to this method.) // TODO: We might be storing a constant to this temporary that we could simply inline. // TODO: (There are other expressions that can be safely moved around other than constants // TODO: as well -- for example a boxing conversion of a constant int to object.) // TODO: Build a better temporary-storage management system that decides whether or not // TODO: to store a temporary. BoundAssignmentOperator tempAssignmentX; BoundLocal boundTempX = _factory.StoreToTemp(xNonNull ?? loweredLeft, out tempAssignmentX); BoundAssignmentOperator tempAssignmentY; BoundLocal boundTempY = _factory.StoreToTemp(yNonNull ?? loweredRight, out tempAssignmentY); BoundExpression callX_GetValueOrDefault = MakeOptimizedGetValueOrDefault(syntax, boundTempX); BoundExpression callY_GetValueOrDefault = MakeOptimizedGetValueOrDefault(syntax, boundTempY); BoundExpression callX_HasValue = MakeOptimizedHasValue(syntax, boundTempX); BoundExpression callY_HasValue = MakeOptimizedHasValue(syntax, boundTempY); // tempx.HasValue == tempy.HasValue BinaryOperatorKind conditionOperator; BinaryOperatorKind operatorKind = kind.Operator(); switch (operatorKind) { case BinaryOperatorKind.Equal: case BinaryOperatorKind.NotEqual: conditionOperator = BinaryOperatorKind.BoolEqual; break; default: conditionOperator = BinaryOperatorKind.BoolAnd; break; } TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression condition = MakeBinaryOperator( syntax: syntax, operatorKind: conditionOperator, loweredLeft: callX_HasValue, loweredRight: callY_HasValue, type: boolType, method: null); // tempX.GetValueOrDefault() OP tempY.GetValueOrDefault() BoundExpression unliftedOp = MakeBinaryOperator( syntax: syntax, operatorKind: kind.Unlifted(), loweredLeft: callX_GetValueOrDefault, loweredRight: callY_GetValueOrDefault, type: boolType, method: method); BoundExpression consequence; if (operatorKind == BinaryOperatorKind.Equal || operatorKind == BinaryOperatorKind.NotEqual) { // tempx.HasValue ? tempX.GetValueOrDefault() == tempY.GetValueOrDefault() : true consequence = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: callX_HasValue, rewrittenConsequence: unliftedOp, rewrittenAlternative: MakeLiteral(syntax, ConstantValue.Create(operatorKind == BinaryOperatorKind.Equal), boolType), constantValueOpt: null, rewrittenType: boolType); } else { // tempX.GetValueOrDefault() OP tempY.GetValueOrDefault() consequence = unliftedOp; } // false BoundExpression alternative = MakeBooleanConstant(syntax, operatorKind == BinaryOperatorKind.NotEqual); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: boolType); return new BoundSequence( syntax: syntax, locals: ImmutableArray.Create<LocalSymbol>(boundTempX.LocalSymbol, boundTempY.LocalSymbol), sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignmentX, tempAssignmentY), value: conditionalExpression, type: boolType); }
internal BinaryOperatorSignature GetSignature(BinaryOperatorKind kind) { var left = LeftType(kind); switch (kind.Operator()) { case BinaryOperatorKind.Multiplication: case BinaryOperatorKind.Division: case BinaryOperatorKind.Subtraction: case BinaryOperatorKind.Remainder: case BinaryOperatorKind.And: case BinaryOperatorKind.Or: case BinaryOperatorKind.Xor: return new BinaryOperatorSignature(kind, left, left, left); case BinaryOperatorKind.Addition: return new BinaryOperatorSignature(kind, LeftType(kind), RightType(kind), ReturnType(kind)); case BinaryOperatorKind.LeftShift: case BinaryOperatorKind.RightShift: TypeSymbol returnType = Compilation.GetSpecialType(SpecialType.System_Int32); if (kind.IsLifted()) { returnType = Compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(returnType); } return new BinaryOperatorSignature(kind, left, returnType, left); case BinaryOperatorKind.Equal: case BinaryOperatorKind.NotEqual: case BinaryOperatorKind.GreaterThan: case BinaryOperatorKind.LessThan: case BinaryOperatorKind.GreaterThanOrEqual: case BinaryOperatorKind.LessThanOrEqual: return new BinaryOperatorSignature(kind, left, left, Compilation.GetSpecialType(SpecialType.System_Boolean)); } return new BinaryOperatorSignature(kind, LeftType(kind), RightType(kind), ReturnType(kind)); }