Skip to content

Commit f2d2c99

Browse files
[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.
1 parent b164d36 commit f2d2c99

File tree

6 files changed

+67
-43
lines changed

6 files changed

+67
-43
lines changed

clang/lib/Sema/SemaDecl.cpp

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13963,31 +13963,10 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
1396313963

1396413964
// We allow integer constant expressions in all cases.
1396513965
} else if (DclT->isIntegralOrEnumerationType()) {
13966-
// Check whether the expression is a constant expression.
13967-
SourceLocation Loc;
1396813966
if (getLangOpts().CPlusPlus11 && DclT.isVolatileQualified())
1396913967
// In C++11, a non-constexpr const static data member with an
1397013968
// in-class initializer cannot be volatile.
1397113969
Diag(VDecl->getLocation(), diag::err_in_class_initializer_volatile);
13972-
else if (Init->isValueDependent())
13973-
; // Nothing to check.
13974-
else if (Init->isIntegerConstantExpr(Context, &Loc))
13975-
; // Ok, it's an ICE!
13976-
else if (Init->getType()->isScopedEnumeralType() &&
13977-
Init->isCXX11ConstantExpr(Context))
13978-
; // Ok, it is a scoped-enum constant expression.
13979-
else if (Init->isEvaluatable(Context)) {
13980-
// If we can constant fold the initializer through heroics, accept it,
13981-
// but report this as a use of an extension for -pedantic.
13982-
Diag(Loc, diag::ext_in_class_initializer_non_constant)
13983-
<< Init->getSourceRange();
13984-
} else {
13985-
// Otherwise, this is some crazy unknown case. Report the issue at the
13986-
// location provided by the isIntegerConstantExpr failed check.
13987-
Diag(Loc, diag::err_in_class_initializer_non_constant)
13988-
<< Init->getSourceRange();
13989-
VDecl->setInvalidDecl();
13990-
}
1399113970

1399213971
// We allow foldable floating-point constants as an extension.
1399313972
} else if (DclT->isFloatingType()) { // also permits complex, which is ok
@@ -14715,6 +14694,17 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
1471514694
// Compute and cache the constant value, and remember that we have a
1471614695
// constant initializer.
1471714696
if (HasConstInit) {
14697+
if (var->isStaticDataMember() && !var->isInline() &&
14698+
var->getLexicalDeclContext()->isRecord() &&
14699+
type->isIntegralOrEnumerationType()) {
14700+
// In C++98, in-class initialization for a static data member must
14701+
// be an integer constant expression.
14702+
SourceLocation Loc;
14703+
if (!Init->isIntegerConstantExpr(Context, &Loc)) {
14704+
Diag(Loc, diag::ext_in_class_initializer_non_constant)
14705+
<< Init->getSourceRange();
14706+
}
14707+
}
1471814708
(void)var->checkForConstantInitialization(Notes);
1471914709
Notes.clear();
1472014710
} else if (CacheCulprit) {
@@ -14750,6 +14740,13 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
1475014740
<< Attr->getRange() << Attr->isConstinit();
1475114741
for (auto &it : Notes)
1475214742
Diag(it.first, it.second);
14743+
} else if (var->isStaticDataMember() && !var->isInline() &&
14744+
var->getLexicalDeclContext()->isRecord()) {
14745+
Diag(var->getLocation(), diag::err_in_class_initializer_non_constant)
14746+
<< Init->getSourceRange();
14747+
for (auto &it : Notes)
14748+
Diag(it.first, it.second);
14749+
var->setInvalidDecl();
1475314750
} else if (IsGlobal &&
1475414751
!getDiagnostics().isIgnored(diag::warn_global_constructor,
1475514752
var->getLocation())) {

clang/test/SemaCXX/builtin-is-constant-evaluated.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,17 @@ namespace narrowing {
154154
// expected-note {{insert an explicit cast to silence this issue}}
155155
}
156156
}
157+
158+
struct GH99680 {
159+
static const int x1 = 1/(1-__builtin_is_constant_evaluated()); // expected-error {{in-class initializer for static data member is not a constant expression}} \
160+
// expected-note {{division by zero}}
161+
static const int x2 = __builtin_is_constant_evaluated();
162+
static_assert(x2 == 1);
163+
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}} \
164+
// expected-note {{add 'constexpr'}} \
165+
// expected-error {{in-class initializer for static data member is not a constant expression}} \
166+
// expected-note {{division by zero}}
167+
static const float x4 = __builtin_is_constant_evaluated(); // expected-error {{in-class initializer for static data member of type 'const float' requires 'constexpr' specifier}} \
168+
// expected-note {{add 'constexpr'}}
169+
static_assert(fold(x4 == 1));
170+
};

