[clang] f2d2c99 - [clang] Remove separate evaluation step for static class member init. (#142713)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 17 16:43:58 PDT 2025
Author: Eli Friedman
Date: 2025-06-17T16:43:55-07:00
New Revision: f2d2c99866dfd133e7b9c98b1d4983c6bce33d67
URL: https://github.com/llvm/llvm-project/commit/f2d2c99866dfd133e7b9c98b1d4983c6bce33d67
DIFF: https://github.com/llvm/llvm-project/commit/f2d2c99866dfd133e7b9c98b1d4983c6bce33d67.diff
LOG: [clang] Remove separate evaluation step for static class member init. (#142713)
We already evaluate the initializers for all global variables, as
required by the standard. Leverage that evaluation instead of trying to
separately validate static class members.
This has a few benefits:
- Improved diagnostics; we now get notes explaining what failed to
evaluate.
- Improved correctness: is_constant_evaluated is handled correctly.
The behavior follows the proposed resolution for CWG1721.
Fixes #88462. Fixes #99680.
Added:
Modified:
clang/lib/Sema/SemaDecl.cpp
clang/test/SemaCXX/builtin-is-constant-evaluated.cpp
clang/test/SemaCXX/class.cpp
clang/test/SemaCXX/cxx0x-class.cpp
clang/test/SemaCXX/cxx2a-consteval.cpp
clang/test/SemaTemplate/instantiate-static-var.cpp
Removed:
################################################################################
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 02ac898a2b702..1bf72e5bb7b9d 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -13963,31 +13963,10 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
// We allow integer constant expressions in all cases.
} else if (DclT->isIntegralOrEnumerationType()) {
- // Check whether the expression is a constant expression.
- SourceLocation Loc;
if (getLangOpts().CPlusPlus11 && DclT.isVolatileQualified())
// In C++11, a non-constexpr const static data member with an
// in-class initializer cannot be volatile.
Diag(VDecl->getLocation(), diag::err_in_class_initializer_volatile);
- else if (Init->isValueDependent())
- ; // Nothing to check.
- else if (Init->isIntegerConstantExpr(Context, &Loc))
- ; // Ok, it's an ICE!
- else if (Init->getType()->isScopedEnumeralType() &&
- Init->isCXX11ConstantExpr(Context))
- ; // Ok, it is a scoped-enum constant expression.
- else if (Init->isEvaluatable(Context)) {
- // If we can constant fold the initializer through heroics, accept it,
- // but report this as a use of an extension for -pedantic.
- Diag(Loc, diag::ext_in_class_initializer_non_constant)
- << Init->getSourceRange();
- } else {
- // Otherwise, this is some crazy unknown case. Report the issue at the
- // location provided by the isIntegerConstantExpr failed check.
- Diag(Loc, diag::err_in_class_initializer_non_constant)
- << Init->getSourceRange();
- VDecl->setInvalidDecl();
- }
// We allow foldable floating-point constants as an extension.
} else if (DclT->isFloatingType()) { // also permits complex, which is ok
@@ -14715,6 +14694,17 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
// Compute and cache the constant value, and remember that we have a
// constant initializer.
if (HasConstInit) {
+ if (var->isStaticDataMember() && !var->isInline() &&
+ var->getLexicalDeclContext()->isRecord() &&
+ type->isIntegralOrEnumerationType()) {
+ // In C++98, in-class initialization for a static data member must
+ // be an integer constant expression.
+ SourceLocation Loc;
+ if (!Init->isIntegerConstantExpr(Context, &Loc)) {
+ Diag(Loc, diag::ext_in_class_initializer_non_constant)
+ << Init->getSourceRange();
+ }
+ }
(void)var->checkForConstantInitialization(Notes);
Notes.clear();
} else if (CacheCulprit) {
@@ -14750,6 +14740,13 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
<< Attr->getRange() << Attr->isConstinit();
for (auto &it : Notes)
Diag(it.first, it.second);
+ } else if (var->isStaticDataMember() && !var->isInline() &&
+ var->getLexicalDeclContext()->isRecord()) {
+ Diag(var->getLocation(), diag::err_in_class_initializer_non_constant)
+ << Init->getSourceRange();
+ for (auto &it : Notes)
+ Diag(it.first, it.second);
+ var->setInvalidDecl();
} else if (IsGlobal &&
!getDiagnostics().isIgnored(diag::warn_global_constructor,
var->getLocation())) {
diff --git a/clang/test/SemaCXX/builtin-is-constant-evaluated.cpp b/clang/test/SemaCXX/builtin-is-constant-evaluated.cpp
index c775fe71069df..66981acf87a8a 100644
--- a/clang/test/SemaCXX/builtin-is-constant-evaluated.cpp
+++ b/clang/test/SemaCXX/builtin-is-constant-evaluated.cpp
@@ -154,3 +154,17 @@ namespace narrowing {
// expected-note {{insert an explicit cast to silence this issue}}
}
}
+
+struct GH99680 {
+ static const int x1 = 1/(1-__builtin_is_constant_evaluated()); // expected-error {{in-class initializer for static data member is not a constant expression}} \
+ // expected-note {{division by zero}}
+ static const int x2 = __builtin_is_constant_evaluated();
+ static_assert(x2 == 1);
+ static const float x3 = 1/(1-__builtin_is_constant_evaluated()); // expected-error {{in-class initializer for static data member of type 'const float' requires 'constexpr' specifier}} \
+ // expected-note {{add 'constexpr'}} \
+ // expected-error {{in-class initializer for static data member is not a constant expression}} \
+ // expected-note {{division by zero}}
+ static const float x4 = __builtin_is_constant_evaluated(); // expected-error {{in-class initializer for static data member of type 'const float' requires 'constexpr' specifier}} \
+ // expected-note {{add 'constexpr'}}
+ static_assert(fold(x4 == 1));
+};
diff --git a/clang/test/SemaCXX/class.cpp b/clang/test/SemaCXX/class.cpp
index 2f59544e7f36c..f1e02d5158aac 100644
--- a/clang/test/SemaCXX/class.cpp
+++ b/clang/test/SemaCXX/class.cpp
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx11 -Wc++11-compat %s
-// RUN: %clang_cc1 -fsyntax-only -verify -Wc++11-compat %s -std=c++98
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98 -Wc++11-compat %s -std=c++98
class C {
public:
auto int errx; // expected-error {{storage class specified for a member declaration}}
@@ -32,7 +32,7 @@ class C {
int : 1, : 2;
typedef int E : 1; // expected-error {{typedef member 'E' cannot be a bit-field}}
static int sb : 1; // expected-error {{static member 'sb' cannot be a bit-field}}
- static int vs;
+ static int vs; // cxx11-note {{declared here}}
typedef int func();
func tm;
@@ -48,20 +48,28 @@ class C {
#endif
static int si = 0; // expected-error {{non-const static data member must be initialized out of line}}
static const NestedC ci = 0; // expected-error {{static data member of type 'const NestedC' must be initialized out of line}}
- static const int nci = vs; // expected-error {{in-class initializer for static data member is not a constant expression}}
+ static const int nci = vs; // expected-error {{in-class initializer for static data member is not a constant expression}} \
+ // cxx11-note {{read of non-const variable 'vs' is not allowed in a constant expression}} \
+ // cxx98-note {{subexpression not valid in a constant expression}}
static const int vi = 0;
static const volatile int cvi = 0; // ok, illegal in C++11
#if __cplusplus >= 201103L
// expected-error at -2 {{static const volatile data member must be initialized out of line}}
#endif
static const E evi = 0;
- static const int overflow = 1000000*1000000; // cxx11-error {{in-class initializer for static data member is not a constant expression}}
- // expected-warning at -1 {{overflow in expression}}
- static const int overflow_shift = 1<<32; // cxx11-error {{in-class initializer for static data member is not a constant expression}}
- static const int overflow_shift2 = 1>>32; // cxx11-error {{in-class initializer for static data member is not a constant expression}}
- static const int overflow_shift3 = 1<<-1; // cxx11-error {{in-class initializer for static data member is not a constant expression}}
- static const int overflow_shift4 = 1<<-1; // cxx11-error {{in-class initializer for static data member is not a constant expression}}
- static const int overflow_shift5 = -1<<1; // cxx11-error {{in-class initializer for static data member is not a constant expression}}
+ static const int overflow = 1000000*1000000; // cxx11-error {{in-class initializer for static data member is not a constant expression}} \
+ // cxx11-note {{value 1000000000000 is outside the range of representable values of type 'int'}} \
+ // expected-warning {{overflow in expression}}
+ static const int overflow_shift = 1<<32; // cxx11-error {{in-class initializer for static data member is not a constant expression}} \
+ // cxx11-note {{shift count 32 >= width of type 'int' (32 bits)}}
+ static const int overflow_shift2 = 1>>32; // cxx11-error {{in-class initializer for static data member is not a constant expression}}\
+ // cxx11-note {{shift count 32 >= width of type 'int' (32 bits)}}
+ static const int overflow_shift3 = 1<<-1; // cxx11-error {{in-class initializer for static data member is not a constant expression}} \
+ // cxx11-note {{negative shift count -1}}
+ static const int overflow_shift4 = 1<<-1; // cxx11-error {{in-class initializer for static data member is not a constant expression}} \
+ // cxx11-note {{negative shift count -1}}
+ static const int overflow_shift5 = -1<<1; // cxx11-error {{in-class initializer for static data member is not a constant expression}} \
+ // cxx11-note {{left shift of negative value -1}}
void m() {
sx = 0;
diff --git a/clang/test/SemaCXX/cxx0x-class.cpp b/clang/test/SemaCXX/cxx0x-class.cpp
index a612a5c07e6ed..4b54221cceff2 100644
--- a/clang/test/SemaCXX/cxx0x-class.cpp
+++ b/clang/test/SemaCXX/cxx0x-class.cpp
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -Wno-uninitialized -fsyntax-only -verify -std=c++11 -Wno-error=static-float-init %s
-int vs = 0;
+int vs = 0; // expected-note {{declared here}}
class C {
public:
@@ -11,17 +11,20 @@ class C {
int i = 0;
static int si = 0; // expected-error {{non-const static data member must be initialized out of line}}
static const NestedC ci = 0; // expected-error {{static data member of type 'const NestedC' must be initialized out of line}}
- static const int nci = vs; // expected-error {{in-class initializer for static data member is not a constant expression}}
+ static const int nci = vs; // expected-error {{in-class initializer for static data member is not a constant expression}} \
+ // expected-note {{read of non-const variable 'vs' is not allowed in a constant expression}}
static const int vi = 0;
static const volatile int cvi = 0; // expected-error {{static const volatile data member must be initialized out of line}}
};
namespace rdar8367341 {
- float foo(); // expected-note {{here}}
+ float foo(); // expected-note 2 {{here}}
struct A {
static const float x = 5.0f; // expected-warning {{requires 'constexpr'}} expected-note {{add 'constexpr'}}
- static const float y = foo(); // expected-warning {{requires 'constexpr'}} expected-note {{add 'constexpr'}}
+ static const float y = foo(); // expected-warning {{requires 'constexpr'}} expected-note {{add 'constexpr'}} \
+ // expected-error {{in-class initializer for static data member is not a constant expression}} \
+ // expected-note {{non-constexpr function 'foo' cannot be used in a constant expression}}
static constexpr float x2 = 5.0f;
static constexpr float y2 = foo(); // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr function 'foo'}}
};
diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp
index d9932e4dd8241..1474c48cda3c1 100644
--- a/clang/test/SemaCXX/cxx2a-consteval.cpp
+++ b/clang/test/SemaCXX/cxx2a-consteval.cpp
@@ -1154,20 +1154,20 @@ namespace GH65985 {
int consteval operator""_foo(unsigned long long V) {
return 0;
}
-int consteval operator""_bar(unsigned long long V); // expected-note 3{{here}}
+int consteval operator""_bar(unsigned long long V); // expected-note 4 {{here}}
int consteval f() {
return 0;
}
-int consteval g(); // expected-note {{here}}
+int consteval g(); // expected-note 2 {{here}}
struct C {
static const int a = 1_foo;
static constexpr int b = 1_foo;
static const int c = 1_bar; // expected-error {{call to consteval function 'GH65985::operator""_bar' is not a constant expression}} \
- // expected-note {{undefined function 'operator""_bar' cannot be used in a constant expression}} \
+ // expected-note 2 {{undefined function 'operator""_bar' cannot be used in a constant expression}} \
// expected-error {{in-class initializer for static data member is not a constant expression}}
// FIXME: remove duplicate diagnostics
@@ -1179,7 +1179,7 @@ struct C {
static const int e = f();
static const int f = g(); // expected-error {{call to consteval function 'GH65985::g' is not a constant expression}} \
// expected-error {{in-class initializer for static data member is not a constant expression}} \
- // expected-note {{undefined function 'g' cannot be used in a constant expression}}
+ // expected-note 2 {{undefined function 'g' cannot be used in a constant expression}}
};
}
diff --git a/clang/test/SemaTemplate/instantiate-static-var.cpp b/clang/test/SemaTemplate/instantiate-static-var.cpp
index 63d8366b617c1..6602670af901f 100644
--- a/clang/test/SemaTemplate/instantiate-static-var.cpp
+++ b/clang/test/SemaTemplate/instantiate-static-var.cpp
@@ -1,11 +1,13 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98 -std=c++98 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx11 -std=c++11 %s
template<typename T, T Divisor>
class X {
public:
- static const T value = 10 / Divisor; // expected-error{{in-class initializer for static data member is not a constant expression}}
+ static const T value = 10 / Divisor; // expected-error{{in-class initializer for static data member is not a constant expression}} \
+ // cxx11-note {{division by zero}} \
+ // cxx98-note {{subexpression not valid}}
};
int array1[X<int, 2>::value == 5? 1 : -1];
More information about the cfe-commits
mailing list