https://github.com/MitalAshok updated https://github.com/llvm/llvm-project/pull/92814
>From 43e9f8fe5cdb19c0f57a00b352592e56e470ffe7 Mon Sep 17 00:00:00 2001 From: Mital Ashok <mi...@mitalashok.co.uk> Date: Mon, 20 May 2024 20:18:48 +0100 Subject: [PATCH 1/3] [Clang] Change how the argument of a delete expression is converted 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. --- clang/docs/ReleaseNotes.rst | 4 + clang/include/clang/Basic/DiagnosticGroups.td | 1 + .../clang/Basic/DiagnosticSemaKinds.td | 1 + clang/lib/Sema/SemaExprCXX.cpp | 187 ++++++++++-------- clang/test/CXX/drs/cwg5xx.cpp | 9 +- .../test/Parser/cxx2c-delete-with-message.cpp | 4 +- clang/www/cxx_dr_status.html | 2 +- 7 files changed, 117 insertions(+), 91 deletions(-) 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@-1 {{cannot delete expression with pointer-to-'void' type 'void *'}} delete t; // expected-error@-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@-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@-1 {{deleting array of type 'const char[6]'}} +// expected-error@-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> >From 123eb0f2f386bbd1168752028d55418857ee2911 Mon Sep 17 00:00:00 2001 From: Mital Ashok <mi...@mitalashok.co.uk> Date: Tue, 21 May 2024 14:49:42 +0100 Subject: [PATCH 2/3] Add more tests for contextual conversions --- clang/lib/Sema/SemaOverload.cpp | 2 +- clang/test/CXX/conv/p5.cpp | 152 ++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 clang/test/CXX/conv/p5.cpp diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 2eb25237a0de6..18445613f5a38 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6735,7 +6735,7 @@ ExprResult Sema::PerformContextualImplicitConversion( if (ToType.isNull()) ToType = CurToType.getUnqualifiedType(); else if (HasUniqueTargetType && - (CurToType.getUnqualifiedType() != ToType)) + !Context.hasSameType(CurToType.getUnqualifiedType(), ToType)) HasUniqueTargetType = false; } ViableConversions.addDecl(I.getDecl(), I.getAccess()); diff --git a/clang/test/CXX/conv/p5.cpp b/clang/test/CXX/conv/p5.cpp new file mode 100644 index 0000000000000..94ac6376c538e --- /dev/null +++ b/clang/test/CXX/conv/p5.cpp @@ -0,0 +1,152 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++98 %s -verify=cxx98,cxx98-cxx11,expected +// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -verify=cxx98-cxx11,cxx11,expected +// RUN: %clang_cc1 -fsyntax-only -std=c++14 %s -verify +// RUN: %clang_cc1 -fsyntax-only -std=c++20 %s -verify +// RUN: %clang_cc1 -fsyntax-only -std=c++23 %s -verify=cxx23,expected + +// Introduced in C++14 by N3323 +// Moved in C++20 to [conv.general]p5 by P1076R1 + +template<class T> +struct X0 { + operator T(); +}; + +void f0() { + delete X0<int*>(); + delete X0<int*&>(); + delete X0<int*&&>(); // cxx98-warning {{C++11 extension}} + switch (X0<int>()) {} + switch (X0<int&>()) {} + switch (X0<int&&>()) {} // cxx98-warning {{C++11 extension}} + delete X0<int(&)[1]>(); +// expected-error@-1 {{cannot delete expression of type 'X0<int (&)[1]>'}} +} + +template<class T> +struct zero_init { + operator T&(); // #zero_init_mut + operator T() const; // #zero_init_const +}; + +void f1(zero_init<int*> p, const zero_init<int*> q) { + delete p; +// cxx98-cxx11-error@-1 {{ambiguous conversion of delete expression of type 'zero_init<int *>' to a pointer}} +// cxx98-cxx11-note@#zero_init_mut {{conversion to pointer type 'int *'}} +// cxx98-cxx11-note@#zero_init_const {{conversion to pointer type 'int *'}} + delete q; +// cxx98-cxx11-error@-1 {{ambiguous conversion of delete expression of type 'const zero_init<int *>' to a pointer}} +// cxx98-cxx11-note@#zero_init_mut {{conversion to pointer type 'int *'}} +// cxx98-cxx11-note@#zero_init_const {{conversion to pointer type 'int *'}} +} + +void f2(zero_init<int> i, const zero_init<int> j) { + switch (i) {} +// cxx98-cxx11-error@-1 {{multiple conversions from switch condition type 'zero_init<int>' to an integral or enumeration type}} +// cxx98-cxx11-note@#zero_init_mut {{conversion to integral type 'int'}} +// cxx98-cxx11-note@#zero_init_const {{conversion to integral type 'int'}} + switch (j) {} +// cxx98-cxx11-error@-1 {{multiple conversions from switch condition type 'const zero_init<int>' to an integral or enumeration type}} +// cxx98-cxx11-note@#zero_init_mut {{conversion to integral type 'int'}} +// cxx98-cxx11-note@#zero_init_const {{conversion to integral type 'int'}} +} + +template<class T> +struct X1 { + template<class Result = T> // cxx98-warning {{C++11 extension}} + operator Result(); +}; + +void f2(X1<int> i, X1<int*> p) { + delete p; +// expected-error@-1 {{cannot delete expression of type 'X1<int *>'}} + switch (i) {} +// expected-error@-1 {{statement requires expression of integer type ('X1<int>' invalid)}} +} + +#if __cplusplus >= 201102L +template<class T> +struct X2 { + T v; + constexpr operator T() const { return v; } +}; + +enum E { E_0, E_4 = 4, E_5 = 5 }; +enum class EC : unsigned { _0 }; + +static constexpr int _1 = 1; +static constexpr unsigned _3 = 3; +static constexpr E _5 = E::E_5; +static constexpr EC EC_0 = EC::_0; + +void f3() { + switch (0) { + case X2<int>{0}:; + case X2<const int&>{_1}:; + case X2<unsigned>{2}:; + case X2<const unsigned&>{_3}:; + case X2<E>{E_4}:; + case X2<const E&>{_5}:; + } + switch (EC::_0) { case X2<EC>{}:; } + switch (EC::_0) { case X2<const EC&>{EC_0}:; } + + int a1[X2<__SIZE_TYPE__>{1}]; + new int[1][X2<__SIZE_TYPE__>{1}]; + // FIXME: Should only allow conversion operators to std::size_t + int a2[X2<int>{1}]; + new int[1][X2<int>{1}]; +} +#endif + +#if __cplusplus >= 202302L +template<typename Derived, typename T, typename U> +struct X3 { + T val; + // There is only one type to convert to, so multiple overloads does not affect search result for contextual conversion (T) + constexpr operator T(this Derived&& self) { return self.val; } + constexpr operator U(this X3&& self) { return self.val; } +}; + +template<typename T> +struct X4 : X3<X4<T>, T, T> { + constexpr X3<X4<T>, T, T>&& base(this X3<X4<T>, T, T>&& self) { return self; } +}; + +void f4() { + delete X4<int*>{}; + delete X4<int*>{}.base(); + switch (X4<int>{}) { case 0:; } + switch (X4<int>{}.base()) { case 0:; } + switch (1) { + case X4<int>{}: + case X4<int>{{1}}.base(): + } +} + +struct Unrelated {}; + +template<typename T1, typename T2> +struct X5 { + operator T1(this X5&&); // #X5-1 + operator T2(this Unrelated&&); // #X5-2 +}; + +void f5() { + delete X5<int*, int*>{}; + delete X5<int*, int*&>{}; + delete X5<int* volatile&&, int* const&>{}; + delete X5<int*, const int*>{}; +// cxx23-error@-1 {{ambiguous conversion of delete expression of type 'X5<int *, const int *>' to a pointer}} +// cxx23-note@#X5-1 {{conversion to pointer type 'int *'}} +// cxx23-note@#X5-2 {{conversion to pointer type 'const int *'}} + switch (X5<int, int>{}) {} + switch (X5<int, const int>{}) {} + switch (X5<int, int&>{}) {} + switch (X5<volatile int&&, const int&>{}) {} + switch (X5<int, unsigned>{}) {} +// cxx23-error@-1 {{multiple conversions from switch condition type 'X5<int, unsigned int>' to an integral or enumeration type}} +// cxx23-note@#X5-1 {{conversion to integral type 'int'}} +// cxx23-note@#X5-2 {{conversion to integral type 'unsigned int'}} +} +#endif >From 4b8ef065888380b5cc019776c0fd2f6cce6bf0ae Mon Sep 17 00:00:00 2001 From: Mital Ashok <mi...@mitalashok.co.uk> Date: Tue, 21 May 2024 15:31:08 +0100 Subject: [PATCH 3/3] Add reference tests to CWG599 tests --- clang/test/CXX/drs/cwg5xx.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/clang/test/CXX/drs/cwg5xx.cpp b/clang/test/CXX/drs/cwg5xx.cpp index 64ea340ae25d2..ca962ff2e2f0e 100644 --- a/clang/test/CXX/drs/cwg5xx.cpp +++ b/clang/test/CXX/drs/cwg5xx.cpp @@ -1236,6 +1236,12 @@ namespace cwg599 { // cwg599: 19 struct T { operator Fn*(); }; struct U { operator int*(); operator void*(); }; struct V { operator int*(); operator Fn*(); }; + struct SR { operator void*&(); }; + struct TR { operator Fn*&(); }; + struct UR { operator int*&(); operator void*&(); }; + struct VR { operator int*&(); operator Fn*&(); }; + struct UR2 { operator int*&(); operator void*(); }; + struct VR2 { operator int*(); operator Fn*&(); }; void f(void *p, void (*q)(), S s, T t, U u, V v) { delete p; // expected-error@-1 {{cannot delete expression with pointer-to-'void' type 'void *'}} @@ -1247,5 +1253,13 @@ namespace cwg599 { // cwg599: 19 // expected-error@-1 {{cannot delete expression of type 'T'}} delete u; delete v; + delete SR(); + // expected-error@-1 {{cannot delete expression with pointer-to-'void' type 'void *'}} + delete TR(); + // expected-error@-1 {{cannot delete expression of type 'TR'}} + delete UR(); + delete VR(); + delete UR2(); + delete VR2(); } } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits