private TypeSymbol LiftedType(BinaryOperatorKind kind) { Debug.Assert(kind.IsLifted()); var nullable = _compilation.GetSpecialType(SpecialType.core_Option_T); switch (kind.OperandTypes()) { case BinaryOperatorKind.Int32: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Int32))); case BinaryOperatorKind.UInt32: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_UInt32))); case BinaryOperatorKind.Int64: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Int64))); case BinaryOperatorKind.UInt64: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_UInt64))); case BinaryOperatorKind.Float32: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Float32))); case BinaryOperatorKind.Float64: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Float64))); case BinaryOperatorKind.Int: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Int))); case BinaryOperatorKind.UInt: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_UInt))); case BinaryOperatorKind.Bool: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Boolean))); } Debug.Assert(false, "Bad operator kind in lifted type"); return(null); }
private TypeSymbol LiftedType(BinaryOperatorKind kind) { Debug.Assert(kind.IsLifted()); var nullable = Compilation.GetSpecialType(SpecialType.System_Nullable_T); switch (kind.OperandTypes()) { case BinaryOperatorKind.Int: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Int32))); case BinaryOperatorKind.UInt: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_UInt32))); case BinaryOperatorKind.Long: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Int64))); case BinaryOperatorKind.ULong: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_UInt64))); case BinaryOperatorKind.Float: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Single))); case BinaryOperatorKind.Double: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Double))); case BinaryOperatorKind.Decimal: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Decimal))); case BinaryOperatorKind.Bool: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Boolean))); } Debug.Assert(false, "Bad operator kind in lifted type"); return(null); }
private BoundExpression VisitBinaryOperator(BinaryOperatorKind opKind, MethodSymbol methodOpt, TypeSymbol type, BoundExpression left, BoundExpression right) { bool isChecked, isLifted, requiresLifted; string opName = GetBinaryOperatorName(opKind, out isChecked, out isLifted, out requiresLifted); // Fix up the null value for a nullable comparison vs null if ((object)left.Type == null && left.IsLiteralNull()) { left = _bound.Default(right.Type); } if ((object)right.Type == null && right.IsLiteralNull()) { right = _bound.Default(left.Type); } // Enums are handled as per their promoted underlying type switch (opKind.OperandTypes()) { case BinaryOperatorKind.EnumAndUnderlying: case BinaryOperatorKind.UnderlyingAndEnum: case BinaryOperatorKind.Enum: { var enumOperand = (opKind.OperandTypes() == BinaryOperatorKind.UnderlyingAndEnum) ? right : left; var promotedType = PromotedType(enumOperand.Type.StrippedType().GetEnumUnderlyingType()); if (opKind.IsLifted()) { promotedType = _nullableType.Construct(promotedType); } var loweredLeft = VisitAndPromoteEnumOperand(left, promotedType, isChecked); var loweredRight = VisitAndPromoteEnumOperand(right, promotedType, isChecked); var result = MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight); return(Demote(result, type, isChecked)); } default: { var loweredLeft = Visit(left); var loweredRight = Visit(right); return(MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight)); } } }
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 TypeSymbol RightType(BinaryOperatorKind kind) { if (kind.IsLifted()) { return(LiftedType(kind)); } else { switch (kind.OperandTypes()) { case BinaryOperatorKind.Int32: return(_compilation.GetSpecialType(SpecialType.System_Int32)); case BinaryOperatorKind.UInt32: return(_compilation.GetSpecialType(SpecialType.System_UInt32)); case BinaryOperatorKind.Int64: return(_compilation.GetSpecialType(SpecialType.System_Int64)); case BinaryOperatorKind.UInt64: return(_compilation.GetSpecialType(SpecialType.System_UInt64)); case BinaryOperatorKind.Float32: return(_compilation.GetSpecialType(SpecialType.System_Float32)); case BinaryOperatorKind.Float64: return(_compilation.GetSpecialType(SpecialType.System_Float64)); case BinaryOperatorKind.Int: return(_compilation.GetSpecialType(SpecialType.System_Int)); case BinaryOperatorKind.UInt: return(_compilation.GetSpecialType(SpecialType.System_UInt)); case BinaryOperatorKind.Bool: return(_compilation.GetSpecialType(SpecialType.System_Boolean)); case BinaryOperatorKind.ObjectAndString: case BinaryOperatorKind.String: return(_compilation.GetSpecialType(SpecialType.System_String)); case BinaryOperatorKind.StringAndObject: case BinaryOperatorKind.Object: return(_compilation.GetSpecialType(SpecialType.System_Object)); } } Debug.Assert(false, "Bad operator kind in right type"); return(null); }
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))); }
private TypeSymbol LeftType(BinaryOperatorKind kind) { if (kind.IsLifted()) { return(LiftedType(kind)); } else { switch (kind.OperandTypes()) { case BinaryOperatorKind.Int: return(Compilation.GetSpecialType(SpecialType.System_Int32)); case BinaryOperatorKind.UInt: return(Compilation.GetSpecialType(SpecialType.System_UInt32)); case BinaryOperatorKind.Long: return(Compilation.GetSpecialType(SpecialType.System_Int64)); case BinaryOperatorKind.ULong: return(Compilation.GetSpecialType(SpecialType.System_UInt64)); case BinaryOperatorKind.Float: return(Compilation.GetSpecialType(SpecialType.System_Single)); case BinaryOperatorKind.Double: return(Compilation.GetSpecialType(SpecialType.System_Double)); case BinaryOperatorKind.Decimal: return(Compilation.GetSpecialType(SpecialType.System_Decimal)); case BinaryOperatorKind.Bool: return(Compilation.GetSpecialType(SpecialType.System_Boolean)); case BinaryOperatorKind.ObjectAndString: case BinaryOperatorKind.Object: return(Compilation.GetSpecialType(SpecialType.System_Object)); case BinaryOperatorKind.String: case BinaryOperatorKind.StringAndObject: return(Compilation.GetSpecialType(SpecialType.System_String)); } } Debug.Assert(false, "Bad operator kind in left type"); return(null); }
private BoundExpression LowerUserDefinedBinaryOperator( CSharpSyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type, MethodSymbol method) { Debug.Assert(!operatorKind.IsLogical()); if (operatorKind.IsLifted()) { return RewriteLiftedBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method); } // Otherwise, nothing special here. Debug.Assert((object)method != null); Debug.Assert(method.ReturnType == type); return BoundCall.Synthesized(syntax, null, method, loweredLeft, loweredRight); }
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); }
private BoundExpression MakeBuiltInIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement) { BoundExpression result; // If we have a built-in increment or decrement then things get a bit trickier. Suppose for example we have // a user-defined conversion from X to short and from short to X, but no user-defined increment operator on // X. The increment portion of "++x" is then: (X)(short)((int)(short)x + 1). That is, first x must be // converted to short via an implicit user- defined conversion, then to int via an implicit numeric // conversion, then the addition is performed in integers. The resulting integer is converted back to short, // and then the short is converted to X. // This is the input and output type of the unary increment operator we're going to call. // That is, "short" in the example above. TypeSymbol unaryOperandType = GetUnaryOperatorType(node); // This is the kind of binary operator that we're going to realize the unary operator // as. That is, "int + int --> int" in the example above. BinaryOperatorKind binaryOperatorKind = GetCorrespondingBinaryOperator(node); binaryOperatorKind |= IsIncrement(node) ? BinaryOperatorKind.Addition : BinaryOperatorKind.Subtraction; // The "1" in the example above. ConstantValue constantOne = GetConstantOneForBinOp(binaryOperatorKind); Debug.Assert(constantOne != null); Debug.Assert(constantOne.SpecialType != SpecialType.None); Debug.Assert(binaryOperatorKind.OperandTypes() != 0); // The input/output type of the binary operand. "int" in the example. TypeSymbol binaryOperandType = _compilation.GetSpecialType(constantOne.SpecialType); // 1 BoundExpression boundOne = MakeLiteral( syntax: node.Syntax, constantValue: constantOne, type: binaryOperandType); if (binaryOperatorKind.IsLifted()) { binaryOperandType = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(binaryOperandType); MethodSymbol ctor = UnsafeGetNullableMethod(node.Syntax, binaryOperandType, SpecialMember.System_Nullable_T__ctor); boundOne = new BoundObjectCreationExpression(node.Syntax, ctor, boundOne); } // Now we construct the other operand to the binary addition. We start with just plain "x". BoundExpression binaryOperand = rewrittenValueToIncrement; bool @checked = node.OperatorKind.IsChecked(); // If we need to make a conversion from the original operand type to the operand type of the // underlying increment operation, do it now. if (!node.OperandConversion.IsIdentity) { // (short)x binaryOperand = MakeConversionNode( syntax: node.Syntax, rewrittenOperand: binaryOperand, conversion: node.OperandConversion, rewrittenType: unaryOperandType, @checked: @checked); } // Early-out for pointer increment - we don't need to convert the operands to a common type. if (node.OperatorKind.OperandTypes() == UnaryOperatorKind.Pointer) { Debug.Assert(binaryOperatorKind.OperandTypes() == BinaryOperatorKind.PointerAndInt); Debug.Assert(binaryOperand.Type.IsPointerType()); Debug.Assert(boundOne.Type.SpecialType == SpecialType.System_Int32); return(MakeBinaryOperator(node.Syntax, binaryOperatorKind, binaryOperand, boundOne, binaryOperand.Type, method: null)); } // If we need to make a conversion from the unary operator type to the binary operator type, // do it now. // (int)(short)x binaryOperand = MakeConversionNode(binaryOperand, binaryOperandType, @checked); // Perform the addition. // (int)(short)x + 1 BoundExpression binOp; if (unaryOperandType.SpecialType == SpecialType.System_Decimal) { binOp = MakeDecimalIncDecOperator(node.Syntax, binaryOperatorKind, binaryOperand); } else if (unaryOperandType.IsNullableType() && unaryOperandType.GetNullableUnderlyingType().SpecialType == SpecialType.System_Decimal) { binOp = MakeLiftedDecimalIncDecOperator(node.Syntax, binaryOperatorKind, binaryOperand); } else { binOp = MakeBinaryOperator(node.Syntax, binaryOperatorKind, binaryOperand, boundOne, binaryOperandType, method: null); } // Generate the conversion back to the type of the unary operator. // (short)((int)(short)x + 1) result = MakeConversionNode(binOp, unaryOperandType, @checked); return(result); }
private BoundExpression VisitBinaryOperator(BinaryOperatorKind opKind, MethodSymbol methodOpt, TypeSymbol type, BoundExpression left, BoundExpression right) { bool isChecked, isLifted, requiresLifted; string opName = GetBinaryOperatorName(opKind, out isChecked, out isLifted, out requiresLifted); // Fix up the null value for a nullable comparison vs null if ((object)left.Type == null && left.IsLiteralNull()) { left = _bound.Default(right.Type); } if ((object)right.Type == null && right.IsLiteralNull()) { right = _bound.Default(left.Type); } var loweredLeft = Visit(left); var loweredRight = Visit(right); // Enums are handled as per their promoted underlying type switch (opKind.OperandTypes()) { case BinaryOperatorKind.Enum: case BinaryOperatorKind.EnumAndUnderlying: case BinaryOperatorKind.UnderlyingAndEnum: { var enumOperand = (opKind.OperandTypes() == BinaryOperatorKind.UnderlyingAndEnum) ? right : left; var promotedType = PromotedType(enumOperand.Type.StrippedType().GetEnumUnderlyingType()); if (opKind.IsLifted()) { promotedType = _nullableType.Construct(promotedType); } loweredLeft = PromoteEnumOperand(left, loweredLeft, promotedType, isChecked); loweredRight = PromoteEnumOperand(right, loweredRight, promotedType, isChecked); var result = MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight); return Demote(result, type, isChecked); } default: return MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight); } }
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; }
// Returns null if the operator can't be evaluated at compile time. private ConstantValue FoldBinaryOperator( CSharpSyntaxNode syntax, BinaryOperatorKind kind, BoundExpression left, BoundExpression right, SpecialType resultType, DiagnosticBag diagnostics, ref int compoundStringLength) { Debug.Assert(left != null); Debug.Assert(right != null); if (left.HasAnyErrors || right.HasAnyErrors) { return null; } // SPEC VIOLATION: see method definition for details ConstantValue nullableEqualityResult = TryFoldingNullableEquality(kind, left, right); if (nullableEqualityResult != null) { return nullableEqualityResult; } var valueLeft = left.ConstantValue; var valueRight = right.ConstantValue; if (valueLeft == null || valueRight == null) { return null; } if (valueLeft.IsBad || valueRight.IsBad) { return ConstantValue.Bad; } if (kind.IsEnum() && !kind.IsLifted()) { return FoldEnumBinaryOperator(syntax, kind, left, right, diagnostics); } // Divisions by zero on integral types and decimal always fail even in an unchecked context. if (IsDivisionByZero(kind, valueRight)) { Error(diagnostics, ErrorCode.ERR_IntDivByZero, syntax); return ConstantValue.Bad; } object newValue = null; // Certain binary operations never fail; bool & bool, for example. If we are in one of those // cases, simply fold the operation and return. // // Although remainder and division always overflow at runtime with arguments int.MinValue/long.MinValue and -1 // (regardless of checked context) the constant folding behavior is different. // Remainder never overflows at compile time while division does. newValue = FoldNeverOverflowBinaryOperators(kind, valueLeft, valueRight); if (newValue != null) { return ConstantValue.Create(newValue, resultType); } ConstantValue concatResult = FoldStringConcatenation(kind, valueLeft, valueRight, ref compoundStringLength); if (concatResult != null) { if (concatResult.IsBad) { Error(diagnostics, ErrorCode.ERR_ConstantStringTooLong, syntax); } return concatResult; } // Certain binary operations always fail if they overflow even when in an unchecked context; // decimal + decimal, for example. If we are in one of those cases, make the attempt. If it // succeeds, return the result. If not, give a compile-time error regardless of context. try { newValue = FoldDecimalBinaryOperators(kind, valueLeft, valueRight); } catch (OverflowException) { Error(diagnostics, ErrorCode.ERR_DecConstError, syntax); return ConstantValue.Bad; } if (newValue != null) { return ConstantValue.Create(newValue, resultType); } if (CheckOverflowAtCompileTime) { try { newValue = FoldCheckedIntegralBinaryOperator(kind, valueLeft, valueRight); } catch (OverflowException) { Error(diagnostics, ErrorCode.ERR_CheckedOverflow, syntax); return ConstantValue.Bad; } } else { newValue = FoldUncheckedIntegralBinaryOperator(kind, valueLeft, valueRight); } if (newValue != null) { return ConstantValue.Create(newValue, resultType); } 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 BoundExpression LowerUserDefinedBinaryOperator( CSharpSyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type, MethodSymbol method) { if (operatorKind.IsLogical()) { // Yes, we could have a lifted, logical, user-defined operator: // // struct C { // public static C operator &(C x, C y) {...} // public static bool operator true(C? c) { ... } // public static bool operator false(C? c) { ... } // } // // If we have C? q, r and we say q && r then this gets bound as // C? tempQ = q ; // C.false(tempQ) ? // tempQ : // ( // C? tempR = r ; // tempQ.HasValue & tempR.HasValue ? // new C?(C.&(tempQ.GetValueOrDefault(), tempR.GetValueOrDefault())) : // default C?() // ) // // Note that the native compiler does not allow q && r. However, the native compiler // *does* allow q && r if C is defined as: // // struct C { // public static C? operator &(C? x, C? y) {...} // public static bool operator true(C? c) { ... } // public static bool operator false(C? c) { ... } // } // // It seems unusual and wrong that an & operator should be allowed to become // a && operator if there is a "manually lifted" operator in source, but not // if there is a "synthesized" lifted operator. Roslyn fixes this bug. // // Anyway, in this case we must lower this to its non-logical form, and then // lower the interior of that to its non-lifted form. return LowerUserDefinedLogicalOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method); } else if (operatorKind.IsLifted()) { if (operatorKind.IsComparison()) { return LowerLiftedUserDefinedComparisonOperator(syntax, operatorKind, loweredLeft, loweredRight, method); } else { return LowerLiftedBinaryArithmeticOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method); } } // Otherwise, nothing special here. Debug.Assert((object)method != null); Debug.Assert(method.ReturnType == type); return BoundCall.Synthesized(syntax, null, method, loweredLeft, loweredRight); }
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)); }
private TypeSymbol ReturnType(BinaryOperatorKind kind) { if (kind.IsLifted()) { return LiftedType(kind); } else { switch (kind.OperandTypes()) { case BinaryOperatorKind.Int: return Compilation.GetSpecialType(SpecialType.System_Int32); case BinaryOperatorKind.UInt: return Compilation.GetSpecialType(SpecialType.System_UInt32); case BinaryOperatorKind.Long: return Compilation.GetSpecialType(SpecialType.System_Int64); case BinaryOperatorKind.ULong: return Compilation.GetSpecialType(SpecialType.System_UInt64); case BinaryOperatorKind.Float: return Compilation.GetSpecialType(SpecialType.System_Single); case BinaryOperatorKind.Double: return Compilation.GetSpecialType(SpecialType.System_Double); case BinaryOperatorKind.Decimal: return Compilation.GetSpecialType(SpecialType.System_Decimal); case BinaryOperatorKind.Bool: return Compilation.GetSpecialType(SpecialType.System_Boolean); case BinaryOperatorKind.Object: return Compilation.GetSpecialType(SpecialType.System_Object); case BinaryOperatorKind.ObjectAndString: case BinaryOperatorKind.StringAndObject: case BinaryOperatorKind.String: return Compilation.GetSpecialType(SpecialType.System_String); } } Debug.Assert(false, "Bad operator kind in return type"); return null; }
private TypeSymbol LiftedType(BinaryOperatorKind kind) { Debug.Assert(kind.IsLifted()); var nullable = Compilation.GetSpecialType(SpecialType.System_Nullable_T); switch (kind.OperandTypes()) { case BinaryOperatorKind.Int: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Int32)); case BinaryOperatorKind.UInt: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_UInt32)); case BinaryOperatorKind.Long: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Int64)); case BinaryOperatorKind.ULong: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_UInt64)); case BinaryOperatorKind.Float: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Single)); case BinaryOperatorKind.Double: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Double)); case BinaryOperatorKind.Decimal: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Decimal)); case BinaryOperatorKind.Bool: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Boolean)); } Debug.Assert(false, "Bad operator kind in lifted type"); return null; }