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@-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@-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]; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits