diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 6b396e7ba63f3..d19cc041b5082 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -222,6 +222,8 @@ C23 Feature Support the same translation unit but from different types. - ``-MG`` now silences the "file not found" errors with ``#embed`` when scanning for dependencies and encountering an unknown file. #GH165632 +- Allow NaN in constant expression evaluation to maintain consistency with + GCC in behavior, even though it's an undefined behavior. #GH161806 Non-comprehensive list of changes in this release ------------------------------------------------- diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 8579e51e45697..53ed180f377a1 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2605,11 +2605,18 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl &Notes, // a constant initializer if we produced notes. In that case, we can't keep // the result, because it may only be correct under the assumption that the // initializer is a constant context. - if (IsConstantInitialization && - (Ctx.getLangOpts().CPlusPlus || - (isConstexpr() && Ctx.getLangOpts().C23)) && - !Notes.empty()) - Result = false; + if (IsConstantInitialization && !Notes.empty()) { + if (getLangOpts().CPlusPlus) + Result = false; + + // Even though hitting an NaN during constant evaluation is an undefined + // behavior, we expect to maintain consistency with GCC in behavior, that + // is, allow NaN to appear in constant evaluation. + bool isNaN = + Eval->Evaluated.isFloat() && Eval->Evaluated.getFloat().isNaN(); + if (getLangOpts().C23 && !isNaN) + Result = false; + } // Ensure the computed APValue is cleaned up later if evaluation succeeded, // or that it's empty (so that there's nothing to clean up) if evaluation @@ -2676,8 +2683,14 @@ bool VarDecl::checkForConstantInitialization( assert(!getInit()->isValueDependent()); // Evaluate the initializer to check whether it's a constant expression. - Eval->HasConstantInitialization = - evaluateValueImpl(Notes, true) && Notes.empty(); + auto *Result = evaluateValueImpl(Notes, true); + + // Even though hitting an NaN during constant evaluation is an undefined + // behavior, we expect to maintain consistency with GCC in behavior, that is, + // allow NaN to appear in constant evaluation. + bool isNaN = Result && Result->isFloat() && Result->getFloat().isNaN(); + bool AllowNaN = getLangOpts().C23 && isNaN; + Eval->HasConstantInitialization = Result && (Notes.empty() || AllowNaN); // If evaluation as a constant initializer failed, allow re-evaluation as a // non-constant initializer if we later find we want the value. diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 972d9fe3b5e4f..bd62fbb2edce4 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -3056,7 +3056,8 @@ static bool handleFloatFloatBinOp(EvalInfo &Info, const BinaryOperator *E, // FIXME: C++ rules require us to not conform to IEEE 754 here. if (LHS.isNaN()) { Info.CCEDiag(E, diag::note_constexpr_float_arithmetic) << LHS.isNaN(); - return Info.noteUndefinedBehavior(); + bool keepEvaluatingAfterUB = Info.noteUndefinedBehavior(); + return Info.Ctx.getLangOpts().C23 || keepEvaluatingAfterUB; } return checkFloatingPointResult(Info, E, St); @@ -19707,7 +19708,8 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, EvalInfo Info(Ctx, EStatus, (IsConstantInitialization && - (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23)) + (Ctx.getLangOpts().CPlusPlus || + (Ctx.getLangOpts().C23 && VD->isConstexpr()))) ? EvaluationMode::ConstantExpression : EvaluationMode::ConstantFold); Info.setEvaluatingDecl(VD, Value); diff --git a/clang/test/AST/const-nan.c b/clang/test/AST/const-nan.c new file mode 100644 index 0000000000000..c732abac4a90d --- /dev/null +++ b/clang/test/AST/const-nan.c @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -std=c23 -triple i386-linux %s -fsyntax-only -verify +// RUN: %clang_cc1 -std=c23 -emit-llvm -triple i386-linux %s -o - | FileCheck %s + +// expected-no-diagnostics + +// CHECK: @[[CONST:.*]] = private unnamed_addr constant [1 x float] [float 0x7FF8000000000000], align 4 +// CHECK: @[[F_X:.*]] = internal global float 0x7FF8000000000000, align 4 +#pragma STDC FENV_ACCESS ON +void f(void) +{ + // CHECK: %[[V:.*]] = alloca double, align 8 + // CHECK: %[[W:.*]] = alloca [1 x float], align 4 + // CHECK: %[[Y:.*]] = alloca float, align 4 + // CHECK: %[[Z:.*]] = alloca double, align 8 + + // CHECK: store double 0x7FF8000000000000, ptr %[[V]], align 8 + constexpr double v = 0.0/0.0; // does not raise an exception + + // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[W]], ptr align 4 @[[CONST]], i32 4, i1 false) + float w[] = { 0.0f/0.0f }; // raises an exception + + // F_X + static float x = 0.0f/0.0f; // does not raise an exception + + // CHECK: %[[DIV:.*]] = call float @llvm.experimental.constrained.fdiv.f32(float 0.000000e+00, float 0.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") + // CHECK: store float %[[DIV]], ptr %[[Y]], align 4 + float y = 0.0f/0.0f; // raises an exception + + // CHECK: %[[DIV1:.*]] = call double @llvm.experimental.constrained.fdiv.f64(double 0.000000e+00, double 0.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") + // CHECK: store double %[[DIV1]], ptr %[[Z]], align 8 + double z = 0.0/0.0; // raises an exception +}