[clang] [Clang] Change how the argument of a delete expression is converted (PR #92814)
via cfe-commits
cfe-commits at lists.llvm.org
Mon May 20 12:38:53 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Mital Ashok (MitalAshok)
<details>
<summary>Changes</summary>
A new warning -Wdelete-array is issued for the now-accepted delete of an array, which becomes a pointer to the first element after an array-to-pointer conversion.
The GNU extension of deleting a void pointer is still accepted, but if that void pointer comes from a conversion operator, a conversion to a pointer to an object type takes priority before conversions are rechecked to allow conversion to a void pointer. This means that previously ambiguous contextual conversions where there was a conversion to a void pointer and an object pointer now unambiguously pick the conversion to an object pointer.
---
Full diff: https://github.com/llvm/llvm-project/pull/92814.diff
7 Files Affected:
- (modified) clang/docs/ReleaseNotes.rst (+4)
- (modified) clang/include/clang/Basic/DiagnosticGroups.td (+1)
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+1)
- (modified) clang/lib/Sema/SemaExprCXX.cpp (+105-82)
- (modified) clang/test/CXX/drs/cwg5xx.cpp (+2-7)
- (modified) clang/test/Parser/cxx2c-delete-with-message.cpp (+3-1)
- (modified) clang/www/cxx_dr_status.html (+1-1)
``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 81e9d0423f96a..6e769f1f99ceb 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -749,6 +749,10 @@ Bug Fixes to C++ Support
- Clang now correctly diagnoses when the current instantiation is used as an incomplete base class.
- Clang no longer treats ``constexpr`` class scope function template specializations of non-static members
as implicitly ``const`` in language modes after C++11.
+- Fix delete-expression operand not undergoing array-to-pointer conversion. Now warn ``-Wdelete-array`` when
+ trying to delete an array object.
+- Fix GNU extension that allows deleting ``void *`` making some deletes of class type ambiguous when there
+ is an object pointer and void pointer conversion operator. Now chooses the object pointer conversion.
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 4cb4f3d999f7a..410c31a25db03 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -168,6 +168,7 @@ def UndefinedFuncTemplate : DiagGroup<"undefined-func-template">;
def MissingNoEscape : DiagGroup<"missing-noescape">;
def DefaultedFunctionDeleted : DiagGroup<"defaulted-function-deleted">;
+def DeleteArray : DiagGroup<"delete-array">;
def DeleteIncomplete : DiagGroup<"delete-incomplete">;
def DeleteNonAbstractNonVirtualDtor : DiagGroup<"delete-non-abstract-non-virtual-dtor">;
def DeleteAbstractNonVirtualDtor : DiagGroup<"delete-abstract-non-virtual-dtor">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e3c65cba4886a..580f8e57804fa 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7930,6 +7930,7 @@ def ext_default_init_const : ExtWarn<
"is a Microsoft extension">,
InGroup<MicrosoftConstInit>;
def err_delete_operand : Error<"cannot delete expression of type %0">;
+def warn_delete_array : Warning<"deleting array of type %0">, InGroup<DeleteArray>;
def ext_delete_void_ptr_operand : ExtWarn<
"cannot delete expression with pointer-to-'void' type %0">,
InGroup<DeleteIncomplete>;
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f543e006060d6..9a1e149a4af8e 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -3675,13 +3675,60 @@ void Sema::AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation DeleteLoc,
}
}
+namespace {
+class DeleteConverter : public Sema::ContextualImplicitConverter {
+public:
+ bool AllowVoidPointer = false;
+
+ DeleteConverter() : ContextualImplicitConverter(false, true) {}
+
+ bool match(QualType ConvType) override {
+ return ConvType->isObjectPointerType() &&
+ (AllowVoidPointer || !ConvType->isVoidPointerType());
+ }
+
+ using SDB = Sema::SemaDiagnosticBuilder;
+
+ SDB diagnoseNoMatch(Sema &S, SourceLocation Loc, QualType T) override {
+ return S.Diag(Loc, diag::err_delete_operand) << T;
+ }
+
+ SDB diagnoseIncomplete(Sema &S, SourceLocation Loc, QualType T) override {
+ return S.Diag(Loc, diag::err_delete_incomplete_class_type) << T;
+ }
+
+ SDB diagnoseExplicitConv(Sema &S, SourceLocation Loc, QualType T,
+ QualType ConvTy) override {
+ return S.Diag(Loc, diag::err_delete_explicit_conversion) << T << ConvTy;
+ }
+
+ SDB noteExplicitConv(Sema &S, CXXConversionDecl *Conv,
+ QualType ConvTy) override {
+ return S.Diag(Conv->getLocation(), diag::note_delete_conversion) << ConvTy;
+ }
+
+ SDB diagnoseAmbiguous(Sema &S, SourceLocation Loc, QualType T) override {
+ return S.Diag(Loc, diag::err_ambiguous_delete_operand) << T;
+ }
+
+ SDB noteAmbiguous(Sema &S, CXXConversionDecl *Conv,
+ QualType ConvTy) override {
+ return S.Diag(Conv->getLocation(), diag::note_delete_conversion) << ConvTy;
+ }
+
+ SDB diagnoseConversion(Sema &S, SourceLocation Loc, QualType T,
+ QualType ConvTy) override {
+ llvm_unreachable("conversion functions are permitted");
+ }
+};
+} // namespace
+
/// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in:
/// @code ::delete ptr; @endcode
/// or
/// @code delete [] ptr; @endcode
-ExprResult
-Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
- bool ArrayForm, Expr *ExE) {
+ExprResult Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
+ bool ArrayForm, Expr *Ex) {
// C++ [expr.delete]p1:
// The operand shall have a pointer type, or a class type having a single
// non-explicit conversion function to a pointer type. The result has type
@@ -3689,88 +3736,61 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
//
// DR599 amends "pointer type" to "pointer to object type" in both cases.
- ExprResult Ex = ExE;
FunctionDecl *OperatorDelete = nullptr;
bool ArrayFormAsWritten = ArrayForm;
bool UsualArrayDeleteWantsSize = false;
- if (!Ex.get()->isTypeDependent()) {
- // Perform lvalue-to-rvalue cast, if needed.
- Ex = DefaultLvalueConversion(Ex.get());
- if (Ex.isInvalid())
+ if (!Ex->isTypeDependent()) {
+ if (ExprResult Conv = DefaultLvalueConversion(Ex); Conv.isInvalid())
return ExprError();
-
- QualType Type = Ex.get()->getType();
-
- class DeleteConverter : public ContextualImplicitConverter {
- public:
- DeleteConverter() : ContextualImplicitConverter(false, true) {}
-
- bool match(QualType ConvType) override {
- // FIXME: If we have an operator T* and an operator void*, we must pick
- // the operator T*.
- if (const PointerType *ConvPtrType = ConvType->getAs<PointerType>())
- if (ConvPtrType->getPointeeType()->isIncompleteOrObjectType())
- return true;
- return false;
- }
-
- SemaDiagnosticBuilder diagnoseNoMatch(Sema &S, SourceLocation Loc,
- QualType T) override {
- return S.Diag(Loc, diag::err_delete_operand) << T;
- }
-
- SemaDiagnosticBuilder diagnoseIncomplete(Sema &S, SourceLocation Loc,
- QualType T) override {
- return S.Diag(Loc, diag::err_delete_incomplete_class_type) << T;
- }
-
- SemaDiagnosticBuilder diagnoseExplicitConv(Sema &S, SourceLocation Loc,
- QualType T,
- QualType ConvTy) override {
- return S.Diag(Loc, diag::err_delete_explicit_conversion) << T << ConvTy;
- }
-
- SemaDiagnosticBuilder noteExplicitConv(Sema &S, CXXConversionDecl *Conv,
- QualType ConvTy) override {
- return S.Diag(Conv->getLocation(), diag::note_delete_conversion)
- << ConvTy;
- }
-
- SemaDiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc,
- QualType T) override {
- return S.Diag(Loc, diag::err_ambiguous_delete_operand) << T;
+ else
+ Ex = Conv.get();
+ QualType Type = Ex->getType();
+
+ if (Type->isRecordType()) {
+ DeleteConverter Converter;
+ // Suppress diagnostics the first time around
+ Converter.Suppress = true;
+
+ if (ExprResult Conv =
+ PerformContextualImplicitConversion(StartLoc, Ex, Converter);
+ !Conv.isInvalid() && Conv.get() != Ex) {
+ Ex = Conv.get();
+ } else {
+ // As an extension, allow void pointers
+ Converter.AllowVoidPointer = true;
+ Converter.Suppress = false;
+ Conv = PerformContextualImplicitConversion(StartLoc, Ex, Converter);
+ if (Conv.isInvalid() || Conv.get() == Ex)
+ return ExprError();
+ Ex = Conv.get();
}
- SemaDiagnosticBuilder noteAmbiguous(Sema &S, CXXConversionDecl *Conv,
- QualType ConvTy) override {
- return S.Diag(Conv->getLocation(), diag::note_delete_conversion)
- << ConvTy;
- }
+ Type = Ex->getType();
+ assert(Converter.match(Type) && "PerformContextualImplicitConversion "
+ "returned something of the wrong type");
+ } else {
+ if (Type->isArrayType())
+ Diag(StartLoc, diag::warn_delete_array) << Type;
- SemaDiagnosticBuilder diagnoseConversion(Sema &S, SourceLocation Loc,
- QualType T,
- QualType ConvTy) override {
- llvm_unreachable("conversion functions are permitted");
+ if (ExprResult Conv = DefaultFunctionArrayLvalueConversion(Ex);
+ Conv.isInvalid())
+ return ExprError();
+ else
+ Ex = Conv.get();
+ Type = Ex->getType();
+ if (!Type->isObjectPointerType()) {
+ Diag(StartLoc, diag::err_delete_operand) << Type;
+ return ExprError();
}
- } Converter;
-
- Ex = PerformContextualImplicitConversion(StartLoc, Ex.get(), Converter);
- if (Ex.isInvalid())
- return ExprError();
- Type = Ex.get()->getType();
- if (!Converter.match(Type))
- // FIXME: PerformContextualImplicitConversion should return ExprError
- // itself in this case.
- return ExprError();
+ }
QualType Pointee = Type->castAs<PointerType>()->getPointeeType();
QualType PointeeElem = Context.getBaseElementType(Pointee);
if (Pointee.getAddressSpace() != LangAS::Default &&
!getLangOpts().OpenCLCPlusPlus)
- return Diag(Ex.get()->getBeginLoc(),
- diag::err_address_space_qualified_delete)
+ return Diag(Ex->getBeginLoc(), diag::err_address_space_qualified_delete)
<< Pointee.getUnqualifiedType()
<< Pointee.getQualifiers().getAddressSpaceAttributePrintValue();
@@ -3780,16 +3800,16 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
// effectively bans deletion of "void*". However, most compilers support
// this, so we treat it as a warning unless we're in a SFINAE context.
Diag(StartLoc, diag::ext_delete_void_ptr_operand)
- << Type << Ex.get()->getSourceRange();
+ << Type << Ex->getSourceRange();
} else if (Pointee->isFunctionType() || Pointee->isVoidType() ||
Pointee->isSizelessType()) {
return ExprError(Diag(StartLoc, diag::err_delete_operand)
- << Type << Ex.get()->getSourceRange());
+ << Type << Ex->getSourceRange());
} else if (!Pointee->isDependentType()) {
// FIXME: This can result in errors if the definition was imported from a
// module but is hidden.
- if (!RequireCompleteType(StartLoc, Pointee,
- diag::warn_delete_incomplete, Ex.get())) {
+ if (!RequireCompleteType(StartLoc, Pointee, diag::warn_delete_incomplete,
+ Ex)) {
if (const RecordType *RT = PointeeElem->getAs<RecordType>())
PointeeRD = cast<CXXRecordDecl>(RT->getDecl());
}
@@ -3797,7 +3817,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
if (Pointee->isArrayType() && !ArrayForm) {
Diag(StartLoc, diag::warn_delete_array_type)
- << Type << Ex.get()->getSourceRange()
+ << Type << Ex->getSourceRange()
<< FixItHint::CreateInsertion(getLocForEndOfToken(StartLoc), "[]");
ArrayForm = true;
}
@@ -3867,7 +3887,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
bool IsVirtualDelete = false;
if (PointeeRD) {
if (CXXDestructorDecl *Dtor = LookupDestructor(PointeeRD)) {
- CheckDestructorAccess(Ex.get()->getExprLoc(), Dtor,
+ CheckDestructorAccess(Ex->getExprLoc(), Dtor,
PDiag(diag::err_access_dtor) << PointeeElem);
IsVirtualDelete = Dtor->isVirtual();
}
@@ -3888,17 +3908,20 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
Qs.removeCVRQualifiers();
QualType Unqual = Context.getPointerType(
Context.getQualifiedType(Pointee.getUnqualifiedType(), Qs));
- Ex = ImpCastExprToType(Ex.get(), Unqual, CK_NoOp);
+ Ex = ImpCastExprToType(Ex, Unqual, CK_NoOp).get();
}
- Ex = PerformImplicitConversion(Ex.get(), ParamType, AA_Passing);
- if (Ex.isInvalid())
+ if (ExprResult Conv =
+ PerformImplicitConversion(Ex, ParamType, AA_Passing);
+ Conv.isInvalid())
return ExprError();
+ else
+ Ex = Conv.get();
}
}
- CXXDeleteExpr *Result = new (Context) CXXDeleteExpr(
- Context.VoidTy, UseGlobal, ArrayForm, ArrayFormAsWritten,
- UsualArrayDeleteWantsSize, OperatorDelete, Ex.get(), StartLoc);
+ CXXDeleteExpr *Result = new (Context)
+ CXXDeleteExpr(Context.VoidTy, UseGlobal, ArrayForm, ArrayFormAsWritten,
+ UsualArrayDeleteWantsSize, OperatorDelete, Ex, StartLoc);
AnalyzeDeleteExprMismatch(Result);
return Result;
}
diff --git a/clang/test/CXX/drs/cwg5xx.cpp b/clang/test/CXX/drs/cwg5xx.cpp
index 9d890f981348a..64ea340ae25d2 100644
--- a/clang/test/CXX/drs/cwg5xx.cpp
+++ b/clang/test/CXX/drs/cwg5xx.cpp
@@ -1230,11 +1230,11 @@ namespace cwg598 { // cwg598: yes
int &t = h(N::i);
}
-namespace cwg599 { // cwg599: partial
+namespace cwg599 { // cwg599: 19
typedef int Fn();
struct S { operator void*(); };
struct T { operator Fn*(); };
- struct U { operator int*(); operator void*(); }; // #cwg599-U
+ struct U { operator int*(); operator void*(); };
struct V { operator int*(); operator Fn*(); };
void f(void *p, void (*q)(), S s, T t, U u, V v) {
delete p;
@@ -1245,12 +1245,7 @@ namespace cwg599 { // cwg599: partial
// expected-error at -1 {{cannot delete expression with pointer-to-'void' type 'void *'}}
delete t;
// expected-error at -1 {{cannot delete expression of type 'T'}}
- // FIXME: This is valid, but is rejected due to a non-conforming GNU
- // extension allowing deletion of pointers to void.
delete u;
- // expected-error at -1 {{ambiguous conversion of delete expression of type 'U' to a pointer}}
- // expected-note@#cwg599-U {{conversion to pointer type 'int *'}}
- // expected-note@#cwg599-U {{conversion to pointer type 'void *'}}
delete v;
}
}
diff --git a/clang/test/Parser/cxx2c-delete-with-message.cpp b/clang/test/Parser/cxx2c-delete-with-message.cpp
index 1767a080a7dcd..36575bf42e824 100644
--- a/clang/test/Parser/cxx2c-delete-with-message.cpp
+++ b/clang/test/Parser/cxx2c-delete-with-message.cpp
@@ -46,6 +46,8 @@ U b = delete ("hello"), c, d = delete ("hello"); // expected-error 2 {{only func
struct C {
T e = delete ("hello"); // expected-error {{'= delete' is a function definition and must occur in a standalone declaration}}
- U f = delete ("hello"); // expected-error {{cannot delete expression of type 'const char[6]'}}
+ U f = delete ("hello");
+// expected-warning at -1 {{deleting array of type 'const char[6]'}}
+// expected-error at -2 {{cannot initialize a member subobject of type 'U' (aka 'int') with an rvalue of type 'void'}}
};
}
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 9d458330f5376..2bcab4be98b40 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -3636,7 +3636,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/599.html">599</a></td>
<td>CD2</td>
<td>Deleting a null function pointer</td>
- <td class="partial" align="center">Partial</td>
+ <td class="unreleased" align="center">Clang 19</td>
</tr>
<tr id="600">
<td><a href="https://cplusplus.github.io/CWG/issues/600.html">600</a></td>
``````````
</details>
https://github.com/llvm/llvm-project/pull/92814
More information about the cfe-commits
mailing list