Skip to content

Commit 393ac2d

Browse files
committed
[clang][C23] Allow NaN in constant evaluation
Signed-off-by: yronglin <[email protected]>
1 parent 1b3eaac commit 393ac2d

File tree

4 files changed

+58
-9
lines changed

4 files changed

+58
-9
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ C23 Feature Support
222222
the same translation unit but from different types.
223223
- ``-MG`` now silences the "file not found" errors with ``#embed`` when
224224
scanning for dependencies and encountering an unknown file. #GH165632
225+
- Allow NaN in constant expression evaluation to maintain consistency with
226+
GCC in behavior, even though it's an undefined behavior. #GH161806
225227

226228
Non-comprehensive list of changes in this release
227229
-------------------------------------------------

clang/lib/AST/Decl.cpp

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2605,11 +2605,18 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
26052605
// a constant initializer if we produced notes. In that case, we can't keep
26062606
// the result, because it may only be correct under the assumption that the
26072607
// initializer is a constant context.
2608-
if (IsConstantInitialization &&
2609-
(Ctx.getLangOpts().CPlusPlus ||
2610-
(isConstexpr() && Ctx.getLangOpts().C23)) &&
2611-
!Notes.empty())
2612-
Result = false;
2608+
if (IsConstantInitialization && !Notes.empty()) {
2609+
if (getLangOpts().CPlusPlus)
2610+
Result = false;
2611+
2612+
// Even though hitting an NaN during constant evaluation is an undefined
2613+
// behavior, we expect to maintain consistency with GCC in behavior, that
2614+
// is, allow NaN to appear in constant evaluation.
2615+
bool isNaN =
2616+
Eval->Evaluated.isFloat() && Eval->Evaluated.getFloat().isNaN();
2617+
if (getLangOpts().C23 && !isNaN)
2618+
Result = false;
2619+
}
26132620

26142621
// Ensure the computed APValue is cleaned up later if evaluation succeeded,
26152622
// or that it's empty (so that there's nothing to clean up) if evaluation
@@ -2676,8 +2683,14 @@ bool VarDecl::checkForConstantInitialization(
26762683
assert(!getInit()->isValueDependent());
26772684

26782685
// Evaluate the initializer to check whether it's a constant expression.
2679-
Eval->HasConstantInitialization =
2680-
evaluateValueImpl(Notes, true) && Notes.empty();
2686+
auto *Result = evaluateValueImpl(Notes, true);
2687+
2688+
// Even though hitting an NaN during constant evaluation is an undefined
2689+
// behavior, we expect to maintain consistency with GCC in behavior, that is,
2690+
// allow NaN to appear in constant evaluation.
2691+
bool isNaN = Result && Result->isFloat() && Result->getFloat().isNaN();
2692+
bool AllowNaN = getLangOpts().C23 && isNaN;
2693+
Eval->HasConstantInitialization = Result && (Notes.empty() || AllowNaN);
26812694

26822695
// If evaluation as a constant initializer failed, allow re-evaluation as a
26832696
// non-constant initializer if we later find we want the value.

clang/lib/AST/ExprConstant.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3056,7 +3056,8 @@ static bool handleFloatFloatBinOp(EvalInfo &Info, const BinaryOperator *E,
30563056
// FIXME: C++ rules require us to not conform to IEEE 754 here.
30573057
if (LHS.isNaN()) {
30583058
Info.CCEDiag(E, diag::note_constexpr_float_arithmetic) << LHS.isNaN();
3059-
return Info.noteUndefinedBehavior();
3059+
bool keepEvaluatingAfterUB = Info.noteUndefinedBehavior();
3060+
return Info.Ctx.getLangOpts().C23 || keepEvaluatingAfterUB;
30603061
}
30613062

30623063
return checkFloatingPointResult(Info, E, St);
@@ -19707,7 +19708,8 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
1970719708

1970819709
EvalInfo Info(Ctx, EStatus,
1970919710
(IsConstantInitialization &&
19710-
(Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
19711+
(Ctx.getLangOpts().CPlusPlus ||
19712+
(Ctx.getLangOpts().C23 && VD->isConstexpr())))
1971119713
? EvaluationMode::ConstantExpression
1971219714
: EvaluationMode::ConstantFold);
1971319715
Info.setEvaluatingDecl(VD, Value);

clang/test/AST/const-nan.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %clang_cc1 -std=c23 -triple i386-linux %s -fsyntax-only -verify
2+
// RUN: %clang_cc1 -std=c23 -emit-llvm -triple i386-linux %s -o - | FileCheck %s
3+
4+
// expected-no-diagnostics
5+
6+
// CHECK: @[[CONST:.*]] = private unnamed_addr constant [1 x float] [float 0x7FF8000000000000], align 4
7+
// CHECK: @[[F_X:.*]] = internal global float 0x7FF8000000000000, align 4
8+
#pragma STDC FENV_ACCESS ON
9+
void f(void)
10+
{
11+
// CHECK: %[[V:.*]] = alloca double, align 8
12+
// CHECK: %[[W:.*]] = alloca [1 x float], align 4
13+
// CHECK: %[[Y:.*]] = alloca float, align 4
14+
// CHECK: %[[Z:.*]] = alloca double, align 8
15+
16+
// CHECK: store double 0x7FF8000000000000, ptr %[[V]], align 8
17+
constexpr double v = 0.0/0.0; // does not raise an exception
18+
19+
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[W]], ptr align 4 @[[CONST]], i32 4, i1 false)
20+
float w[] = { 0.0f/0.0f }; // raises an exception
21+
22+
// F_X
23+
static float x = 0.0f/0.0f; // does not raise an exception
24+
25+
// CHECK: %[[DIV:.*]] = call float @llvm.experimental.constrained.fdiv.f32(float 0.000000e+00, float 0.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict")
26+
// CHECK: store float %[[DIV]], ptr %[[Y]], align 4
27+
float y = 0.0f/0.0f; // raises an exception
28+
29+
// CHECK: %[[DIV1:.*]] = call double @llvm.experimental.constrained.fdiv.f64(double 0.000000e+00, double 0.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict")
30+
// CHECK: store double %[[DIV1]], ptr %[[Z]], align 8
31+
double z = 0.0/0.0; // raises an exception
32+
}

0 commit comments

Comments
 (0)