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/2] [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/2] 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

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to