Changeset View
Standalone View
clang/lib/AST/ExprConstant.cpp
- This file is larger than 256 KB, so syntax highlighting is disabled by default.
Show First 20 Lines • Show All 2,412 Lines • ▼ Show 20 Lines | static bool HandleFloatToIntCast(EvalInfo &Info, const Expr *E, | ||||
Result = APSInt(DestWidth, !DestSigned); | Result = APSInt(DestWidth, !DestSigned); | ||||
bool ignored; | bool ignored; | ||||
if (Value.convertToInteger(Result, llvm::APFloat::rmTowardZero, &ignored) | if (Value.convertToInteger(Result, llvm::APFloat::rmTowardZero, &ignored) | ||||
& APFloat::opInvalidOp) | & APFloat::opInvalidOp) | ||||
return HandleOverflow(Info, E, Value, DestType); | return HandleOverflow(Info, E, Value, DestType); | ||||
return true; | return true; | ||||
} | } | ||||
/// Get rounding mode used for evaluation of the specified expression. | |||||
/// \param[out] DynamicRM Is set to true is the requested rounding mode is | |||||
/// dynamic. | |||||
/// If rounding mode is unknown at compile time, still try to evaluate the | |||||
/// expression. If the result is exact, it does not depend on rounding mode. | |||||
/// So return "tonearest" mode instead of "dynamic". | |||||
static llvm::RoundingMode getActiveRoundingMode(EvalInfo &Info, const Expr *E, | |||||
bool &DynamicRM) { | |||||
llvm::RoundingMode RM = | |||||
E->getFPFeaturesInEffect(Info.Ctx.getLangOpts()).getRoundingMode(); | |||||
DynamicRM = (RM == llvm::RoundingMode::Dynamic); | |||||
if (DynamicRM) | |||||
RM = llvm::RoundingMode::NearestTiesToEven; | |||||
return RM; | |||||
} | |||||
/// Check if the given evaluation result is allowed for constant evaluation. | |||||
static bool checkFloatingPointResult(EvalInfo &Info, const Expr *E, | |||||
mibintc: If the result is this false, shall this routine create a Note diag explaining the false result? | |||||
It makes sense. Added respective diagnostics. sepavloff: It makes sense. Added respective diagnostics. | |||||
APFloat::opStatus St) { | |||||
FPOptions FPO = E->getFPFeaturesInEffect(Info.Ctx.getLangOpts()); | |||||
if ((St & APFloat::opInexact) && | |||||
FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) { | |||||
// Inexact result means that it depends on rounding mode. If the requested | |||||
// mode is dynamic, the evaluation cannot be made in compile time. | |||||
Info.FFDiag(E); | |||||
return false; | |||||
} | |||||
if (St & APFloat::opStatus::opInvalidOp) { | |||||
// There is no usefully definable result. | |||||
Info.FFDiag(E); | |||||
return false; | |||||
} | |||||
// FIXME: if: | |||||
// - evaluation triggered other FP exception, and | |||||
// - exception mode is not "ignore", and | |||||
// - the expression being evaluated is not a part of global variable | |||||
// initializer, | |||||
// the evaluation probably need to be rejected. | |||||
return true; | |||||
} | |||||
rjmccallUnsubmitted Not Done ReplyInline ActionsThanks, these look good. rjmccall: Thanks, these look good. | |||||
static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E, | static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E, | ||||
QualType SrcType, QualType DestType, | QualType SrcType, QualType DestType, | ||||
APFloat &Result) { | APFloat &Result) { | ||||
bool DynamicRM; | |||||
llvm::RoundingMode RM = getActiveRoundingMode(Info, E, DynamicRM); | |||||
APFloat::opStatus St; | |||||
APFloat Value = Result; | APFloat Value = Result; | ||||
Not Done ReplyInline ActionsI think the options really need to be passed in or else correctness is somewhat doomed here. For example, the call to CompoundAssignSubobjectHandler needs to propagate this down from the operator expression. rjmccall: I think the options really need to be passed in or else correctness is somewhat doomed here. | |||||
It is guaranteed by the way AST is built, no? As FP options may be changed only by pragmas and the pragmas can be specified only at file or block level, all sub-expression are evaluated at the same options. sepavloff: It is guaranteed by the way AST is built, no?
As FP options may be changed only by pragmas and… | |||||
Not Done ReplyInline ActionsYes, but you can't actually reliably recover those settings from E unless you're sure E is one of a few kinds of expression. The concern is that E might end up being some closely-related expression that isn't actually the expression that carries the current settings, and then we'll fall back on using the global defaults. It's much more correct-by-construction to pass the settings down from the caller based on the caller's static knowledge of which expression is under consideration, and I think you'll see that that's pretty straightforward in practice. rjmccall: Yes, but you can't actually reliably recover those settings from E unless you're sure E is one… | |||||
This is a peculiarity of CompoundAssignOperator, which itself makes conversion, without CastExpr. I added assert to ensure that other nodes cannot appear here. Also some tests with conversion in CompoundAssignOperator were added. sepavloff: This is a peculiarity of `CompoundAssignOperator`, which itself makes conversion, without… | |||||
Not Done ReplyInline ActionsAlright, well, I don't understand the reluctance to pass this down, but I won't insist. LGTM. rjmccall: Alright, well, I don't understand the reluctance to pass this down, but I won't insist. LGTM. | |||||
bool ignored; | bool ignored; | ||||
Result.convert(Info.Ctx.getFloatTypeSemantics(DestType), | St = Result.convert(Info.Ctx.getFloatTypeSemantics(DestType), RM, &ignored); | ||||
APFloat::rmNearestTiesToEven, &ignored); | return checkFloatingPointResult(Info, E, St); | ||||
return true; | |||||
} | } | ||||
static APSInt HandleIntToIntCast(EvalInfo &Info, const Expr *E, | static APSInt HandleIntToIntCast(EvalInfo &Info, const Expr *E, | ||||
QualType DestType, QualType SrcType, | QualType DestType, QualType SrcType, | ||||
const APSInt &Value) { | const APSInt &Value) { | ||||
unsigned DestWidth = Info.Ctx.getIntWidth(DestType); | unsigned DestWidth = Info.Ctx.getIntWidth(DestType); | ||||
// Figure out if this is a truncate, extend or noop cast. | // Figure out if this is a truncate, extend or noop cast. | ||||
// If the input is signed, do a sign extend, noop, or truncate. | // If the input is signed, do a sign extend, noop, or truncate. | ||||
▲ Show 20 Lines • Show All 205 Lines • ▼ Show 20 Lines | static bool handleIntIntBinOp(EvalInfo &Info, const Expr *E, const APSInt &LHS, | ||||
case BO_EQ: Result = LHS == RHS; return true; | case BO_EQ: Result = LHS == RHS; return true; | ||||
case BO_NE: Result = LHS != RHS; return true; | case BO_NE: Result = LHS != RHS; return true; | ||||
case BO_Cmp: | case BO_Cmp: | ||||
llvm_unreachable("BO_Cmp should be handled elsewhere"); | llvm_unreachable("BO_Cmp should be handled elsewhere"); | ||||
} | } | ||||
} | } | ||||
/// Perform the given binary floating-point operation, in-place, on LHS. | /// Perform the given binary floating-point operation, in-place, on LHS. | ||||
static bool handleFloatFloatBinOp(EvalInfo &Info, const Expr *E, | static bool handleFloatFloatBinOp(EvalInfo &Info, const BinaryOperator *E, | ||||
APFloat &LHS, BinaryOperatorKind Opcode, | APFloat &LHS, BinaryOperatorKind Opcode, | ||||
const APFloat &RHS) { | const APFloat &RHS) { | ||||
bool DynamicRM; | |||||
llvm::RoundingMode RM = getActiveRoundingMode(Info, E, DynamicRM); | |||||
APFloat::opStatus St; | |||||
switch (Opcode) { | switch (Opcode) { | ||||
default: | default: | ||||
Info.FFDiag(E); | Info.FFDiag(E); | ||||
return false; | return false; | ||||
Feels like you should have a helper function like getActiveRoundingMode(Info, FPFeatures) rjmccall: Feels like you should have a helper function like `getActiveRoundingMode(Info, FPFeatures)` | |||||
It required implementation of statically polymorphic method Expr::getFPFeaturesInEffect but finally code looks better. sepavloff: It required implementation of statically polymorphic method `Expr::getFPFeaturesInEffect` but… | |||||
case BO_Mul: | case BO_Mul: | ||||
LHS.multiply(RHS, APFloat::rmNearestTiesToEven); | St = LHS.multiply(RHS, RM); | ||||
break; | break; | ||||
case BO_Add: | case BO_Add: | ||||
LHS.add(RHS, APFloat::rmNearestTiesToEven); | St = LHS.add(RHS, RM); | ||||
break; | break; | ||||
case BO_Sub: | case BO_Sub: | ||||
LHS.subtract(RHS, APFloat::rmNearestTiesToEven); | St = LHS.subtract(RHS, RM); | ||||
break; | break; | ||||
case BO_Div: | case BO_Div: | ||||
// [expr.mul]p4: | // [expr.mul]p4: | ||||
// If the second operand of / or % is zero the behavior is undefined. | // If the second operand of / or % is zero the behavior is undefined. | ||||
if (RHS.isZero()) | if (RHS.isZero()) | ||||
Info.CCEDiag(E, diag::note_expr_divide_by_zero); | Info.CCEDiag(E, diag::note_expr_divide_by_zero); | ||||
LHS.divide(RHS, APFloat::rmNearestTiesToEven); | St = LHS.divide(RHS, RM); | ||||
break; | break; | ||||
} | } | ||||
// [expr.pre]p4: | // [expr.pre]p4: | ||||
// If during the evaluation of an expression, the result is not | // If during the evaluation of an expression, the result is not | ||||
// mathematically defined [...], the behavior is undefined. | // mathematically defined [...], the behavior is undefined. | ||||
// FIXME: C++ rules require us to not conform to IEEE 754 here. | // FIXME: C++ rules require us to not conform to IEEE 754 here. | ||||
if (LHS.isNaN()) { | if (LHS.isNaN()) { | ||||
Info.CCEDiag(E, diag::note_constexpr_float_arithmetic) << LHS.isNaN(); | Info.CCEDiag(E, diag::note_constexpr_float_arithmetic) << LHS.isNaN(); | ||||
return Info.noteUndefinedBehavior(); | return Info.noteUndefinedBehavior(); | ||||
} | } | ||||
return true; | |||||
return checkFloatingPointResult(Info, E, St); | |||||
} | } | ||||
Not Done ReplyInline ActionsIs this processing something you can extract meaningfully into a common function that works with an opStatus, so that you can just write if (!checkFloatingPointFailure(Info, E, status, fpFeatures)) return false; in all these places? rjmccall: Is this processing something you can extract meaningfully into a common function that works… | |||||
Good idea, thank you. I used a bit different name to have positive semantics. sepavloff: Good idea, thank you. I used a bit different name to have positive semantics. | |||||
static bool handleLogicalOpForVector(const APInt &LHSValue, | static bool handleLogicalOpForVector(const APInt &LHSValue, | ||||
BinaryOperatorKind Opcode, | BinaryOperatorKind Opcode, | ||||
const APInt &RHSValue, APInt &Result) { | const APInt &RHSValue, APInt &Result) { | ||||
bool LHS = (LHSValue != 0); | bool LHS = (LHSValue != 0); | ||||
bool RHS = (RHSValue != 0); | bool RHS = (RHSValue != 0); | ||||
if (Opcode == BO_LAnd) | if (Opcode == BO_LAnd) | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | if (LHSValue.getKind() == APValue::Int) | ||||
return handleCompareOpForVectorHelper(LHSValue.getInt(), Opcode, | return handleCompareOpForVectorHelper(LHSValue.getInt(), Opcode, | ||||
RHSValue.getInt(), Result); | RHSValue.getInt(), Result); | ||||
assert(LHSValue.getKind() == APValue::Float && "Should be no other options"); | assert(LHSValue.getKind() == APValue::Float && "Should be no other options"); | ||||
return handleCompareOpForVectorHelper(LHSValue.getFloat(), Opcode, | return handleCompareOpForVectorHelper(LHSValue.getFloat(), Opcode, | ||||
RHSValue.getFloat(), Result); | RHSValue.getFloat(), Result); | ||||
} | } | ||||
// Perform binary operations for vector types, in place on the LHS. | // Perform binary operations for vector types, in place on the LHS. | ||||
static bool handleVectorVectorBinOp(EvalInfo &Info, const Expr *E, | static bool handleVectorVectorBinOp(EvalInfo &Info, const BinaryOperator *E, | ||||
BinaryOperatorKind Opcode, | BinaryOperatorKind Opcode, | ||||
APValue &LHSValue, | APValue &LHSValue, | ||||
const APValue &RHSValue) { | const APValue &RHSValue) { | ||||
assert(Opcode != BO_PtrMemD && Opcode != BO_PtrMemI && | assert(Opcode != BO_PtrMemD && Opcode != BO_PtrMemI && | ||||
"Operation not supported on vector types"); | "Operation not supported on vector types"); | ||||
const auto *VT = E->getType()->castAs<VectorType>(); | const auto *VT = E->getType()->castAs<VectorType>(); | ||||
unsigned NumElements = VT->getNumElements(); | unsigned NumElements = VT->getNumElements(); | ||||
▲ Show 20 Lines • Show All 1,297 Lines • ▼ Show 20 Lines | static bool handleAssignment(EvalInfo &Info, const Expr *E, const LValue &LVal, | ||||
CompleteObject Obj = findCompleteObject(Info, E, AK_Assign, LVal, LValType); | CompleteObject Obj = findCompleteObject(Info, E, AK_Assign, LVal, LValType); | ||||
return Obj && modifySubobject(Info, E, Obj, LVal.Designator, Val); | return Obj && modifySubobject(Info, E, Obj, LVal.Designator, Val); | ||||
} | } | ||||
namespace { | namespace { | ||||
struct CompoundAssignSubobjectHandler { | struct CompoundAssignSubobjectHandler { | ||||
EvalInfo &Info; | EvalInfo &Info; | ||||
const Expr *E; | const CompoundAssignOperator *E; | ||||
QualType PromotedLHSType; | QualType PromotedLHSType; | ||||
BinaryOperatorKind Opcode; | BinaryOperatorKind Opcode; | ||||
const APValue &RHS; | const APValue &RHS; | ||||
static const AccessKinds AccessKind = AK_Assign; | static const AccessKinds AccessKind = AK_Assign; | ||||
typedef bool result_type; | typedef bool result_type; | ||||
▲ Show 20 Lines • Show All 103 Lines • ▼ Show 20 Lines | bool foundPointer(APValue &Subobj, QualType SubobjType) { | ||||
return true; | return true; | ||||
} | } | ||||
}; | }; | ||||
} // end anonymous namespace | } // end anonymous namespace | ||||
const AccessKinds CompoundAssignSubobjectHandler::AccessKind; | const AccessKinds CompoundAssignSubobjectHandler::AccessKind; | ||||
/// Perform a compound assignment of LVal <op>= RVal. | /// Perform a compound assignment of LVal <op>= RVal. | ||||
static bool handleCompoundAssignment( | static bool handleCompoundAssignment(EvalInfo &Info, | ||||
EvalInfo &Info, const Expr *E, | const CompoundAssignOperator *E, | ||||
const LValue &LVal, QualType LValType, QualType PromotedLValType, | const LValue &LVal, QualType LValType, | ||||
BinaryOperatorKind Opcode, const APValue &RVal) { | QualType PromotedLValType, | ||||
BinaryOperatorKind Opcode, | |||||
const APValue &RVal) { | |||||
if (LVal.Designator.Invalid) | if (LVal.Designator.Invalid) | ||||
return false; | return false; | ||||
if (!Info.getLangOpts().CPlusPlus14) { | if (!Info.getLangOpts().CPlusPlus14) { | ||||
Info.FFDiag(E); | Info.FFDiag(E); | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 11,058 Lines • Show Last 20 Lines |
If the result is this false, shall this routine create a Note diag explaining the false result?