https://github.com/AaronBallman updated https://github.com/llvm/llvm-project/pull/137166
>From bb184fc38d13126f244615425cfefe7368ca71b8 Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Wed, 23 Apr 2025 13:40:44 -0400 Subject: [PATCH 1/8] Add -Wdefault-const-init --- clang/docs/ReleaseNotes.rst | 3 ++ clang/include/clang/Basic/DiagnosticGroups.td | 3 +- .../clang/Basic/DiagnosticSemaKinds.td | 6 +++ clang/lib/Sema/Sema.cpp | 10 ++++ clang/lib/Sema/SemaDecl.cpp | 8 +++ clang/lib/Sema/SemaInit.cpp | 36 ++++++++++--- clang/test/Sema/typedef-retain.c | 3 +- clang/test/Sema/warn-default-const-init.c | 54 +++++++++++++++++++ 8 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 clang/test/Sema/warn-default-const-init.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index bec670e573ca6..1885acec12bae 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -140,6 +140,9 @@ C Language Changes - Clang now allows an ``inline`` specifier on a typedef declaration of a 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 C2y Feature Support ^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 59036b695da85..3686e65c9102a 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -154,8 +154,9 @@ 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 CXXCompat: DiagGroup<"c++-compat">; +def CXXCompat: DiagGroup<"c++-compat", [DefaultConstInit]>; def ExternCCompat : DiagGroup<"extern-c-compat">; def KeywordCompat : DiagGroup<"keyword-compat">; def GNUCaseRange : DiagGroup<"gnu-case-range">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c562802efba57..d97b2feb1b379 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8197,6 +8197,12 @@ def err_address_space_qualified_new : Error< def err_address_space_qualified_delete : Error< "'delete' cannot delete objects of type %0 in address space '%1'">; +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 unitialized and}2 is incompatible with C++">, + InGroup<DefaultConstInit>; def err_default_init_const : Error< "default initialization of an object of const type %0" "%select{| without a user-provided default constructor}1">; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 4039601612c62..d09f94d10b5e0 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1448,6 +1448,16 @@ void Sema::ActOnEndOfTranslationUnit() { // No initialization is performed for a tentative definition. CheckCompleteVariableDeclaration(VD); + // In C, if the definition is const-qualified and has no initializer, it + // 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); + // Notify the consumer that we've completed a tentative definition. if (!VD->isInvalidDecl()) Consumer.CompleteTentativeDefinition(VD); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index d28a2107d58a9..747e9e6d9ac74 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14333,6 +14333,14 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) { return; } + // 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); + // Check for jumps past the implicit initializer. C++0x // clarifies that this applies to a "variable with automatic // storage duration", not a "local variable". diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 0910a820438b0..5c45980ad3dff 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -6496,6 +6496,17 @@ static bool canPerformArrayCopy(const InitializedEntity &Entity) { return false; } +static const FieldDecl *GetConstField(const RecordDecl *RD) { + for (const FieldDecl *FD : RD->fields()) { + QualType QT = FD->getType(); + if (QT.isConstQualified()) + return FD; + if (const auto *RD = QT->getAsRecordDecl()) + return GetConstField(RD); + } + return nullptr; +} + void InitializationSequence::InitializeFrom(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, @@ -6563,13 +6574,26 @@ void InitializationSequence::InitializeFrom(Sema &S, if (!S.getLangOpts().CPlusPlus && Kind.getKind() == InitializationKind::IK_Default) { - RecordDecl *Rec = DestType->getAsRecordDecl(); - if (Rec && Rec->hasUninitializedExplicitInitFields()) { + if (RecordDecl *Rec = DestType->getAsRecordDecl()) { VarDecl *Var = dyn_cast_or_null<VarDecl>(Entity.getDecl()); - if (Var && !Initializer) { - S.Diag(Var->getLocation(), diag::warn_field_requires_explicit_init) - << /* Var-in-Record */ 1 << Rec; - emitUninitializedExplicitInitFields(S, Rec); + if (Rec->hasUninitializedExplicitInitFields()) { + if (Var && !Initializer) { + S.Diag(Var->getLocation(), diag::warn_field_requires_explicit_init) + << /* Var-in-Record */ 1 << Rec; + emitUninitializedExplicitInitFields(S, Rec); + } + } + // If the record has any members which are const (recursively checked), + // then we want to diagnose those as being unitialized if there is no + // 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); + S.Diag(FD->getLocation(), diag::note_default_init_const_member) << FD; + } } } } diff --git a/clang/test/Sema/typedef-retain.c b/clang/test/Sema/typedef-retain.c index 2d94a8f665fe3..603867ee3080d 100644 --- a/clang/test/Sema/typedef-retain.c +++ b/clang/test/Sema/typedef-retain.c @@ -16,7 +16,8 @@ void test2(float4 a, int4p result, int i) { typedef int a[5]; void test3(void) { typedef const a b; - b r; // expected-note {{variable 'r' declared const here}} + b r; // expected-note {{variable 'r' declared const here}} \ + expected-warning {{default initialization of an object of type 'b' (aka 'const int[5]') leaves the object unitialized and is incompatible with C++}} r[0] = 10; // expected-error {{cannot assign to variable 'r' with const-qualified type 'b' (aka 'const int[5]')}} } diff --git a/clang/test/Sema/warn-default-const-init.c b/clang/test/Sema/warn-default-const-init.c new file mode 100644 index 0000000000000..5a9a1219bf6ab --- /dev/null +++ b/clang/test/Sema/warn-default-const-init.c @@ -0,0 +1,54 @@ +// 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=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}} \ + 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}} \ + 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 unitialized 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 unitialized and is incompatible with C++}} \ + cxx-error {{call to implicitly-deleted default constructor of 'struct T'}} + struct T t2 = { { 0 } }; +} +void h() { + struct U u1 = { { 0 } }; + 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 unitialized 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 } }; +} + +// Test a tentative definition which does eventually get an initializer. +extern const int i; +const int i = 12; + +static const int j; // 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'}} +const int k; // 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'}} +const struct S s; // c-warning {{default initialization of an object of type 'const struct S' is incompatible with C++}} \ + 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 unitialized 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'}} +} + >From c3aef4903deefec36ddec049489f40f8f2543e0e Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Wed, 23 Apr 2025 13:55:38 -0400 Subject: [PATCH 2/8] Fix typo --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/lib/Sema/SemaInit.cpp | 2 +- clang/test/Sema/typedef-retain.c | 2 +- clang/test/Sema/warn-default-const-init.c | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d97b2feb1b379..d2c1422173532 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8201,7 +8201,7 @@ 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 unitialized and}2 is incompatible with C++">, + "%select{| leaves the object uninitialized and}2 is incompatible with C++">, InGroup<DefaultConstInit>; def err_default_init_const : Error< "default initialization of an object of const type %0" diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 5c45980ad3dff..29890929e4e69 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -6584,7 +6584,7 @@ void InitializationSequence::InitializeFrom(Sema &S, } } // If the record has any members which are const (recursively checked), - // then we want to diagnose those as being unitialized if there is no + // then we want to diagnose those as being uninitialized if there is no // initializer present. if (!Initializer) { if (const FieldDecl *FD = GetConstField(Rec)) { diff --git a/clang/test/Sema/typedef-retain.c b/clang/test/Sema/typedef-retain.c index 603867ee3080d..76715ca360cbe 100644 --- a/clang/test/Sema/typedef-retain.c +++ b/clang/test/Sema/typedef-retain.c @@ -17,7 +17,7 @@ typedef int a[5]; void test3(void) { typedef const a b; b r; // expected-note {{variable 'r' declared const here}} \ - expected-warning {{default initialization of an object of type 'b' (aka 'const int[5]') leaves the object unitialized and is incompatible with C++}} + expected-warning {{default initialization of an object of type 'b' (aka 'const int[5]') leaves the object uninitialized and is incompatible with C++}} r[0] = 10; // expected-error {{cannot assign to variable 'r' with const-qualified type 'b' (aka 'const int[5]')}} } diff --git a/clang/test/Sema/warn-default-const-init.c b/clang/test/Sema/warn-default-const-init.c index 5a9a1219bf6ab..d216065e13dc5 100644 --- a/clang/test/Sema/warn-default-const-init.c +++ b/clang/test/Sema/warn-default-const-init.c @@ -14,12 +14,12 @@ struct V { int i; const struct A a; }; // c-note {{member 'a' declared 'const' h 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 unitialized and is incompatible with C++}} \ + 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++}} \ 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 unitialized and is incompatible with C++}} \ + 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++}} \ cxx-error {{call to implicitly-deleted default constructor of 'struct T'}} struct T t2 = { { 0 } }; } @@ -28,7 +28,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 unitialized and is incompatible with C++}} \ + 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++}} \ cxx-error {{call to implicitly-deleted default constructor of 'struct V'}} struct V v2 = { 0 }; struct V v3 = { 0, { 0 } }; @@ -46,7 +46,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 unitialized and is incompatible with C++}} \ + const int a; // c-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'}} >From f8e1760ca5d20261ecdd2e89ee432d091c6dbfa6 Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Wed, 23 Apr 2025 14:30:31 -0400 Subject: [PATCH 3/8] Split the diagnostic into two groups The unsafe variant is on by default, the other one is not. --- clang/docs/ReleaseNotes.rst | 8 +++++-- clang/include/clang/Basic/DiagnosticGroups.td | 3 ++- .../clang/Basic/DiagnosticSemaKinds.td | 10 ++++++--- clang/lib/Sema/Sema.cpp | 13 +++++++----- clang/lib/Sema/SemaDecl.cpp | 12 ++++++----- clang/lib/Sema/SemaInit.cpp | 10 +++++---- clang/test/Sema/warn-default-const-init.c | 21 ++++++++++--------- 7 files changed, 47 insertions(+), 30 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 1885acec12bae..4f98325fecb75 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -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 ^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 3686e65c9102a..6c5d21b721d33 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -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">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d2c1422173532..07fac362a8b71 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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">; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index d09f94d10b5e0..87670fe1953db 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -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()) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 747e9e6d9ac74..fa1f3bc732338 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -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 diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 29890929e4e69..8c2ba46031ce9 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -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; } } diff --git a/clang/test/Sema/warn-default-const-init.c b/clang/test/Sema/warn-default-const-init.c index d216065e13dc5..99773dbbb9450 100644 --- a/clang/test/Sema/warn-default-const-init.c +++ b/clang/test/Sema/warn-default-const-init.c @@ -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 } }; } @@ -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 } }; @@ -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'}} >From 32daa84fadc0b6117b2b2d917c644781732679eb Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Thu, 24 Apr 2025 06:37:27 -0400 Subject: [PATCH 4/8] Update tests --- clang/test/C/C23/n2607.c | 4 ++-- clang/test/C/drs/dr1xx.c | 4 ++-- clang/test/Parser/typeof.c | 4 ++-- clang/test/Sema/assign.c | 4 ++-- clang/test/Sema/atomic-ops.c | 14 +++++++------- clang/test/Sema/block-return.c | 2 +- clang/test/Sema/builtins-bpf.c | 2 +- clang/test/Sema/builtins-elementwise-math.c | 10 +++++----- clang/test/Sema/builtins-overflow.c | 2 +- clang/test/Sema/enable_if.c | 2 +- clang/test/Sema/implicit-decl.c | 2 +- clang/test/Sema/overloadable.c | 2 +- clang/test/Sema/sizeless-1.c | 4 ++-- clang/test/Sema/varargs-x86-64.c | 2 +- clang/test/Sema/warn-unused-function.c | 2 +- clang/test/SemaObjC/message.m | 2 +- clang/test/SemaOpenCL/cl20-device-side-enqueue.cl | 2 +- clang/test/SemaOpenCL/invalid-block.cl | 3 ++- .../SemaOpenMP/atomic-capture-const-no-crash.c | 2 +- 19 files changed, 35 insertions(+), 34 deletions(-) diff --git a/clang/test/C/C23/n2607.c b/clang/test/C/C23/n2607.c index 9595aaed54c43..41e8b2ac4b2cd 100644 --- a/clang/test/C/C23/n2607.c +++ b/clang/test/C/C23/n2607.c @@ -24,7 +24,7 @@ void test1(void) { void test2(void) { typedef int array[1]; array reg_array; - const array const_array; + const array const_array = { 0 }; // An array and its elements are identically qualified. We have to test this // using pointers to the array and element, because the controlling @@ -50,7 +50,7 @@ void test2(void) { void test3(void) { // Validate that we pick the correct composite type for a conditional // operator in the presence of qualifiers. - const int const_array[1]; + const int const_array[1] = { 0 }; int array[1]; // FIXME: the type here should be `const int (*)[1]`, but for some reason, diff --git a/clang/test/C/drs/dr1xx.c b/clang/test/C/drs/dr1xx.c index 47538e44428c3..3e4c39cca62e4 100644 --- a/clang/test/C/drs/dr1xx.c +++ b/clang/test/C/drs/dr1xx.c @@ -289,7 +289,7 @@ void dr124(void) { */ void dr126(void) { typedef int *IP; - const IP object; /* expected-note {{variable 'object' declared const here}} */ + const IP object = 0; /* expected-note {{variable 'object' declared const here}} */ /* The root of the DR is whether 'object' is a pointer to a const int, or a * const pointer to int. @@ -329,7 +329,7 @@ void dr129(void) { void dr131(void) { struct S { const int i; /* expected-note {{data member 'i' declared const here}} */ - } s1, s2; + } s1 = { 0 }, s2 = { 0 }; s1 = s2; /* expected-error {{cannot assign to variable 's1' with const-qualified data member 'i'}} */ } diff --git a/clang/test/Parser/typeof.c b/clang/test/Parser/typeof.c index 08f3ca72ab942..f962bcf143957 100644 --- a/clang/test/Parser/typeof.c +++ b/clang/test/Parser/typeof.c @@ -12,8 +12,8 @@ static void test(void) { short TInt eee; // expected-error{{expected ';' at end of declaration}} void ary[7] fff; // expected-error{{array has incomplete element type 'void'}} expected-error{{expected ';' at end of declaration}} typeof(void ary[7]) anIntError; // expected-error{{expected ')'}} expected-note {{to match this '('}} expected-error {{variable has incomplete type 'typeof(void)' (aka 'void')}} - typeof(const int) aci; - const typeof (*pi) aConstInt; + typeof(const int) aci = 0; + const typeof (*pi) aConstInt = 0; int xx; int *i; } diff --git a/clang/test/Sema/assign.c b/clang/test/Sema/assign.c index fe2a9f1434479..13ea1062b9e1e 100644 --- a/clang/test/Sema/assign.c +++ b/clang/test/Sema/assign.c @@ -11,8 +11,8 @@ void test2 (const struct {int a;} *x) { typedef int arr[10]; void test3(void) { - const arr b; // expected-note {{variable 'b' declared const here}} - const int b2[10]; // expected-note {{variable 'b2' declared const here}} + const arr b = {}; // expected-note {{variable 'b' declared const here}} + const int b2[10] = {}; // expected-note {{variable 'b2' declared const here}} b[4] = 1; // expected-error {{cannot assign to variable 'b' with const-qualified type 'const arr' (aka 'const int[10]')}} b2[4] = 1; // expected-error {{cannot assign to variable 'b2' with const-qualified type 'const int[10]'}} } diff --git a/clang/test/Sema/atomic-ops.c b/clang/test/Sema/atomic-ops.c index 725a12060d4e0..aae7aced2628a 100644 --- a/clang/test/Sema/atomic-ops.c +++ b/clang/test/Sema/atomic-ops.c @@ -1,20 +1,20 @@ // RUN: %clang_cc1 %s -verify=expected,fp80,noi128 -fgnuc-version=4.2.1 -ffreestanding \ -// RUN: -fsyntax-only -triple=i686-linux-gnu -std=c11 +// RUN: -Wno-default-const-init-unsafe -fsyntax-only -triple=i686-linux-gnu -std=c11 // RUN: %clang_cc1 %s -verify=expected,noi128 -fgnuc-version=4.2.1 -ffreestanding \ -// RUN: -fsyntax-only -triple=i686-linux-android -std=c11 +// RUN: -Wno-default-const-init-unsafe -fsyntax-only -triple=i686-linux-android -std=c11 // RUN: %clang_cc1 %s -verify -fgnuc-version=4.2.1 -ffreestanding \ -// RUN: -fsyntax-only -triple=powerpc64-linux-gnu -std=c11 +// RUN: -Wno-default-const-init-unsafe -fsyntax-only -triple=powerpc64-linux-gnu -std=c11 // RUN: %clang_cc1 %s -verify -fgnuc-version=4.2.1 -ffreestanding \ -// RUN: -fsyntax-only -triple=powerpc64-linux-gnu -std=c11 \ +// RUN: -Wno-default-const-init-unsafe -fsyntax-only -triple=powerpc64-linux-gnu -std=c11 \ // RUN: -target-cpu pwr7 // RUN: %clang_cc1 %s -verify -fgnuc-version=4.2.1 -ffreestanding \ -// RUN: -fsyntax-only -triple=powerpc64le-linux-gnu -std=c11 \ +// RUN: -Wno-default-const-init-unsafe -fsyntax-only -triple=powerpc64le-linux-gnu -std=c11 \ // RUN: -target-cpu pwr8 -DPPC64_PWR8 // RUN: %clang_cc1 %s -verify -fgnuc-version=4.2.1 -ffreestanding \ -// RUN: -fsyntax-only -triple=powerpc64-unknown-aix -std=c11 \ +// RUN: -Wno-default-const-init-unsafe -fsyntax-only -triple=powerpc64-unknown-aix -std=c11 \ // RUN: -target-cpu pwr8 // RUN: %clang_cc1 %s -verify -fgnuc-version=4.2.1 -ffreestanding \ -// RUN: -fsyntax-only -triple=powerpc64-unknown-aix -std=c11 \ +// RUN: -Wno-default-const-init-unsafe -fsyntax-only -triple=powerpc64-unknown-aix -std=c11 \ // RUN: -mabi=quadword-atomics -target-cpu pwr8 -DPPC64_PWR8 // Basic parsing/Sema tests for __c11_atomic_* diff --git a/clang/test/Sema/block-return.c b/clang/test/Sema/block-return.c index 126fc6f953dea..312ded75b6a81 100644 --- a/clang/test/Sema/block-return.c +++ b/clang/test/Sema/block-return.c @@ -126,7 +126,7 @@ void foo7(void) int (^JJ) (void) = ^{ return j; }; // OK int (^KK) (void) = ^{ return j+1; }; // OK - __block const int k; + __block const int k = 0; const int cint = 100; int (^MM) (void) = ^{ return k; }; diff --git a/clang/test/Sema/builtins-bpf.c b/clang/test/Sema/builtins-bpf.c index fc540260c91c3..d6e17683d6898 100644 --- a/clang/test/Sema/builtins-bpf.c +++ b/clang/test/Sema/builtins-bpf.c @@ -69,7 +69,7 @@ unsigned invalid11(struct s *arg, int info_kind) { } unsigned valid12(void) { - const struct s t; + const struct s t = {}; return __builtin_preserve_type_info(t, 0) + __builtin_preserve_type_info(*(struct s *)0, 1); } diff --git a/clang/test/Sema/builtins-elementwise-math.c b/clang/test/Sema/builtins-elementwise-math.c index 5c54202991a85..b5648a5e5c6e8 100644 --- a/clang/test/Sema/builtins-elementwise-math.c +++ b/clang/test/Sema/builtins-elementwise-math.c @@ -88,7 +88,7 @@ void test_builtin_elementwise_add_sat(int i, short s, double d, float4 v, int3 i _BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}} ext = __builtin_elementwise_add_sat(ext, ext); - const int ci; + const int ci = 0; i = __builtin_elementwise_add_sat(ci, i); i = __builtin_elementwise_add_sat(i, ci); i = __builtin_elementwise_add_sat(ci, ci); @@ -154,7 +154,7 @@ void test_builtin_elementwise_sub_sat(int i, short s, double d, float4 v, int3 i _BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}} ext = __builtin_elementwise_sub_sat(ext, ext); - const int ci; + const int ci = 0; i = __builtin_elementwise_sub_sat(ci, i); i = __builtin_elementwise_sub_sat(i, ci); i = __builtin_elementwise_sub_sat(ci, ci); @@ -214,7 +214,7 @@ void test_builtin_elementwise_max(int i, short s, double d, float4 v, int3 iv, u _BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}} ext = __builtin_elementwise_max(ext, ext); - const int ci; + const int ci = 0; i = __builtin_elementwise_max(ci, i); i = __builtin_elementwise_max(i, ci); i = __builtin_elementwise_max(ci, ci); @@ -274,7 +274,7 @@ void test_builtin_elementwise_min(int i, short s, double d, float4 v, int3 iv, u _BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}} ext = __builtin_elementwise_min(ext, ext); - const int ci; + const int ci = 0; i = __builtin_elementwise_min(ci, i); i = __builtin_elementwise_min(i, ci); i = __builtin_elementwise_min(ci, ci); @@ -1070,7 +1070,7 @@ void test_builtin_elementwise_copysign(int i, short s, double d, float f, float4 ext = __builtin_elementwise_copysign(ext, ext); // expected-error@-1 {{1st argument must be a scalar or vector of floating-point types (was '_BitInt(32)')}} - const float cf32; + const float cf32 = 0.0f; f = __builtin_elementwise_copysign(cf32, f); f = __builtin_elementwise_copysign(f, cf32); f = __builtin_elementwise_copysign(cf32, f); diff --git a/clang/test/Sema/builtins-overflow.c b/clang/test/Sema/builtins-overflow.c index 302489c19e379..9928afbd17c6c 100644 --- a/clang/test/Sema/builtins-overflow.c +++ b/clang/test/Sema/builtins-overflow.c @@ -9,7 +9,7 @@ void test(void) { unsigned r; const char * c; float f; - const unsigned q; + const unsigned q = 0; __builtin_add_overflow(); // expected-error {{too few arguments to function call, expected 3, have 0}} __builtin_add_overflow(1, 1, 1, 1); // expected-error {{too many arguments to function call, expected 3, have 4}} diff --git a/clang/test/Sema/enable_if.c b/clang/test/Sema/enable_if.c index 9d46c71274d69..3ef8310a2fef7 100644 --- a/clang/test/Sema/enable_if.c +++ b/clang/test/Sema/enable_if.c @@ -52,7 +52,7 @@ size_t strnlen(const char *s, size_t maxlen) // expected-note {{'strnlen' has be void test2(const char *s, int i) { // CHECK: define {{.*}}void @test2 - const char c[123]; + const char c[123] = { 0 }; strnlen(s, i); // CHECK: call {{.*}}strnlen_real1 strnlen(s, 999); diff --git a/clang/test/Sema/implicit-decl.c b/clang/test/Sema/implicit-decl.c index a3f35222d833c..03ee6cdcb204a 100644 --- a/clang/test/Sema/implicit-decl.c +++ b/clang/test/Sema/implicit-decl.c @@ -13,7 +13,7 @@ typedef unsigned char Boolean; extern int printf(__const char *__restrict __format, ...); // both-note{{'printf' declared here}} void func(void) { int32_t *vector[16]; - const char compDesc[16 + 1]; + const char compDesc[16 + 1] = { 0 }; int32_t compCount = 0; if (_CFCalendarDecomposeAbsoluteTimeV(compDesc, vector, compCount)) { // expected-error {{call to undeclared function '_CFCalendarDecomposeAbsoluteTimeV'; ISO C99 and later do not support implicit function declarations}} \ expected-note {{previous implicit declaration}} \ diff --git a/clang/test/Sema/overloadable.c b/clang/test/Sema/overloadable.c index 9eecad18064e2..4c6fd0102c59a 100644 --- a/clang/test/Sema/overloadable.c +++ b/clang/test/Sema/overloadable.c @@ -155,7 +155,7 @@ void incompatible_pointer_type_conversions() { } void dropping_qualifiers_is_incompatible() { - const char ccharbuf[1]; + const char ccharbuf[1] = {0}; volatile char vcharbuf[1]; void foo(char *c) __attribute__((overloadable)); diff --git a/clang/test/Sema/sizeless-1.c b/clang/test/Sema/sizeless-1.c index 9ec884b9f1fda..af96023f4bff5 100644 --- a/clang/test/Sema/sizeless-1.c +++ b/clang/test/Sema/sizeless-1.c @@ -94,12 +94,12 @@ void func(int sel) { svint8_t bad_brace_init_int8_6 = {{local_int8, 0}}; // expected-warning {{too many braces around initializer}} const svint8_t const_int8 = local_int8; // expected-note {{declared const here}} - const svint8_t uninit_const_int8; + const svint8_t uninit_const_int8; // expected-warning {{default initialization of an object of type 'const svint8_t' (aka 'const __SVInt8_t') leaves the object uninitialized and is incompatible with C++}}; volatile svint8_t volatile_int8; const volatile svint8_t const_volatile_int8 = local_int8; // expected-note {{declared const here}} - const volatile svint8_t uninit_const_volatile_int8; + const volatile svint8_t uninit_const_volatile_int8; // expected-warning {{default initialization of an object of type 'const volatile svint8_t' (aka 'const volatile __SVInt8_t') leaves the object uninitialized and is incompatible with C++}} _Atomic svint8_t atomic_int8; // expected-error {{_Atomic cannot be applied to sizeless type 'svint8_t'}} __restrict svint8_t restrict_int8; // expected-error {{requires a pointer or reference}} diff --git a/clang/test/Sema/varargs-x86-64.c b/clang/test/Sema/varargs-x86-64.c index ff7fdd533ab0b..f58bbc9682499 100644 --- a/clang/test/Sema/varargs-x86-64.c +++ b/clang/test/Sema/varargs-x86-64.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s -triple x86_64-apple-darwin9 +// RUN: %clang_cc1 -fsyntax-only -Wno-default-const-init-unsafe -verify %s -triple x86_64-apple-darwin9 void f1(void) { const __builtin_va_list args2; diff --git a/clang/test/Sema/warn-unused-function.c b/clang/test/Sema/warn-unused-function.c index 0a0133c21383d..97628b5b5399b 100644 --- a/clang/test/Sema/warn-unused-function.c +++ b/clang/test/Sema/warn-unused-function.c @@ -48,7 +48,7 @@ static void unused(void) { unused(); } // expected-warning{{not needed and will static void cleanupMalloc(char * const * const allocation) { } void f13(void) { - char * const __attribute__((cleanup(cleanupMalloc))) a; + char * const __attribute__((cleanup(cleanupMalloc))) a = 0; (void)a; } diff --git a/clang/test/SemaObjC/message.m b/clang/test/SemaObjC/message.m index 20568ac201288..c769005b08f3e 100644 --- a/clang/test/SemaObjC/message.m +++ b/clang/test/SemaObjC/message.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s +// RUN: %clang_cc1 -fsyntax-only -Wno-default-const-init-unsafe -verify -Wno-objc-root-class %s typedef struct objc_object { Class isa; diff --git a/clang/test/SemaOpenCL/cl20-device-side-enqueue.cl b/clang/test/SemaOpenCL/cl20-device-side-enqueue.cl index 524de8ce2f7dc..a44d9dd86b86a 100644 --- a/clang/test/SemaOpenCL/cl20-device-side-enqueue.cl +++ b/clang/test/SemaOpenCL/cl20-device-side-enqueue.cl @@ -14,7 +14,7 @@ typedef struct {int a;} ndrange_t; kernel void enqueue_kernel_tests(void) { queue_t default_queue; unsigned flags = 0; - QUALS ndrange_t ndrange; + QUALS ndrange_t ndrange = { 0 }; clk_event_t evt; clk_event_t event_wait_list; clk_event_t event_wait_list2[] = {evt, evt}; diff --git a/clang/test/SemaOpenCL/invalid-block.cl b/clang/test/SemaOpenCL/invalid-block.cl index 1605369000429..2214908f800a4 100644 --- a/clang/test/SemaOpenCL/invalid-block.cl +++ b/clang/test/SemaOpenCL/invalid-block.cl @@ -13,7 +13,8 @@ void f1(void) { f0(bl1); f0(bl2); bl1 = bl2; // expected-error{{invalid operands to binary expression ('int (__generic ^const __private)(void)' and 'int (__generic ^const __private)(void)')}} - int (^const bl3)(void); // expected-error{{invalid block variable declaration - must be initialized}} + int (^const bl3)(void); // expected-error{{invalid block variable declaration - must be initialized}} \ + expected-warning {{default initialization of an object of type 'int (__generic ^const __private)(void)' leaves the object uninitialized and is incompatible with C++}} } // A block with extern storage class is not allowed. diff --git a/clang/test/SemaOpenMP/atomic-capture-const-no-crash.c b/clang/test/SemaOpenMP/atomic-capture-const-no-crash.c index 8739d5c088c13..1e1ccf87ea09f 100644 --- a/clang/test/SemaOpenMP/atomic-capture-const-no-crash.c +++ b/clang/test/SemaOpenMP/atomic-capture-const-no-crash.c @@ -3,7 +3,7 @@ // or https://github.com/llvm/llvm-project/pull/71480 void test() { - int v; const int x; // expected-note {{variable 'x' declared const here}} + int v; const int x = 0; // expected-note {{variable 'x' declared const here}} #pragma omp atomic capture { v = x; >From ef899624e1a20bca23e4eb65c5ce17b4f276f494 Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Thu, 24 Apr 2025 07:46:27 -0400 Subject: [PATCH 5/8] Fix handling of Objective-C for...in statements Without this change, we parse as though the initial declaration is an uninitialized variable. With this change, we behave the same as we do for range-based for loops in C++, which is not handled as an uninitialized variable. Note, ParseDeclGroup (which is what is eventually called as a result of this change) was already expecting to be able to handle for...in loops, so this appears to be fixing an oversight. --- clang/lib/Parse/ParseStmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 4a82d57fe566b..4e801f4ef890f 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -2154,7 +2154,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { FirstPart = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation()); } else { // In C++0x, "for (T NS:a" might not be a typo for :: - bool MightBeForRangeStmt = getLangOpts().CPlusPlus; + bool MightBeForRangeStmt = getLangOpts().CPlusPlus || getLangOpts().ObjC; ColonProtectionRAIIObject ColonProtection(*this, MightBeForRangeStmt); ParsedAttributes DeclSpecAttrs(AttrFactory); DG = ParseSimpleDeclaration( >From 1e345633586328cb149f326d6ed7443cc29ffb32 Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Thu, 24 Apr 2025 08:16:54 -0400 Subject: [PATCH 6/8] Fix formatting; NFC --- clang/lib/Sema/Sema.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 87670fe1953db..4edcd3f945f6c 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1460,7 +1460,6 @@ void Sema::ActOnEndOfTranslationUnit() { Diag(VD->getLocation(), DiagID) << Type << /*not a field*/ 0; } - // Notify the consumer that we've completed a tentative definition. if (!VD->isInvalidDecl()) Consumer.CompleteTentativeDefinition(VD); >From c64209569cbbf64de0375972e73ccfcf29c0090d Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Thu, 24 Apr 2025 15:09:10 -0400 Subject: [PATCH 7/8] Update helper function name; NFC --- clang/lib/Sema/SemaInit.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index ddc4070346265..2d965ae6c9262 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -6496,13 +6496,13 @@ static bool canPerformArrayCopy(const InitializedEntity &Entity) { return false; } -static const FieldDecl *GetConstField(const RecordDecl *RD) { +static const FieldDecl *getConstField(const RecordDecl *RD) { for (const FieldDecl *FD : RD->fields()) { QualType QT = FD->getType(); if (QT.isConstQualified()) return FD; if (const auto *RD = QT->getAsRecordDecl()) - return GetConstField(RD); + return getConstField(RD); } return nullptr; } @@ -6587,7 +6587,7 @@ void InitializationSequence::InitializeFrom(Sema &S, // then we want to diagnose those as being uninitialized if there is no // initializer present. if (!Initializer) { - if (const FieldDecl *FD = GetConstField(Rec)) { + if (const FieldDecl *FD = getConstField(Rec)) { unsigned DiagID = diag::warn_default_init_const_unsafe; if (Var->getStorageDuration() == SD_Static || Var->getStorageDuration() == SD_Thread) >From b23359e65e6f11cfaec1460aca0b6b09e0d7b4e0 Mon Sep 17 00:00:00 2001 From: Aaron Ballman <aa...@aaronballman.com> Date: Fri, 25 Apr 2025 07:23:20 -0400 Subject: [PATCH 8/8] Add assertion, update logic, add test All based on review feedback --- clang/lib/Sema/SemaInit.cpp | 7 +++++-- clang/test/Sema/warn-default-const-init.c | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 2d965ae6c9262..ef036d2fac18c 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -6497,12 +6497,15 @@ static bool canPerformArrayCopy(const InitializedEntity &Entity) { } static const FieldDecl *getConstField(const RecordDecl *RD) { + assert(!isa<CXXRecordDecl>(RD) && "Only expect to call this in C mode"); for (const FieldDecl *FD : RD->fields()) { QualType QT = FD->getType(); if (QT.isConstQualified()) return FD; - if (const auto *RD = QT->getAsRecordDecl()) - return getConstField(RD); + if (const auto *RD = QT->getAsRecordDecl()) { + if (const FieldDecl *FD = getConstField(RD)) + return FD; + } } return nullptr; } diff --git a/clang/test/Sema/warn-default-const-init.c b/clang/test/Sema/warn-default-const-init.c index 99773dbbb9450..b8da41b333f3d 100644 --- a/clang/test/Sema/warn-default-const-init.c +++ b/clang/test/Sema/warn-default-const-init.c @@ -13,6 +13,8 @@ struct T { struct S s; }; // cxx-note {{default constructor of 'T' struct U { struct S s; const int j; }; 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}} +struct W { struct A a; const int j; }; // unsafe-note {{member 'j' declared 'const' here}} \ + cxx-note {{default constructor of 'W' is implicitly deleted because field 'j' of const-qualified type 'const int' would not be initialized}} void f() { 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++}} \ @@ -34,6 +36,12 @@ void x() { struct V v2 = { 0 }; struct V v3 = { 0, { 0 } }; } +void y() { + struct W w1; // unsafe-warning {{default initialization of an object of type 'struct W' with const member leaves the object uninitialized and is incompatible with C++}} \ + cxx-error {{call to implicitly-deleted default constructor of 'struct W'}} + struct W w2 = { 0 }; + struct W w3 = { { 0 }, 0 }; +} // Test a tentative definition which does eventually get an initializer. extern const int i; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits