Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
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
7 changes: 7 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ 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. 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
- Added ``-Wimplicit-void-ptr-cast``, grouped under ``-Wc++-compat``, which
diagnoses implicit conversion from ``void *`` to another pointer type as
being incompatible with C++. (#GH17792)
Expand Down
6 changes: 4 additions & 2 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,11 @@ def BuiltinRequiresHeader : DiagGroup<"builtin-requires-header">;
def C99Compat : DiagGroup<"c99-compat">;
def C23Compat : DiagGroup<"c23-compat">;
def : DiagGroup<"c2x-compat", [C23Compat]>;

def DefaultConstInitUnsafe : DiagGroup<"default-const-init-unsafe">;
def DefaultConstInit : DiagGroup<"default-const-init", [DefaultConstInitUnsafe]>;
def ImplicitVoidPtrCast : DiagGroup<"implicit-void-ptr-cast">;
def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast]>;
def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast, DefaultConstInit]>;

def ExternCCompat : DiagGroup<"extern-c-compat">;
def KeywordCompat : DiagGroup<"keyword-compat">;
def GNUCaseRange : DiagGroup<"gnu-case-range">;
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -8197,6 +8197,16 @@ 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 "
"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++">,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AaronBallman I just came across this diagnostic. Why did we chose to mention the incompatibility with C++? I found it odd because the diagnostic is showing up when building a C source file. If the code was in an inline function in a header (that could be consumed from C++) then I think it would make sense to mention incompatibility with C++ but if it's in a C source file it makes less sense to me. When I'm building C source files I don't typically think (or care) about what would happen if I were to build the source file with C++ instead of C.

Really not a huge deal but I'd like to understand the reasoning and here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! I wish I didn't. :-D We don't have a way to know whether a particular warning group is enabled, so there's no way to tell the difference between the user passing no flags and getting the default-enabled -Wdefault-const-init-unsafe, whether they passed -Wdefault-const-init-unsafe explicitly, or whether they passed -Wc++-compat. We just can tell that the warn_default_init_const_unsafe diagnostic itself is/is not ignored at a particular source location. So I don't have a good way to drop/add the "and is incompatible with C++" because I don't have a way to test "did the user explicitly opt in to -Wc++-compat.

However, maybe I can hack around this by adding a new off-by-default fake diagnostic that is never emitted by Clang but is in the -Wc++-compat warning group. If that diagnostic is enabled, it must mean the user passed -Wc++-compat...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I posted #138266 to try to improve this, thank you for bringing it up!

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
2 changes: 1 addition & 1 deletion clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1448,6 +1448,18 @@ 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()) {
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())
Consumer.CompleteTentativeDefinition(VD);
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14333,6 +14333,16 @@ 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()) {
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
// storage duration", not a "local variable".
Expand Down
38 changes: 32 additions & 6 deletions clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -6563,13 +6574,28 @@ 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 uninitialized if there is no
// initializer present.
if (!Initializer) {
if (const FieldDecl *FD = getConstField(Rec)) {
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
4 changes: 2 additions & 2 deletions clang/test/C/C23/n2607.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions clang/test/C/drs/dr1xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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'}} */
}

Expand Down
4 changes: 2 additions & 2 deletions clang/test/Parser/typeof.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions clang/test/Sema/assign.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]'}}
}
Expand Down
14 changes: 7 additions & 7 deletions clang/test/Sema/atomic-ops.c
Original file line number Diff line number Diff line change
@@ -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_*
Expand Down
2 changes: 1 addition & 1 deletion clang/test/Sema/block-return.c
Original file line number Diff line number Diff line change
Expand Up @@ -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; };
Expand Down
2 changes: 1 addition & 1 deletion clang/test/Sema/builtins-bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
10 changes: 5 additions & 5 deletions clang/test/Sema/builtins-elementwise-math.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion clang/test/Sema/builtins-overflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand Down
2 changes: 1 addition & 1 deletion clang/test/Sema/enable_if.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion clang/test/Sema/implicit-decl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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}} \
Expand Down
2 changes: 1 addition & 1 deletion clang/test/Sema/overloadable.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
4 changes: 2 additions & 2 deletions clang/test/Sema/sizeless-1.c
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand Down
3 changes: 2 additions & 1 deletion clang/test/Sema/typedef-retain.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 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]')}}
}

Expand Down
2 changes: 1 addition & 1 deletion clang/test/Sema/varargs-x86-64.c
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Loading
Loading