Skip to content

Commit bb184fc

Browse files
committed
Add -Wdefault-const-init
1 parent eea1efe commit bb184fc

File tree

8 files changed

+115
-8
lines changed

8 files changed

+115
-8
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ C Language Changes
140140
- Clang now allows an ``inline`` specifier on a typedef declaration of a
141141
function type in Microsoft compatibility mode. #GH124869
142142
- Clang now allows ``restrict`` qualifier for array types with pointer elements (#GH92847).
143+
- Clang now diagnoses ``const``-qualified object definitions without an
144+
initializer, under the new warning ``-Wdefault-const-init`` (which is grouped
145+
under ``-Wc++-compat``, as this construct is not compatible with C++). #GH19297
143146

144147
C2y Feature Support
145148
^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,9 @@ def BuiltinRequiresHeader : DiagGroup<"builtin-requires-header">;
154154
def C99Compat : DiagGroup<"c99-compat">;
155155
def C23Compat : DiagGroup<"c23-compat">;
156156
def : DiagGroup<"c2x-compat", [C23Compat]>;
157+
def DefaultConstInit : DiagGroup<"default-const-init">;
157158

158-
def CXXCompat: DiagGroup<"c++-compat">;
159+
def CXXCompat: DiagGroup<"c++-compat", [DefaultConstInit]>;
159160
def ExternCCompat : DiagGroup<"extern-c-compat">;
160161
def KeywordCompat : DiagGroup<"keyword-compat">;
161162
def GNUCaseRange : DiagGroup<"gnu-case-range">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8197,6 +8197,12 @@ def err_address_space_qualified_new : Error<
81978197
def err_address_space_qualified_delete : Error<
81988198
"'delete' cannot delete objects of type %0 in address space '%1'">;
81998199

8200+
def note_default_init_const_member : Note<
8201+
"member %0 declared 'const' here">;
8202+
def warn_default_init_const : Warning<
8203+
"default initialization of an object of type %0%select{| with const member}1"
8204+
"%select{| leaves the object unitialized and}2 is incompatible with C++">,
8205+
InGroup<DefaultConstInit>;
82008206
def err_default_init_const : Error<
82018207
"default initialization of an object of const type %0"
82028208
"%select{| without a user-provided default constructor}1">;

clang/lib/Sema/Sema.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,6 +1448,16 @@ void Sema::ActOnEndOfTranslationUnit() {
14481448
// No initialization is performed for a tentative definition.
14491449
CheckCompleteVariableDeclaration(VD);
14501450

1451+
// In C, if the definition is const-qualified and has no initializer, it
1452+
// is left uninitialized unless it has static or thread storage duration.
1453+
QualType Type = VD->getType();
1454+
if (!VD->isInvalidDecl() && !getLangOpts().CPlusPlus &&
1455+
Type.isConstQualified() && !VD->getAnyInitializer())
1456+
Diag(VD->getLocation(), diag::warn_default_init_const)
1457+
<< Type << /*not a field*/0
1458+
<< (VD->getStorageDuration() != SD_Static &&
1459+
VD->getStorageDuration() != SD_Thread);
1460+
14511461
// Notify the consumer that we've completed a tentative definition.
14521462
if (!VD->isInvalidDecl())
14531463
Consumer.CompleteTentativeDefinition(VD);

clang/lib/Sema/SemaDecl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14333,6 +14333,14 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
1433314333
return;
1433414334
}
1433514335

14336+
// In C, if the definition is const-qualified and has no initializer, it
14337+
// is left uninitialized unless it has static or thread storage duration.
14338+
if (!getLangOpts().CPlusPlus && Type.isConstQualified())
14339+
Diag(Var->getLocation(), diag::warn_default_init_const)
14340+
<< Type << /*not a field*/0
14341+
<< (Var->getStorageDuration() != SD_Static &&
14342+
Var->getStorageDuration() != SD_Thread);
14343+
1433614344
// Check for jumps past the implicit initializer. C++0x
1433714345
// clarifies that this applies to a "variable with automatic
1433814346
// storage duration", not a "local variable".

clang/lib/Sema/SemaInit.cpp

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6496,6 +6496,17 @@ static bool canPerformArrayCopy(const InitializedEntity &Entity) {
64966496
return false;
64976497
}
64986498

6499+
static const FieldDecl *GetConstField(const RecordDecl *RD) {
6500+
for (const FieldDecl *FD : RD->fields()) {
6501+
QualType QT = FD->getType();
6502+
if (QT.isConstQualified())
6503+
return FD;
6504+
if (const auto *RD = QT->getAsRecordDecl())
6505+
return GetConstField(RD);
6506+
}
6507+
return nullptr;
6508+
}
6509+
64996510
void InitializationSequence::InitializeFrom(Sema &S,
65006511
const InitializedEntity &Entity,
65016512
const InitializationKind &Kind,
@@ -6563,13 +6574,26 @@ void InitializationSequence::InitializeFrom(Sema &S,
65636574

65646575
if (!S.getLangOpts().CPlusPlus &&
65656576
Kind.getKind() == InitializationKind::IK_Default) {
6566-
RecordDecl *Rec = DestType->getAsRecordDecl();
6567-
if (Rec && Rec->hasUninitializedExplicitInitFields()) {
6577+
if (RecordDecl *Rec = DestType->getAsRecordDecl()) {
65686578
VarDecl *Var = dyn_cast_or_null<VarDecl>(Entity.getDecl());
6569-
if (Var && !Initializer) {
6570-
S.Diag(Var->getLocation(), diag::warn_field_requires_explicit_init)
6571-
<< /* Var-in-Record */ 1 << Rec;
6572-
emitUninitializedExplicitInitFields(S, Rec);
6579+
if (Rec->hasUninitializedExplicitInitFields()) {
6580+
if (Var && !Initializer) {
6581+
S.Diag(Var->getLocation(), diag::warn_field_requires_explicit_init)
6582+
<< /* Var-in-Record */ 1 << Rec;
6583+
emitUninitializedExplicitInitFields(S, Rec);
6584+
}
6585+
}
6586+
// If the record has any members which are const (recursively checked),
6587+
// then we want to diagnose those as being unitialized if there is no
6588+
// initializer present.
6589+
if (!Initializer) {
6590+
if (const FieldDecl *FD = GetConstField(Rec)) {
6591+
S.Diag(Var->getLocation(), diag::warn_default_init_const)
6592+
<< Var->getType() << /*member*/ 1
6593+
<< (Var->getStorageDuration() != SD_Static &&
6594+
Var->getStorageDuration() != SD_Thread);
6595+
S.Diag(FD->getLocation(), diag::note_default_init_const_member) << FD;
6596+
}
65736597
}
65746598
}
65756599
}

clang/test/Sema/typedef-retain.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ void test2(float4 a, int4p result, int i) {
1616
typedef int a[5];
1717
void test3(void) {
1818
typedef const a b;
19-
b r; // expected-note {{variable 'r' declared const here}}
19+
b r; // expected-note {{variable 'r' declared const here}} \
20+
expected-warning {{default initialization of an object of type 'b' (aka 'const int[5]') leaves the object unitialized and is incompatible with C++}}
2021
r[0] = 10; // expected-error {{cannot assign to variable 'r' with const-qualified type 'b' (aka 'const int[5]')}}
2122
}
2223

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify=c -Wdefault-const-init %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify=c -Wc++-compat %s
3+
// RUN: %clang_cc1 -fsyntax-only -verify=c %s
4+
// RUN: %clang_cc1 -fsyntax-only -verify=cxx -x c++ %s
5+
// RUN: %clang_cc1 -fsyntax-only -verify=good -Wc++-compat -Wno-default-const-init %s
6+
// good-no-diagnostics
7+
8+
struct A { int i; };
9+
struct S{ const int i; }; // c-note 2 {{member 'i' declared 'const' here}} \
10+
cxx-note 3 {{default constructor of 'S' is implicitly deleted because field 'i' of const-qualified type 'const int' would not be initialized}}
11+
struct T { struct S s; }; // cxx-note {{default constructor of 'T' is implicitly deleted because field 's' has a deleted default constructor}}
12+
struct U { struct S s; const int j; };
13+
struct V { int i; const struct A a; }; // c-note {{member 'a' declared 'const' here}} \
14+
cxx-note {{default constructor of 'V' is implicitly deleted because field 'a' of const-qualified type 'const struct A' would not be initialized}}
15+
16+
void f() {
17+
struct S s1; // c-warning {{default initialization of an object of type 'struct S' with const member leaves the object unitialized and is incompatible with C++}} \
18+
cxx-error {{call to implicitly-deleted default constructor of 'struct S'}}
19+
struct S s2 = { 0 };
20+
}
21+
void g() {
22+
struct T t1; // c-warning {{default initialization of an object of type 'struct T' with const member leaves the object unitialized and is incompatible with C++}} \
23+
cxx-error {{call to implicitly-deleted default constructor of 'struct T'}}
24+
struct T t2 = { { 0 } };
25+
}
26+
void h() {
27+
struct U u1 = { { 0 } };
28+
struct U u2 = { { 0 }, 0 };
29+
}
30+
void x() {
31+
struct V v1; // c-warning {{default initialization of an object of type 'struct V' with const member leaves the object unitialized and is incompatible with C++}} \
32+
cxx-error {{call to implicitly-deleted default constructor of 'struct V'}}
33+
struct V v2 = { 0 };
34+
struct V v3 = { 0, { 0 } };
35+
}
36+
37+
// Test a tentative definition which does eventually get an initializer.
38+
extern const int i;
39+
const int i = 12;
40+
41+
static const int j; // c-warning {{default initialization of an object of type 'const int' is incompatible with C++}} \
42+
cxx-error {{default initialization of an object of const type 'const int'}}
43+
const int k; // c-warning {{default initialization of an object of type 'const int' is incompatible with C++}} \
44+
cxx-error {{default initialization of an object of const type 'const int'}}
45+
const struct S s; // c-warning {{default initialization of an object of type 'const struct S' is incompatible with C++}} \
46+
cxx-error {{call to implicitly-deleted default constructor of 'const struct S'}}
47+
48+
void func() {
49+
const int a; // c-warning {{default initialization of an object of type 'const int' leaves the object unitialized and is incompatible with C++}} \
50+
cxx-error {{default initialization of an object of const type 'const int'}}
51+
static const int b; // c-warning {{default initialization of an object of type 'const int' is incompatible with C++}} \
52+
cxx-error {{default initialization of an object of const type 'const int'}}
53+
}
54+

0 commit comments

Comments
 (0)