Skip to content

[C] Warn on uninitialized const objects #137166

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 25, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Split the diagnostic into two groups
The unsafe variant is on by default, the other one is not.
  • Loading branch information
AaronBallman committed Apr 23, 2025
commit f8e1760ca5d20261ecdd2e89ee432d091c6dbfa6
8 changes: 6 additions & 2 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,12 @@ C Language Changes
function type in Microsoft compatibility mode. #GH124869
- Clang now allows ``restrict`` qualifier for array types with pointer elements (#GH92847).
- Clang now diagnoses ``const``-qualified object definitions without an
initializer, under the new warning ``-Wdefault-const-init`` (which is grouped
under ``-Wc++-compat``, as this construct is not compatible with C++). #GH19297
initializer. If the object is zero-initialized, it will be diagnosed under
the new warning ``-Wdefault-const-init`` (which is grouped under
``-Wc++-compat`` because this construct is not compatible with C++). If the
object is left uninitialized, it will be diagnosed unsed the new warning
``-Wdefault-const-init-unsafe`` (which is grouped under
``-Wdefault-const-init``). #GH19297

C2y Feature Support
^^^^^^^^^^^^^^^^^^^
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ def BuiltinRequiresHeader : DiagGroup<"builtin-requires-header">;
def C99Compat : DiagGroup<"c99-compat">;
def C23Compat : DiagGroup<"c23-compat">;
def : DiagGroup<"c2x-compat", [C23Compat]>;
def DefaultConstInit : DiagGroup<"default-const-init">;
def DefaultConstInitUnsafe : DiagGroup<"default-const-init-unsafe">;
def DefaultConstInit : DiagGroup<"default-const-init", [DefaultConstInitUnsafe]>;

def CXXCompat: DiagGroup<"c++-compat", [DefaultConstInit]>;
def ExternCCompat : DiagGroup<"extern-c-compat">;
Expand Down
10 changes: 7 additions & 3 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -8200,9 +8200,13 @@ def err_address_space_qualified_delete : Error<
def note_default_init_const_member : Note<
"member %0 declared 'const' here">;
def warn_default_init_const : Warning<
"default initialization of an object of type %0%select{| with const member}1"
"%select{| leaves the object uninitialized and}2 is incompatible with C++">,
InGroup<DefaultConstInit>;
"default initialization of an object of type %0%select{| with const member}1 "
"is incompatible with C++">,
InGroup<DefaultConstInit>, DefaultIgnore;
def warn_default_init_const_unsafe : Warning<
"default initialization of an object of type %0%select{| with const member}1 "
"leaves the object uninitialized and is incompatible with C++">,
InGroup<DefaultConstInitUnsafe>;
def err_default_init_const : Error<
"default initialization of an object of const type %0"
"%select{| without a user-provided default constructor}1">;
Expand Down
13 changes: 8 additions & 5 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1452,11 +1452,14 @@ void Sema::ActOnEndOfTranslationUnit() {
// is left uninitialized unless it has static or thread storage duration.
QualType Type = VD->getType();
if (!VD->isInvalidDecl() && !getLangOpts().CPlusPlus &&
Type.isConstQualified() && !VD->getAnyInitializer())
Diag(VD->getLocation(), diag::warn_default_init_const)
<< Type << /*not a field*/0
<< (VD->getStorageDuration() != SD_Static &&
VD->getStorageDuration() != SD_Thread);
Type.isConstQualified() && !VD->getAnyInitializer()) {
unsigned DiagID = diag::warn_default_init_const_unsafe;
if (VD->getStorageDuration() == SD_Static ||
VD->getStorageDuration() == SD_Thread)
DiagID = diag::warn_default_init_const;
Diag(VD->getLocation(), DiagID) << Type << /*not a field*/ 0;
}


// Notify the consumer that we've completed a tentative definition.
if (!VD->isInvalidDecl())
Expand Down
12 changes: 7 additions & 5 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14335,11 +14335,13 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {

// In C, if the definition is const-qualified and has no initializer, it
// is left uninitialized unless it has static or thread storage duration.
if (!getLangOpts().CPlusPlus && Type.isConstQualified())
Diag(Var->getLocation(), diag::warn_default_init_const)
<< Type << /*not a field*/0
<< (Var->getStorageDuration() != SD_Static &&
Var->getStorageDuration() != SD_Thread);
if (!getLangOpts().CPlusPlus && Type.isConstQualified()) {
unsigned DiagID = diag::warn_default_init_const_unsafe;
if (Var->getStorageDuration() == SD_Static ||
Var->getStorageDuration() == SD_Thread)
DiagID = diag::warn_default_init_const;
Diag(Var->getLocation(), DiagID) << Type << /*not a field*/ 0;
}

// Check for jumps past the implicit initializer. C++0x
// clarifies that this applies to a "variable with automatic
Expand Down
10 changes: 6 additions & 4 deletions clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6588,10 +6588,12 @@ void InitializationSequence::InitializeFrom(Sema &S,
// initializer present.
if (!Initializer) {
if (const FieldDecl *FD = GetConstField(Rec)) {
S.Diag(Var->getLocation(), diag::warn_default_init_const)
<< Var->getType() << /*member*/ 1
<< (Var->getStorageDuration() != SD_Static &&
Var->getStorageDuration() != SD_Thread);
unsigned DiagID = diag::warn_default_init_const_unsafe;
if (Var->getStorageDuration() == SD_Static ||
Var->getStorageDuration() == SD_Thread)
DiagID = diag::warn_default_init_const;

S.Diag(Var->getLocation(), DiagID) << Var->getType() << /*member*/ 1;
S.Diag(FD->getLocation(), diag::note_default_init_const_member) << FD;
}
}
Expand Down
21 changes: 11 additions & 10 deletions clang/test/Sema/warn-default-const-init.c
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
// RUN: %clang_cc1 -fsyntax-only -verify=c -Wdefault-const-init %s
// RUN: %clang_cc1 -fsyntax-only -verify=c -Wc++-compat %s
// RUN: %clang_cc1 -fsyntax-only -verify=c %s
// RUN: %clang_cc1 -fsyntax-only -verify=c,unsafe -Wdefault-const-init %s
// RUN: %clang_cc1 -fsyntax-only -verify=c,unsafe -Wc++-compat %s
// RUN: %clang_cc1 -fsyntax-only -verify=unsafe %s
// RUN: %clang_cc1 -fsyntax-only -verify=c -Wdefault-const-init -Wno-default-const-init-unsafe %s
// RUN: %clang_cc1 -fsyntax-only -verify=good -Wno-default-const-init-unsafe %s
// RUN: %clang_cc1 -fsyntax-only -verify=cxx -x c++ %s
// RUN: %clang_cc1 -fsyntax-only -verify=good -Wc++-compat -Wno-default-const-init %s
// good-no-diagnostics

struct A { int i; };
struct S{ const int i; }; // c-note 2 {{member 'i' declared 'const' here}} \
struct S{ const int i; }; // unsafe-note 2 {{member 'i' declared 'const' here}} \
cxx-note 3 {{default constructor of 'S' is implicitly deleted because field 'i' of const-qualified type 'const int' would not be initialized}}
struct T { struct S s; }; // cxx-note {{default constructor of 'T' is implicitly deleted because field 's' has a deleted default constructor}}
struct U { struct S s; const int j; };
struct V { int i; const struct A a; }; // c-note {{member 'a' declared 'const' here}} \
struct V { int i; const struct A a; }; // unsafe-note {{member 'a' declared 'const' here}} \
cxx-note {{default constructor of 'V' is implicitly deleted because field 'a' of const-qualified type 'const struct A' would not be initialized}}

void f() {
struct S s1; // c-warning {{default initialization of an object of type 'struct S' with const member leaves the object uninitialized and is incompatible with C++}} \
struct S s1; // unsafe-warning {{default initialization of an object of type 'struct S' with const member leaves the object uninitialized and is incompatible with C++}} \
cxx-error {{call to implicitly-deleted default constructor of 'struct S'}}
struct S s2 = { 0 };
}
void g() {
struct T t1; // c-warning {{default initialization of an object of type 'struct T' with const member leaves the object uninitialized and is incompatible with C++}} \
struct T t1; // unsafe-warning {{default initialization of an object of type 'struct T' with const member leaves the object uninitialized and is incompatible with C++}} \
cxx-error {{call to implicitly-deleted default constructor of 'struct T'}}
struct T t2 = { { 0 } };
}
Expand All @@ -28,7 +29,7 @@ void h() {
struct U u2 = { { 0 }, 0 };
}
void x() {
struct V v1; // c-warning {{default initialization of an object of type 'struct V' with const member leaves the object uninitialized and is incompatible with C++}} \
struct V v1; // unsafe-warning {{default initialization of an object of type 'struct V' with const member leaves the object uninitialized and is incompatible with C++}} \
cxx-error {{call to implicitly-deleted default constructor of 'struct V'}}
struct V v2 = { 0 };
struct V v3 = { 0, { 0 } };
Expand All @@ -46,7 +47,7 @@ const struct S s; // c-warning {{default initialization of an object of type '
cxx-error {{call to implicitly-deleted default constructor of 'const struct S'}}

void func() {
const int a; // c-warning {{default initialization of an object of type 'const int' leaves the object uninitialized and is incompatible with C++}} \
const int a; // unsafe-warning {{default initialization of an object of type 'const int' leaves the object uninitialized and is incompatible with C++}} \
cxx-error {{default initialization of an object of const type 'const int'}}
static const int b; // c-warning {{default initialization of an object of type 'const int' is incompatible with C++}} \
cxx-error {{default initialization of an object of const type 'const int'}}
Expand Down