[clang][Interp] Check floating results for NaNs

Differential Revision: https://reviews.llvm.org/D156506
This commit is contained in:
Timm Bäder 2023-07-28 08:07:51 +02:00
parent 53602e6193
commit 4b5fe9c42d
3 changed files with 38 additions and 10 deletions

View File

@ -495,13 +495,25 @@ bool CheckPotentialReinterpretCast(InterpState &S, CodePtr OpPC,
return false;
}
bool CheckFloatResult(InterpState &S, CodePtr OpPC, APFloat::opStatus Status) {
bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
APFloat::opStatus Status) {
const SourceInfo &E = S.Current->getSource(OpPC);
// [expr.pre]p4:
// If during the evaluation of an expression, the result is not
// mathematically defined [...], the behavior is undefined.
// FIXME: C++ rules require us to not conform to IEEE 754 here.
if (Result.isNan()) {
S.CCEDiag(E, diag::note_constexpr_float_arithmetic)
<< /*NaN=*/true << S.Current->getRange(OpPC);
return S.noteUndefinedBehavior();
}
// In a constant context, assume that any dynamic rounding mode or FP
// exception state matches the default floating-point environment.
if (S.inConstantContext())
return true;
const SourceInfo &E = S.Current->getSource(OpPC);
FPOptions FPO = E.asExpr()->getFPFeaturesInEffect(S.Ctx.getLangOpts());
if ((Status & APFloat::opInexact) &&

View File

@ -172,7 +172,8 @@ bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) {
/// Checks if the result of a floating-point operation is valid
/// in the current context.
bool CheckFloatResult(InterpState &S, CodePtr OpPC, APFloat::opStatus Status);
bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
APFloat::opStatus Status);
/// Interpreter entry point.
bool Interpret(InterpState &S, APValue &Result);
@ -304,7 +305,7 @@ inline bool Addf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
Floating Result;
auto Status = Floating::add(LHS, RHS, RM, &Result);
S.Stk.push<Floating>(Result);
return CheckFloatResult(S, OpPC, Status);
return CheckFloatResult(S, OpPC, Result, Status);
}
template <PrimType Name, class T = typename PrimConv<Name>::T>
@ -322,7 +323,7 @@ inline bool Subf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
Floating Result;
auto Status = Floating::sub(LHS, RHS, RM, &Result);
S.Stk.push<Floating>(Result);
return CheckFloatResult(S, OpPC, Status);
return CheckFloatResult(S, OpPC, Result, Status);
}
template <PrimType Name, class T = typename PrimConv<Name>::T>
@ -340,7 +341,7 @@ inline bool Mulf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
Floating Result;
auto Status = Floating::mul(LHS, RHS, RM, &Result);
S.Stk.push<Floating>(Result);
return CheckFloatResult(S, OpPC, Status);
return CheckFloatResult(S, OpPC, Result, Status);
}
/// 1) Pops the RHS from the stack.
/// 2) Pops the LHS from the stack.
@ -443,7 +444,7 @@ inline bool Divf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
Floating Result;
auto Status = Floating::div(LHS, RHS, RM, &Result);
S.Stk.push<Floating>(Result);
return CheckFloatResult(S, OpPC, Status);
return CheckFloatResult(S, OpPC, Result, Status);
}
//===----------------------------------------------------------------------===//
@ -622,7 +623,7 @@ bool IncDecFloatHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
Ptr.deref<Floating>() = Result;
return CheckFloatResult(S, OpPC, Status);
return CheckFloatResult(S, OpPC, Result, Status);
}
inline bool Incf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
@ -1525,7 +1526,7 @@ bool CastIntegralFloating(InterpState &S, CodePtr OpPC,
auto Status = Floating::fromIntegral(FromAP, *Sem, RM, Result);
S.Stk.push<Floating>(Result);
return CheckFloatResult(S, OpPC, Status);
return CheckFloatResult(S, OpPC, Result, Status);
}
template <PrimType Name, class T = typename PrimConv<Name>::T>
@ -1550,7 +1551,7 @@ bool CastFloatingIntegral(InterpState &S, CodePtr OpPC) {
}
S.Stk.push<T>(T(Result));
return CheckFloatResult(S, OpPC, Status);
return CheckFloatResult(S, OpPC, F, Status);
}
}

View File

@ -202,3 +202,18 @@ namespace Compare {
static_assert(!(inf < nan), "");
static_assert(!(inf > nan), "");
}
namespace nan {
constexpr double nan = __builtin_nan("");
static_assert(nan);
constexpr double D1 = 1 + nan; // ref-error {{must be initialized by a constant expression}} \
// ref-note {{produces a NaN}} \
// expected-error {{must be initialized by a constant expression}} \
// expected-note {{produces a NaN}}
constexpr double D2 = __builtin_inf() / __builtin_inf(); // ref-error {{must be initialized by a constant expression}} \
// ref-note {{produces a NaN}} \
// expected-error {{must be initialized by a constant expression}} \
// expected-note {{produces a NaN}}
}