Skip to content

Commit 788731c

Browse files
authored
[clang] Implement P3144R2 "Deleting a Pointer to an Incomplete Type..." (#97733)
This patch implements (not yet published) [P3144R2](https://wiki.edg.com/pub/Wg21stlouis2024/StrawPolls/p3144r2.pdf) "Deleting a Pointer to an Incomplete Type Should be Ill-formed". Wording changes (not yet merged into the working draft) read: > 7.6.2.9 [expr.delete] Delete > If the object being deleted has incomplete class type at the point of deletion <del>and the complete class has a non-trivial destructor or a deallocation function, the behavior is undefined</del>, <ins>the program is ill-formed</ins>. We preserve status quo of emitting a warning when deleting a pointer to incomplete type up to, and including, C++23, but make it ill-formed since C++26. Same goes for deleting pointers to `void`, which has been allowed as an extension.
1 parent 0f1da49 commit 788731c

File tree

8 files changed

+51
-24
lines changed

8 files changed

+51
-24
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ C++2c Feature Support
272272

273273
- Implemented `P2809R3: Trivial infinite loops are not Undefined Behavior <https://wg21.link/P2809R3>`_.
274274

275+
- Implemented `P3144R2 Deleting a Pointer to an Incomplete Type Should be Ill-formed <https://wg21.link/P3144R2>`_.
275276

276277
Resolutions to C++ Defect Reports
277278
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -438,6 +439,9 @@ New Compiler Flags
438439
Matches MSVC behaviour by defining ``__STDC__`` to ``1`` when
439440
MSVC compatibility mode is used. It has no effect for C++ code.
440441

442+
- ``-Wc++2c-compat`` group was added to help migrating existing codebases
443+
to C++26.
444+
441445
Deprecated Compiler Flags
442446
-------------------------
443447

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,8 @@ def CXX20CompatPedantic : DiagGroup<"c++20-compat-pedantic",
420420
def : DiagGroup<"c++2a-compat", [CXX20Compat]>;
421421
def : DiagGroup<"c++2a-compat-pedantic", [CXX20CompatPedantic]>;
422422

423+
def CXX26Compat : DiagGroup<"c++2c-compat", [DeleteIncomplete]>;
424+
423425
def ExitTimeDestructors : DiagGroup<"exit-time-destructors">;
424426
def FlexibleArrayExtensions : DiagGroup<"flexible-array-extensions">;
425427
def FourByteMultiChar : DiagGroup<"four-char-constants">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7989,8 +7989,11 @@ def ext_delete_void_ptr_operand : ExtWarn<
79897989
def err_ambiguous_delete_operand : Error<
79907990
"ambiguous conversion of delete expression of type %0 to a pointer">;
79917991
def warn_delete_incomplete : Warning<
7992-
"deleting pointer to incomplete type %0 may cause undefined behavior">,
7992+
"deleting pointer to incomplete type %0 is incompatible with C++2c"
7993+
" and may cause undefined behavior">,
79937994
InGroup<DeleteIncomplete>;
7995+
def err_delete_incomplete : Error<
7996+
"cannot delete pointer to incomplete type %0">;
79947997
def err_delete_incomplete_class_type : Error<
79957998
"deleting incomplete class type %0; no conversions to pointer type">;
79967999
def err_delete_explicit_conversion : Error<

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3719,8 +3719,11 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
37193719
// The C++ standard bans deleting a pointer to a non-object type, which
37203720
// effectively bans deletion of "void*". However, most compilers support
37213721
// this, so we treat it as a warning unless we're in a SFINAE context.
3722-
Diag(StartLoc, diag::ext_delete_void_ptr_operand)
3723-
<< Type << Ex.get()->getSourceRange();
3722+
// But we still prohibit this since C++26.
3723+
Diag(StartLoc, LangOpts.CPlusPlus26 ? diag::err_delete_incomplete
3724+
: diag::ext_delete_void_ptr_operand)
3725+
<< (LangOpts.CPlusPlus26 ? Pointee : Type)
3726+
<< Ex.get()->getSourceRange();
37243727
} else if (Pointee->isFunctionType() || Pointee->isVoidType() ||
37253728
Pointee->isSizelessType()) {
37263729
return ExprError(Diag(StartLoc, diag::err_delete_operand)
@@ -3729,7 +3732,10 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
37293732
// FIXME: This can result in errors if the definition was imported from a
37303733
// module but is hidden.
37313734
if (!RequireCompleteType(StartLoc, Pointee,
3732-
diag::warn_delete_incomplete, Ex.get())) {
3735+
LangOpts.CPlusPlus26
3736+
? diag::err_delete_incomplete
3737+
: diag::warn_delete_incomplete,
3738+
Ex.get())) {
37333739
if (const RecordType *RT = PointeeElem->getAs<RecordType>())
37343740
PointeeRD = cast<CXXRecordDecl>(RT->getDecl());
37353741
}

clang/test/CXX/drs/cwg5xx.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
// RUN: %clang_cc1 -std=c++98 %s -verify=expected,cxx98-11,cxx98-14,cxx98-17,cxx98 -fexceptions -fcxx-exceptions -pedantic-errors
2-
// RUN: %clang_cc1 -std=c++11 %s -verify=expected,cxx98-11,cxx98-14,cxx98-17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
3-
// RUN: %clang_cc1 -std=c++14 %s -verify=expected,cxx98-14,cxx98-17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
4-
// RUN: %clang_cc1 -std=c++17 %s -verify=expected,since-cxx17,cxx98-17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
5-
// RUN: %clang_cc1 -std=c++20 %s -verify=expected,since-cxx20,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
6-
// RUN: %clang_cc1 -std=c++23 %s -verify=expected,since-cxx23,since-cxx20,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
1+
// RUN: %clang_cc1 -std=c++98 %s -verify=expected,cxx98-23,cxx98-11,cxx98-14,cxx98-17,cxx98 -fexceptions -fcxx-exceptions -pedantic-errors
2+
// RUN: %clang_cc1 -std=c++11 %s -verify=expected,cxx98-23,cxx98-11,cxx98-14,cxx98-17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
3+
// RUN: %clang_cc1 -std=c++14 %s -verify=expected,cxx98-23,cxx98-14,cxx98-17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
4+
// RUN: %clang_cc1 -std=c++17 %s -verify=expected,cxx98-23,since-cxx17,cxx98-17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
5+
// RUN: %clang_cc1 -std=c++20 %s -verify=expected,cxx98-23,since-cxx20,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
6+
// RUN: %clang_cc1 -std=c++23 %s -verify=expected,cxx98-23,since-cxx23,since-cxx20,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
7+
// RUN: %clang_cc1 -std=c++2c %s -verify=expected,since-cxx26,since-cxx23,since-cxx20,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
78

89
#if __cplusplus == 199711L
910
#define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
@@ -901,7 +902,8 @@ namespace cwg573 { // cwg573: no
901902
void *d = reinterpret_cast<void*>(c);
902903
// cxx98-error@-1 {{cast between pointer-to-function and pointer-to-object is an extension}}
903904
void f() { delete a; }
904-
// expected-error@-1 {{cannot delete expression with pointer-to-'void' type 'void *'}}
905+
// cxx98-23-error@-1 {{cannot delete expression with pointer-to-'void' type 'void *'}}
906+
// since-cxx26-error@-2 {{cannot delete pointer to incomplete type 'void'}}
905907
int n = d - a;
906908
// expected-error@-1 {{arithmetic on pointers to void}}
907909
// FIXME: This is ill-formed.
@@ -1238,11 +1240,13 @@ namespace cwg599 { // cwg599: partial
12381240
struct V { operator int*(); operator Fn*(); };
12391241
void f(void *p, void (*q)(), S s, T t, U u, V v) {
12401242
delete p;
1241-
// expected-error@-1 {{cannot delete expression with pointer-to-'void' type 'void *'}}
1243+
// cxx98-23-error@-1 {{cannot delete expression with pointer-to-'void' type 'void *'}}
1244+
// since-cxx26-error@-2 {{cannot delete pointer to incomplete type 'void'}}
12421245
delete q;
12431246
// expected-error@-1 {{cannot delete expression of type 'void (*)()'}}
12441247
delete s;
1245-
// expected-error@-1 {{cannot delete expression with pointer-to-'void' type 'void *'}}
1248+
// cxx98-23-error@-1 {{cannot delete expression with pointer-to-'void' type 'void *'}}
1249+
// since-cxx26-error@-2 {{cannot delete pointer to incomplete type 'void'}}
12461250
delete t;
12471251
// expected-error@-1 {{cannot delete expression of type 'T'}}
12481252
// FIXME: This is valid, but is rejected due to a non-conforming GNU

clang/test/OpenMP/deferred-diags.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ namespace TestDeleteIncompleteClassDefinition {
4141
struct a;
4242
struct b {
4343
b() {
44-
delete c; // expected-warning {{deleting pointer to incomplete type 'a' may cause undefined behavior}}
44+
delete c; // expected-warning {{deleting pointer to incomplete type 'a' is incompatible with C++2c and may cause undefined behavior}}
4545
}
4646
a *c;
4747
};

clang/test/SemaCXX/new-delete.cpp

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
// RUN: %clang_cc1 -fsyntax-only -verify=expected,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++98
2-
// RUN: %clang_cc1 -fsyntax-only -verify=expected,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++11
3-
// RUN: %clang_cc1 -fsyntax-only -verify=expected,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++14
4-
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++17
5-
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17,cxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++20
1+
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98-23,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++98
2+
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98-23,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++11
3+
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98-23,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++14
4+
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98-23,cxx17,precxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++17
5+
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98-23,cxx17,cxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++20
6+
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98-23,cxx17,cxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++23
7+
// RUN: %clang_cc1 -fsyntax-only -verify=expected,since-cxx26,cxx17,cxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++2c
68

79
// FIXME Location is (frontend)
810
// cxx17-note@*:* {{candidate function not viable: requires 2 arguments, but 3 were provided}}
@@ -172,8 +174,12 @@ void bad_deletes()
172174
{
173175
delete 0; // expected-error {{cannot delete expression of type 'int'}}
174176
delete [0] (int*)0; // expected-error {{expected variable name or 'this' in lambda capture list}}
175-
delete (void*)0; // expected-warning {{cannot delete expression with pointer-to-'void' type 'void *'}}
176-
delete (T*)0; // expected-warning {{deleting pointer to incomplete type}}
177+
delete (void*)0;
178+
// cxx98-23-warning@-1 {{cannot delete expression with pointer-to-'void' type 'void *'}}
179+
// since-cxx26-error@-2 {{cannot delete pointer to incomplete type 'void'}}
180+
delete (T*)0;
181+
// cxx98-23-warning@-1 {{deleting pointer to incomplete type}}
182+
// since-cxx26-error@-2 {{cannot delete pointer to incomplete type 'T'}}
177183
::S::delete (int*)0; // expected-error {{expected unqualified-id}}
178184
}
179185

@@ -513,8 +519,10 @@ namespace DeleteIncompleteClass {
513519

514520
namespace DeleteIncompleteClassPointerError {
515521
struct A; // expected-note {{forward declaration}}
516-
void f(A *x) { 1+delete x; } // expected-warning {{deleting pointer to incomplete type}} \
517-
// expected-error {{invalid operands to binary expression}}
522+
void f(A *x) { 1+delete x; }
523+
// expected-error@-1 {{invalid operands to binary expression}}
524+
// cxx98-23-warning@-2 {{deleting pointer to incomplete type}}
525+
// since-cxx26-error@-3 {{cannot delete pointer to incomplete type 'A'}}
518526
}
519527

520528
namespace PR10504 {

clang/www/cxx_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
213213
<tr>
214214
<td>Deleting a Pointer to an Incomplete Type Should be Ill-formed</td>
215215
<td><a href="https://wg21.link/P3144">P3144R2</a></td>
216-
<td class="none" align="center">No</td>
216+
<td class="Unreleased" align="center">Clang 19</td>
217217
</tr>
218218
<tr>
219219
<td>Ordering of constraints involving fold expressions</td>

0 commit comments

Comments
 (0)