diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2528,6 +2528,11 @@ /// Check if the given evaluation result is allowed for constant evaluation. static bool checkFloatingPointResult(EvalInfo &Info, const Expr *E, APFloat::opStatus St) { + // In a constant context, assume that any dynamic rounding mode or FP + // exception state matches the default floating-point environment. + if (Info.InConstantContext) + return true; + FPOptions FPO = E->getFPFeaturesInEffect(Info.Ctx.getLangOpts()); if ((St & APFloat::opInexact) && FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) { diff --git a/clang/test/Sema/rounding-math.c b/clang/test/Sema/rounding-math.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/rounding-math.c @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple x86_64-linux -verify=norounding %s +// RUN: %clang_cc1 -triple x86_64-linux -std=c17 -verify=rounding-std %s -frounding-math +// RUN: %clang_cc1 -triple x86_64-linux -std=gnu17 -verify=rounding-gnu %s -frounding-math + +#define fold(x) (__builtin_constant_p(x) ? (x) : (x)) + +double a = 1.0 / 3.0; + +#define f(n) ((n) * (1.0 / 3.0)) +_Static_assert(fold((int)f(3)) == 1, ""); + +typedef int T[fold((int)f(3))]; +typedef int T[1]; + +enum Enum { enum_a = (int)f(3) }; + +struct Bitfield { + unsigned int n : 1; + unsigned int m : fold((int)f(3)); +}; + +void bitfield(struct Bitfield *b) { + b->n = (int)(6 * (1.0 / 3.0)); // norounding-warning {{changes value from 2 to 0}} +} + +void vlas() { + // Under -frounding-math, this is a VLA. + // FIXME: Due to PR44406, in GNU mode we constant-fold the initializer resulting in a non-VLA. + typedef int vla[(int)(-3 * (1.0 / 3.0))]; // norounding-error {{negative size}} rounding-gnu-error {{negative size}} + struct X { vla v; }; // rounding-std-error {{fields must have a constant size}} +} diff --git a/clang/test/SemaCXX/rounding-math.cpp b/clang/test/SemaCXX/rounding-math.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/rounding-math.cpp @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -triple x86_64-linux -verify=norounding %s +// RUN: %clang_cc1 -triple x86_64-linux -verify=rounding %s -frounding-math +// rounding-no-diagnostics + +#define fold(x) (__builtin_constant_p(x) ? (x) : (x)) + +constexpr double a = 1.0 / 3.0; + +constexpr int f(int n) { return int(n * (1.0 / 3.0)); } + +using T = int[f(3)]; +using T = int[1]; + +enum Enum { enum_a = f(3) }; + +struct Bitfield { + unsigned int n : 1; + unsigned int m : f(3); +}; + +void f(Bitfield &b) { + b.n = int(6 * (1.0 / 3.0)); // norounding-warning {{changes value from 2 to 0}} +} + +const int k = 3 * (1.0 / 3.0); +static_assert(k == 1, ""); + +void g() { + // FIXME: Constant-evaluating this initializer is surprising, and violates + // the recommended practice in C++ [expr.const]p12: + // + // Implementations should provide consistent results of floating-point + // evaluations, irrespective of whether the evaluation is performed during + // translation or during program execution. + const int k = 3 * (1.0 / 3.0); + static_assert(k == 1, ""); +} + +int *h() { + return new int[int(-3 * (1.0 / 3.0))]; // norounding-error {{too large}} +}