clang/test/SemaCXX/class.cpp

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx11 -Wc++11-compat %s
2-
// RUN: %clang_cc1 -fsyntax-only -verify -Wc++11-compat %s -std=c++98
2+
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98 -Wc++11-compat %s -std=c++98
33
class C {
44
public:
55
auto int errx; // expected-error {{storage class specified for a member declaration}}
@@ -32,7 +32,7 @@ class C {
3232
int : 1, : 2;
3333
typedef int E : 1; // expected-error {{typedef member 'E' cannot be a bit-field}}
3434
static int sb : 1; // expected-error {{static member 'sb' cannot be a bit-field}}
35-
static int vs;
35+
static int vs; // cxx11-note {{declared here}}
3636

3737
typedef int func();
3838
func tm;
@@ -48,20 +48,28 @@ class C {
4848
#endif
4949
static int si = 0; // expected-error {{non-const static data member must be initialized out of line}}
5050
static const NestedC ci = 0; // expected-error {{static data member of type 'const NestedC' must be initialized out of line}}
51-
static const int nci = vs; // expected-error {{in-class initializer for static data member is not a constant expression}}
51+
static const int nci = vs; // expected-error {{in-class initializer for static data member is not a constant expression}} \
52+
// cxx11-note {{read of non-const variable 'vs' is not allowed in a constant expression}} \
53+
// cxx98-note {{subexpression not valid in a constant expression}}
5254
static const int vi = 0;
5355
static const volatile int cvi = 0; // ok, illegal in C++11
5456
#if __cplusplus >= 201103L
5557
// expected-error@-2 {{static const volatile data member must be initialized out of line}}
5658
#endif
5759
static const E evi = 0;
58-
static const int overflow = 1000000*1000000; // cxx11-error {{in-class initializer for static data member is not a constant expression}}
59-
// expected-warning@-1 {{overflow in expression}}
60-
static const int overflow_shift = 1<<32; // cxx11-error {{in-class initializer for static data member is not a constant expression}}
61-
static const int overflow_shift2 = 1>>32; // cxx11-error {{in-class initializer for static data member is not a constant expression}}
62-
static const int overflow_shift3 = 1<<-1; // cxx11-error {{in-class initializer for static data member is not a constant expression}}
63-
static const int overflow_shift4 = 1<<-1; // cxx11-error {{in-class initializer for static data member is not a constant expression}}
64-
static const int overflow_shift5 = -1<<1; // cxx11-error {{in-class initializer for static data member is not a constant expression}}
60+
static const int overflow = 1000000*1000000; // cxx11-error {{in-class initializer for static data member is not a constant expression}} \
61+
// cxx11-note {{value 1000000000000 is outside the range of representable values of type 'int'}} \
62+
// expected-warning {{overflow in expression}}
63+
static const int overflow_shift = 1<<32; // cxx11-error {{in-class initializer for static data member is not a constant expression}} \
64+
// cxx11-note {{shift count 32 >= width of type 'int' (32 bits)}}
65+
static const int overflow_shift2 = 1>>32; // cxx11-error {{in-class initializer for static data member is not a constant expression}}\
66+
// cxx11-note {{shift count 32 >= width of type 'int' (32 bits)}}
67+
static const int overflow_shift3 = 1<<-1; // cxx11-error {{in-class initializer for static data member is not a constant expression}} \
68+
// cxx11-note {{negative shift count -1}}
69+
static const int overflow_shift4 = 1<<-1; // cxx11-error {{in-class initializer for static data member is not a constant expression}} \
70+
// cxx11-note {{negative shift count -1}}
71+
static const int overflow_shift5 = -1<<1; // cxx11-error {{in-class initializer for static data member is not a constant expression}} \
72+
// cxx11-note {{left shift of negative value -1}}
6573

6674
void m() {
6775
sx = 0;

clang/test/SemaCXX/cxx0x-class.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %clang_cc1 -Wno-uninitialized -fsyntax-only -verify -std=c++11 -Wno-error=static-float-init %s
22

3-
int vs = 0;
3+
int vs = 0; // expected-note {{declared here}}
44

55
class C {
66
public:
@@ -11,17 +11,20 @@ class C {
1111
int i = 0;
1212
static int si = 0; // expected-error {{non-const static data member must be initialized out of line}}
1313
static const NestedC ci = 0; // expected-error {{static data member of type 'const NestedC' must be initialized out of line}}
14-
static const int nci = vs; // expected-error {{in-class initializer for static data member is not a constant expression}}
14+
static const int nci = vs; // expected-error {{in-class initializer for static data member is not a constant expression}} \
15+
// expected-note {{read of non-const variable 'vs' is not allowed in a constant expression}}
1516
static const int vi = 0;
1617
static const volatile int cvi = 0; // expected-error {{static const volatile data member must be initialized out of line}}
1718
};
1819

1920
namespace rdar8367341 {
20-
float foo(); // expected-note {{here}}
21+
float foo(); // expected-note 2 {{here}}
2122

2223
struct A {
2324
static const float x = 5.0f; // expected-warning {{requires 'constexpr'}} expected-note {{add 'constexpr'}}
24-
static const float y = foo(); // expected-warning {{requires 'constexpr'}} expected-note {{add 'constexpr'}}
25+
static const float y = foo(); // expected-warning {{requires 'constexpr'}} expected-note {{add 'constexpr'}} \
26+
// expected-error {{in-class initializer for static data member is not a constant expression}} \
27+
// expected-note {{non-constexpr function 'foo' cannot be used in a constant expression}}
2528
static constexpr float x2 = 5.0f;
2629
static constexpr float y2 = foo(); // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr function 'foo'}}
2730
};

clang/test/SemaCXX/cxx2a-consteval.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,20 +1154,20 @@ namespace GH65985 {
11541154
int consteval operator""_foo(unsigned long long V) {
11551155
return 0;
11561156
}
1157-
int consteval operator""_bar(unsigned long long V); // expected-note 3{{here}}
1157+
int consteval operator""_bar(unsigned long long V); // expected-note 4 {{here}}
11581158

11591159
int consteval f() {
11601160
return 0;
11611161
}
11621162

1163-
int consteval g(); // expected-note {{here}}
1163+
int consteval g(); // expected-note 2 {{here}}
11641164

11651165

11661166
struct C {
11671167
static const int a = 1_foo;
11681168
static constexpr int b = 1_foo;
11691169
static const int c = 1_bar; // expected-error {{call to consteval function 'GH65985::operator""_bar' is not a constant expression}} \
1170-
// expected-note {{undefined function 'operator""_bar' cannot be used in a constant expression}} \
1170+
// expected-note 2 {{undefined function 'operator""_bar' cannot be used in a constant expression}} \
11711171
// expected-error {{in-class initializer for static data member is not a constant expression}}
11721172

11731173
// FIXME: remove duplicate diagnostics
@@ -1179,7 +1179,7 @@ struct C {
11791179
static const int e = f();
11801180
static const int f = g(); // expected-error {{call to consteval function 'GH65985::g' is not a constant expression}} \
11811181
// expected-error {{in-class initializer for static data member is not a constant expression}} \
1182-
// expected-note {{undefined function 'g' cannot be used in a constant expression}}
1182+
// expected-note 2 {{undefined function 'g' cannot be used in a constant expression}}
11831183
};
11841184

11851185
}

clang/test/SemaTemplate/instantiate-static-var.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
// RUN: %clang_cc1 -fsyntax-only -verify %s
2-
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
3-
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
1+
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx11 %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98 -std=c++98 %s
3+
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx11 -std=c++11 %s
44

55
template<typename T, T Divisor>
66
class X {
77
public:
8-
static const T value = 10 / Divisor; // expected-error{{in-class initializer for static data member is not a constant expression}}
8+
static const T value = 10 / Divisor; // expected-error{{in-class initializer for static data member is not a constant expression}} \
9+
// cxx11-note {{division by zero}} \
10+
// cxx98-note {{subexpression not valid}}
911
};
1012

1113
int array1[X<int, 2>::value == 5? 1 : -1];

0 commit comments

Comments
 (0)