[clang] [Clang] [C++26] Implement P2573R2: `= delete("should have a reason");` (PR #86526)

via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 10 02:58:27 PDT 2024


https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/86526

>From 98af47e8ccc633016c14b91c221f9f8fc620f068 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 25 Mar 2024 14:46:52 +0100
Subject: [PATCH 01/22] [Clang] Parse `= delete("message")`

---
 clang/include/clang/AST/Decl.h                | 13 ++++++++
 clang/include/clang/Parse/Parser.h            |  1 +
 clang/include/clang/Sema/Sema.h               |  6 ++--
 clang/lib/AST/Decl.cpp                        |  4 +--
 clang/lib/AST/DeclPrinter.cpp                 |  9 +++--
 clang/lib/AST/TextNodeDumper.cpp              |  3 ++
 clang/lib/Parse/ParseCXXInlineMethods.cpp     | 24 +++++++++++++-
 clang/lib/Parse/Parser.cpp                    |  4 ++-
 clang/lib/Sema/SemaDeclCXX.cpp                | 10 +++---
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  6 ++++
 .../test/Parser/cxx2c-delete-with-message.cpp | 33 +++++++++++++++++++
 11 files changed, 100 insertions(+), 13 deletions(-)
 create mode 100644 clang/test/Parser/cxx2c-delete-with-message.cpp

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index a5879591f4c659..24f0394577e228 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -2013,6 +2013,12 @@ class FunctionDecl : public DeclaratorDecl,
     DefaultedFunctionInfo *DefaultedInfo;
   };
 
+  /// Message that indicates why this function was deleted.
+  ///
+  /// FIXME: Figure out where to actually put this; maybe in the
+  /// 'DefaultedInfo' above?
+  StringLiteral *DeletedMessage;
+
   unsigned ODRHash;
 
   /// End part of this FunctionDecl's source range.
@@ -2483,6 +2489,10 @@ class FunctionDecl : public DeclaratorDecl,
   }
 
   void setDeletedAsWritten(bool D = true) { FunctionDeclBits.IsDeleted = D; }
+  void setDeletedWithMessage(StringLiteral* Message) {
+    FunctionDeclBits.IsDeleted = true;
+    DeletedMessage = Message;
+  }
 
   /// Determines whether this function is "main", which is the
   /// entry point into an executable program.
@@ -2638,6 +2648,9 @@ class FunctionDecl : public DeclaratorDecl,
       AC.push_back(TRC);
   }
 
+  /// Get the message that indicates why this function was deleted.
+  StringLiteral *getDeletedMessage() const { return DeletedMessage; }
+
   void setPreviousDeclaration(FunctionDecl * PrevDecl);
 
   FunctionDecl *getCanonicalDecl() override;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 14df75180ef321..559e6416b7dfb3 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1600,6 +1600,7 @@ class Parser : public CodeCompletionHandler {
                                      const ParsedTemplateInfo &TemplateInfo,
                                      const VirtSpecifiers &VS,
                                      SourceLocation PureSpecLoc);
+  StringLiteral *ParseCXXDeletedFunctionMessage();
   void ParseCXXNonStaticMemberInitializer(Decl *VarD);
   void ParseLexedAttributes(ParsingClass &Class);
   void ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 64de39acc72176..919f8f6b0a35a2 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4926,10 +4926,12 @@ class Sema final {
                                SourceLocation EqualLoc);
 
   void ActOnPureSpecifier(Decl *D, SourceLocation PureSpecLoc);
-  void SetDeclDeleted(Decl *dcl, SourceLocation DelLoc);
+  void SetDeclDeleted(Decl *dcl, SourceLocation DelLoc,
+                      StringLiteral *Message = nullptr);
   void SetDeclDefaulted(Decl *dcl, SourceLocation DefaultLoc);
 
-  void SetFunctionBodyKind(Decl *D, SourceLocation Loc, FnBodyKind BodyKind);
+  void SetFunctionBodyKind(Decl *D, SourceLocation Loc, FnBodyKind BodyKind,
+                           StringLiteral *DeletedMessage = nullptr);
   void ActOnStartTrailingRequiresClause(Scope *S, Declarator &D);
   ExprResult ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr);
   ExprResult ActOnRequiresClause(ExprResult ConstraintExpr);
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 95900afdd2c5d8..b5e61f0eceb6e4 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3043,8 +3043,8 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
                            Expr *TrailingRequiresClause)
     : DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo,
                      StartLoc),
-      DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0),
-      EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) {
+      DeclContext(DK), redeclarable_base(C), Body(), DeletedMessage(nullptr),
+      ODRHash(0), EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) {
   assert(T.isNull() || T->isFunctionType());
   FunctionDeclBits.SClass = S;
   FunctionDeclBits.IsInline = isInlineSpecified;
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index b701581b2474a9..d04cdd5a10e033 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -873,9 +873,14 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
 
   if (D->isPureVirtual())
     Out << " = 0";
-  else if (D->isDeletedAsWritten())
+  else if (D->isDeletedAsWritten()) {
     Out << " = delete";
-  else if (D->isExplicitlyDefaulted())
+    if (const auto *M = D->getDeletedMessage()) {
+      Out << "(";
+      M->outputString(Out);
+      Out << ")";
+    }
+  } else if (D->isExplicitlyDefaulted())
     Out << " = default";
   else if (D->doesThisDeclarationHaveABody()) {
     if (!Policy.TerseOutput) {
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index b683eb1edd8f13..4a2369330a35e7 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1936,6 +1936,9 @@ void TextNodeDumper::VisitFunctionDecl(const FunctionDecl *D) {
   if (D->isTrivial())
     OS << " trivial";
 
+  if (const auto *M = D->getDeletedMessage())
+    AddChild("delete message", [=] { Visit(M); });
+
   if (D->isIneligibleOrNotSelected())
     OS << (isa<CXXDestructorDecl>(D) ? " not_selected" : " ineligible");
 
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index d790060c17c049..c551023a1e9dbc 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -20,6 +20,27 @@
 
 using namespace clang;
 
+/// Parse the optional ("message") part of a deleted-function-body.
+StringLiteral *Parser::ParseCXXDeletedFunctionMessage() {
+  if (!Tok.is(tok::l_paren)) return nullptr;
+  StringLiteral *Message = nullptr;
+  BalancedDelimiterTracker BT{*this, tok::l_paren};
+  BT.consumeOpen();
+
+  if (isTokenStringLiteral()) {
+    ExprResult Res = ParseUnevaluatedStringLiteralExpression();
+    if (Res.isUsable())
+      Message = Res.getAs<StringLiteral>();
+  } else {
+    Diag(Tok.getLocation(), diag::err_expected_string_literal)
+      << /*Source='in'*/ 0 << "'delete'";
+    SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch);
+  }
+
+  BT.consumeClose();
+  return Message;
+}
+
 /// ParseCXXInlineMethodDef - We parsed and verified that the specified
 /// Declarator is a well formed C++ inline method definition. Now lex its body
 /// and store its tokens for parsing after the C++ class is complete.
@@ -70,7 +91,8 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(
                       ? diag::warn_cxx98_compat_defaulted_deleted_function
                       : diag::ext_defaulted_deleted_function)
         << 1 /* deleted */;
-      Actions.SetDeclDeleted(FnD, KWLoc);
+      StringLiteral* Message = ParseCXXDeletedFunctionMessage();
+      Actions.SetDeclDeleted(FnD, KWLoc, Message);
       Delete = true;
       if (auto *DeclAsFunction = dyn_cast<FunctionDecl>(FnD)) {
         DeclAsFunction->setRangeEnd(KWEndLoc);
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index cc0e41ed221c4f..d6f2b9f448cd52 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1404,6 +1404,7 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
 
   // Parse function body eagerly if it is either '= delete;' or '= default;' as
   // ActOnStartOfFunctionDef needs to know whether the function is deleted.
+  StringLiteral *DeletedMessage = nullptr;
   Sema::FnBodyKind BodyKind = Sema::FnBodyKind::Other;
   SourceLocation KWLoc;
   if (TryConsumeToken(tok::equal)) {
@@ -1415,6 +1416,7 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
                       : diag::ext_defaulted_deleted_function)
           << 1 /* deleted */;
       BodyKind = Sema::FnBodyKind::Delete;
+      DeletedMessage = ParseCXXDeletedFunctionMessage();
     } else if (TryConsumeToken(tok::kw_default, KWLoc)) {
       Diag(KWLoc, getLangOpts().CPlusPlus11
                       ? diag::warn_cxx98_compat_defaulted_deleted_function
@@ -1473,7 +1475,7 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
   D.getMutableDeclSpec().abort();
 
   if (BodyKind != Sema::FnBodyKind::Other) {
-    Actions.SetFunctionBodyKind(Res, KWLoc, BodyKind);
+    Actions.SetFunctionBodyKind(Res, KWLoc, BodyKind, DeletedMessage);
     Stmt *GeneratedBody = Res ? Res->getBody() : nullptr;
     Actions.ActOnFinishFunctionBody(Res, GeneratedBody, false);
     return Res;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index e258a4f7c89415..e6d653aedd061c 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18108,7 +18108,7 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
   return ND;
 }
 
-void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc) {
+void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc, StringLiteral *Message) {
   AdjustDeclIfTemplate(Dcl);
 
   FunctionDecl *Fn = dyn_cast_or_null<FunctionDecl>(Dcl);
@@ -18157,7 +18157,7 @@ void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc) {
   // C++11 [dcl.fct.def.delete]p4:
   //  A deleted function is implicitly inline.
   Fn->setImplicitlyInline();
-  Fn->setDeletedAsWritten();
+  Fn->setDeletedWithMessage(Message);
 }
 
 void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) {
@@ -18270,11 +18270,11 @@ void Sema::DiagnoseReturnInConstructorExceptionHandler(CXXTryStmt *TryBlock) {
   }
 }
 
-void Sema::SetFunctionBodyKind(Decl *D, SourceLocation Loc,
-                               FnBodyKind BodyKind) {
+void Sema::SetFunctionBodyKind(Decl *D, SourceLocation Loc, FnBodyKind BodyKind,
+                               StringLiteral *DeletedMessage) {
   switch (BodyKind) {
   case FnBodyKind::Delete:
-    SetDeclDeleted(D, Loc);
+    SetDeclDeleted(D, Loc, DeletedMessage);
     break;
   case FnBodyKind::Default:
     SetDeclDefaulted(D, Loc);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index dc972018e7b281..549ffd4b0b6df5 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2202,6 +2202,9 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
   if (D->isLocalExternDecl())
     Function->setLocalExternDecl();
 
+  if (D->isDeletedAsWritten())
+    Function->setDeletedWithMessage(D->getDeletedMessage());
+
   DeclContext *LexicalDC = Owner;
   if (!isFriend && D->isOutOfLine() && !D->isLocalExternDecl()) {
     assert(D->getDeclContext()->isFileContext());
@@ -2627,6 +2630,9 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
   if (QualifierLoc)
     Method->setQualifierInfo(QualifierLoc);
 
+  if (D->isDeletedAsWritten())
+    Method->setDeletedWithMessage(D->getDeletedMessage());
+
   if (TemplateParams) {
     // Our resulting instantiation is actually a function template, since we
     // are substituting only the outer template parameters. For example, given
diff --git a/clang/test/Parser/cxx2c-delete-with-message.cpp b/clang/test/Parser/cxx2c-delete-with-message.cpp
new file mode 100644
index 00000000000000..67a4c944187d7f
--- /dev/null
+++ b/clang/test/Parser/cxx2c-delete-with-message.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s
+
+struct S {
+  void a() = delete;
+  void b() = delete(; // expected-error {{expected string literal}} expected-error {{expected ')'}} expected-note {{to match this '('}}
+  void c() = delete(); // expected-error {{expected string literal}}
+  void d() = delete(42); // expected-error {{expected string literal}}
+  void e() = delete("foo"[0]); // expected-error {{expected ')'}} expected-note {{to match this '('}}
+  void f() = delete("foo");
+
+  S() = delete("foo");
+  ~S() = delete("foo");
+  S(const S&) = delete("foo");
+  S(S&&) = delete("foo");
+  S& operator=(const S&) = delete("foo");
+  S& operator=(S&&) = delete("foo");
+};
+
+struct T {
+  T() = delete(); // expected-error {{expected string literal}}
+  ~T() = delete(); // expected-error {{expected string literal}}
+  T(const T&) = delete(); // expected-error {{expected string literal}}
+  T(T&&) = delete(); // expected-error {{expected string literal}}
+  T& operator=(const T&) = delete(); // expected-error {{expected string literal}}
+  T& operator=(T&&) = delete(); // expected-error {{expected string literal}}
+};
+
+void a() = delete;
+void b() = delete(; // expected-error {{expected string literal}} expected-error {{expected ')'}} expected-note {{to match this '('}}
+void c() = delete(); // expected-error {{expected string literal}}
+void d() = delete(42); // expected-error {{expected string literal}}
+void e() = delete("foo"[0]); // expected-error {{expected ')'}} expected-note {{to match this '('}}
+void f() = delete("foo");

>From 5ffac609e2cd0a365632974a72f944ed7f1a3779 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 25 Mar 2024 15:06:01 +0100
Subject: [PATCH 02/22] [Clang] Tentative serialisation

---
 clang/lib/Serialization/ASTReaderDecl.cpp     |  5 ++++
 clang/lib/Serialization/ASTWriterDecl.cpp     |  7 ++++++
 .../ast-dump-cxx2c-delete-with-message.cpp    | 23 +++++++++++++++++++
 3 files changed, 35 insertions(+)
 create mode 100644 clang/test/AST/ast-dump-cxx2c-delete-with-message.cpp

diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index a22f760408c634..37bc61b9a63a34 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1122,6 +1122,11 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
     }
   }
 
+  // FIXME: See ASTWriterDecl::VisitFunctionDecl.
+  if (FD->isDeletedAsWritten())
+    FD->setDeletedWithMessage(
+        cast_if_present<StringLiteral>(Record.readStmt()));
+
   if (Existing)
     mergeRedeclarable(FD, Existing, Redecl);
   else if (auto Kind = FD->getTemplatedKind();
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 86f64bf2a24250..af7dbe916419eb 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -761,6 +761,13 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
     }
   }
 
+  // FIXME: Hack: We're out of bits in FunctionDeclBits, so always
+  // add this even though it's 0 in the vast majority of cases. We
+  // might really want to consider storing this in the DefaultedFunctionInfo
+  // instead.
+  if (D->isDeletedAsWritten())
+    Record.AddStmt(D->getDeletedMessage());
+
   Record.push_back(D->param_size());
   for (auto *P : D->parameters())
     Record.AddDeclRef(P);
diff --git a/clang/test/AST/ast-dump-cxx2c-delete-with-message.cpp b/clang/test/AST/ast-dump-cxx2c-delete-with-message.cpp
new file mode 100644
index 00000000000000..ea16b97da23e40
--- /dev/null
+++ b/clang/test/AST/ast-dump-cxx2c-delete-with-message.cpp
@@ -0,0 +1,23 @@
+// Without serialization:
+// RUN: %clang_cc1 -ast-dump %s | FileCheck %s
+//
+// With serialization:
+// RUN: %clang_cc1 -emit-pch -o %t %s
+// RUN: %clang_cc1 -x c++ -include-pch %t -ast-dump-all  /dev/null | FileCheck %s
+
+struct S {
+  // CHECK:      CXXMethodDecl {{.*}} a 'void ()' delete
+  // CHECK-NEXT:   delete message: StringLiteral {{.*}} "foo"
+  void a() = delete("foo");
+
+  // CHECK:      FunctionTemplateDecl {{.*}} b
+  // CHECK-NEXT:   TemplateTypeParmDecl
+  // CHECK-NEXT:   CXXMethodDecl {{.*}} b 'void ()' delete
+  // CHECK-NEXT:     delete message: StringLiteral {{.*}} "bar"
+  template <typename>
+  void b() = delete("bar");
+};
+
+// CHECK:      FunctionDecl {{.*}} c 'void ()' delete
+// CHECK-NEXT:   delete message: StringLiteral {{.*}} "baz"
+void c() = delete("baz");

>From 8dc5f8b4eaca3ea7a4c41044af9e4329478403df Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 25 Mar 2024 16:55:03 +0100
Subject: [PATCH 03/22] [Clang] Sema and tests

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  7 ++--
 clang/include/clang/Sema/Sema.h               |  5 +++
 clang/lib/Sema/SemaExpr.cpp                   |  7 ++--
 clang/lib/Sema/SemaExprCXX.cpp                | 23 ++++++-------
 clang/lib/Sema/SemaOverload.cpp               | 32 ++++++++++++-------
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 11 ++-----
 .../SemaCXX/cxx2c-delete-with-message.cpp     | 30 +++++++++++++++++
 7 files changed, 76 insertions(+), 39 deletions(-)
 create mode 100644 clang/test/SemaCXX/cxx2c-delete-with-message.cpp

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d7ab1635cf12bc..37197b60ddfbb8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4661,11 +4661,10 @@ def err_ovl_no_viable_member_function_in_call : Error<
   "no matching member function for call to %0">;
 def err_ovl_ambiguous_call : Error<
   "call to %0 is ambiguous">;
-def err_ovl_deleted_call : Error<"call to deleted function %0">;
+def err_ovl_deleted_call : Error<"call to deleted"
+  "%select{| member}0 function %1%select{|: %3}2">;
 def err_ovl_ambiguous_member_call : Error<
   "call to member function %0 is ambiguous">;
-def err_ovl_deleted_member_call : Error<
-  "call to deleted member function %0">;
 def note_ovl_too_many_candidates : Note<
     "remaining %0 candidate%s0 omitted; "
     "pass -fshow-overloads=all to show them">;
@@ -8857,7 +8856,7 @@ def err_nontemporal_builtin_must_be_pointer_intfltptr_or_vector : Error<
   "address argument to nontemporal builtin must be a pointer to integer, float, "
   "pointer, or a vector of such types (%0 invalid)">;
 
-def err_deleted_function_use : Error<"attempt to use a deleted function">;
+def err_deleted_function_use : Error<"attempt to use a deleted function%select{|: %1}0">;
 def err_deleted_inherited_ctor_use : Error<
   "constructor inherited by %0 from base class %1 is implicitly deleted">;
 
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 919f8f6b0a35a2..9f6e2639508b2a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -8264,6 +8264,11 @@ class Sema final {
   bool IsFunctionConversion(QualType FromType, QualType ToType,
                             QualType &ResultTy);
   bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType);
+  void DiagnoseUseOfDeletedFunction(SourceLocation Loc, SourceRange Range,
+                                    DeclarationName Name,
+                                    OverloadCandidateSet &CandidateSet,
+                                    FunctionDecl *Fn, MultiExprArg Args,
+                                    bool IsMember = false);
 
   ExprResult InitializeExplicitObjectArgument(Sema &S, Expr *Obj,
                                               FunctionDecl *Fun);
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 8725b09f8546cf..468b5ed1010457 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -271,8 +271,11 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
         Diag(Loc, diag::err_deleted_inherited_ctor_use)
             << Ctor->getParent()
             << Ctor->getInheritedConstructor().getConstructor()->getParent();
-      else
-        Diag(Loc, diag::err_deleted_function_use);
+      else {
+        StringLiteral *Msg = FD->getDeletedMessage();
+        Diag(Loc, diag::err_deleted_function_use)
+          << !!Msg << (Msg ? Msg->getString() : StringRef());
+      }
       NoteDeletedFunction(FD);
       return true;
     }
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index c34a40fa7c81ac..ff1fb146544d40 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -2663,13 +2663,9 @@ static bool resolveAllocationOverload(
     return true;
 
   case OR_Deleted: {
-    if (Diagnose) {
-      Candidates.NoteCandidates(
-          PartialDiagnosticAt(R.getNameLoc(),
-                              S.PDiag(diag::err_ovl_deleted_call)
-                                  << R.getLookupName() << Range),
-          S, OCD_AllCandidates, Args);
-    }
+    if (Diagnose)
+      S.DiagnoseUseOfDeletedFunction(R.getNameLoc(), Range, R.getLookupName(),
+                                     Candidates, Best->Function, Args);
     return true;
   }
   }
@@ -3323,7 +3319,9 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
     // FIXME: DiagnoseUseOfDecl?
     if (Operator->isDeleted()) {
       if (Diagnose) {
-        Diag(StartLoc, diag::err_deleted_function_use);
+        StringLiteral *Msg = Operator->getDeletedMessage();
+        Diag(StartLoc, diag::err_deleted_function_use)
+          << !!Msg << (Msg ? Msg->getString() : StringRef());
         NoteDeletedFunction(Operator);
       }
       return true;
@@ -3927,14 +3925,11 @@ static bool resolveBuiltinNewDeleteOverload(Sema &S, CallExpr *TheCall,
         S, OCD_AmbiguousCandidates, Args);
     return true;
 
-  case OR_Deleted: {
-    Candidates.NoteCandidates(
-        PartialDiagnosticAt(R.getNameLoc(), S.PDiag(diag::err_ovl_deleted_call)
-                                                << R.getLookupName() << Range),
-        S, OCD_AllCandidates, Args);
+  case OR_Deleted:
+    S.DiagnoseUseOfDeletedFunction(R.getNameLoc(), Range, R.getLookupName(),
+                                   Candidates, Best->Function, Args);
     return true;
   }
-  }
   llvm_unreachable("Unreachable, bad result from BestViableFunction");
 }
 
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 4e03c3124e39ab..e4350df47c3570 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -14138,15 +14138,13 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
     break;
 
   case OR_Deleted: {
-    CandidateSet->NoteCandidates(
-        PartialDiagnosticAt(Fn->getBeginLoc(),
-                            SemaRef.PDiag(diag::err_ovl_deleted_call)
-                                << ULE->getName() << Fn->getSourceRange()),
-        SemaRef, OCD_AllCandidates, Args);
+    FunctionDecl *FDecl = (*Best)->Function;
+    SemaRef.DiagnoseUseOfDeletedFunction(Fn->getBeginLoc(),
+                                         Fn->getSourceRange(), ULE->getName(),
+                                         *CandidateSet, FDecl, Args);
 
     // We emitted an error for the unavailable/deleted function call but keep
     // the call in the AST.
-    FunctionDecl *FDecl = (*Best)->Function;
     ExprResult Res =
         SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl);
     if (Res.isInvalid())
@@ -15588,11 +15586,9 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
           *this, OCD_AmbiguousCandidates, Args);
       break;
     case OR_Deleted:
-      CandidateSet.NoteCandidates(
-          PartialDiagnosticAt(UnresExpr->getMemberLoc(),
-                              PDiag(diag::err_ovl_deleted_member_call)
-                                  << DeclName << MemExprE->getSourceRange()),
-          *this, OCD_AllCandidates, Args);
+      DiagnoseUseOfDeletedFunction(
+          UnresExpr->getMemberLoc(), MemExprE->getSourceRange(), DeclName,
+          CandidateSet, Best->Function, Args, /*IsMember=*/true);
       break;
     }
     // Overload resolution fails, try to recover.
@@ -16483,3 +16479,17 @@ bool clang::shouldEnforceArgLimit(bool PartialOverloading,
         return false;
   return true;
 }
+
+void Sema::DiagnoseUseOfDeletedFunction(SourceLocation Loc, SourceRange Range,
+                                        DeclarationName Name,
+                                        OverloadCandidateSet &CandidateSet,
+                                        FunctionDecl *Fn, MultiExprArg Args,
+                                        bool IsMember) {
+  StringLiteral *Msg = Fn->getDeletedMessage();
+  CandidateSet.NoteCandidates(
+      PartialDiagnosticAt(Loc, PDiag(diag::err_ovl_deleted_call)
+                                   << IsMember << Name << !!Msg
+                                   << (Msg ? Msg->getString() : StringRef())
+                                   << Range),
+      *this, OCD_AllCandidates, Args);
+}
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 549ffd4b0b6df5..dfc6edc003e641 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2202,9 +2202,6 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
   if (D->isLocalExternDecl())
     Function->setLocalExternDecl();
 
-  if (D->isDeletedAsWritten())
-    Function->setDeletedWithMessage(D->getDeletedMessage());
-
   DeclContext *LexicalDC = Owner;
   if (!isFriend && D->isOutOfLine() && !D->isLocalExternDecl()) {
     assert(D->getDeclContext()->isFileContext());
@@ -2432,7 +2429,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
       return nullptr;
   }
   if (D->isDeleted())
-    SemaRef.SetDeclDeleted(Function, D->getLocation());
+    SemaRef.SetDeclDeleted(Function, D->getLocation(), D->getDeletedMessage());
 
   NamedDecl *PrincipalDecl =
       (TemplateParams ? cast<NamedDecl>(FunctionTemplate) : Function);
@@ -2630,9 +2627,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
   if (QualifierLoc)
     Method->setQualifierInfo(QualifierLoc);
 
-  if (D->isDeletedAsWritten())
-    Method->setDeletedWithMessage(D->getDeletedMessage());
-
   if (TemplateParams) {
     // Our resulting instantiation is actually a function template, since we
     // are substituting only the outer template parameters. For example, given
@@ -2811,7 +2805,8 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
       return nullptr;
   }
   if (D->isDeletedAsWritten())
-    SemaRef.SetDeclDeleted(Method, Method->getLocation());
+    SemaRef.SetDeclDeleted(Method, Method->getLocation(),
+                           D->getDeletedMessage());
 
   // If this is an explicit specialization, mark the implicitly-instantiated
   // template specialization as being an explicit specialization too.
diff --git a/clang/test/SemaCXX/cxx2c-delete-with-message.cpp b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
new file mode 100644
index 00000000000000..d0f62859c547e6
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s
+
+struct S {
+  void f() = delete("deleted (1)"); // expected-note {{explicitly marked deleted}}
+
+  template <typename T>
+  T g() = delete("deleted (2)"); // expected-note {{explicitly deleted}}
+};
+
+template <typename T>
+struct TS {
+  T f() = delete("deleted (3)"); // expected-note {{explicitly marked deleted}}
+
+  template <typename U>
+  T g(U) = delete("deleted (4)"); // expected-note {{explicitly deleted}}
+};
+
+void f() = delete("deleted (5)"); // expected-note {{explicitly deleted}}
+
+template <typename T>
+T g() = delete("deleted (6)"); // expected-note {{explicitly deleted}}
+
+void h() {
+  S{}.f(); // expected-error {{attempt to use a deleted function: deleted (1)}}
+  S{}.g<int>(); // expected-error {{call to deleted member function 'g': deleted (2)}}
+  TS<int>{}.f(); // expected-error {{attempt to use a deleted function: deleted (3)}}
+  TS<int>{}.g<int>(0); // expected-error {{call to deleted member function 'g': deleted (4)}}
+  f(); // expected-error {{call to deleted function 'f': deleted (5)}}
+  g<int>(); // expected-error {{call to deleted function 'g': deleted (6)}}
+}

>From a018c8814b024ca08f5487be5be9132aa5c99dfe Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 25 Mar 2024 17:00:14 +0100
Subject: [PATCH 04/22] [Clang] Add ast printer tests

---
 .../ast-print-cxx2c-delete-with-message.cpp    | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
 create mode 100644 clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp

diff --git a/clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp b/clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp
new file mode 100644
index 00000000000000..edc9544d718d19
--- /dev/null
+++ b/clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp
@@ -0,0 +1,18 @@
+// Without serialization:
+// RUN: %clang_cc1 -ast-print %s | FileCheck %s
+//
+// With serialization:
+// RUN: %clang_cc1 -emit-pch -o %t %s
+// RUN: %clang_cc1 -x c++ -include-pch %t -ast-print-all  /dev/null | FileCheck %s
+
+// CHECK: struct S {
+struct S {
+  // CHECK-NEXT: void a() = delete("foo");
+  void a() = delete("foo");
+
+  // CHECK-NEXT: template <typename T> T b() = delete("bar");
+  template <typename T> T b() = delete("bar");
+};
+
+// CHECK: void c() = delete("baz");
+void c() = delete("baz");

>From b5a50beebc1f54fd106ebeda086b73c6875591a5 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 25 Mar 2024 17:08:04 +0100
Subject: [PATCH 05/22] [NFC] clang-format

---
 clang/include/clang/AST/Decl.h            | 2 +-
 clang/lib/Parse/ParseCXXInlineMethods.cpp | 7 ++++---
 clang/lib/Sema/SemaDeclCXX.cpp            | 3 ++-
 clang/lib/Sema/SemaExpr.cpp               | 2 +-
 clang/lib/Sema/SemaExprCXX.cpp            | 2 +-
 5 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 24f0394577e228..9c445eccdf8992 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -2489,7 +2489,7 @@ class FunctionDecl : public DeclaratorDecl,
   }
 
   void setDeletedAsWritten(bool D = true) { FunctionDeclBits.IsDeleted = D; }
-  void setDeletedWithMessage(StringLiteral* Message) {
+  void setDeletedWithMessage(StringLiteral *Message) {
     FunctionDeclBits.IsDeleted = true;
     DeletedMessage = Message;
   }
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index c551023a1e9dbc..9e2f4ce562a4ea 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -22,7 +22,8 @@ using namespace clang;
 
 /// Parse the optional ("message") part of a deleted-function-body.
 StringLiteral *Parser::ParseCXXDeletedFunctionMessage() {
-  if (!Tok.is(tok::l_paren)) return nullptr;
+  if (!Tok.is(tok::l_paren))
+    return nullptr;
   StringLiteral *Message = nullptr;
   BalancedDelimiterTracker BT{*this, tok::l_paren};
   BT.consumeOpen();
@@ -33,7 +34,7 @@ StringLiteral *Parser::ParseCXXDeletedFunctionMessage() {
       Message = Res.getAs<StringLiteral>();
   } else {
     Diag(Tok.getLocation(), diag::err_expected_string_literal)
-      << /*Source='in'*/ 0 << "'delete'";
+        << /*Source='in'*/ 0 << "'delete'";
     SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch);
   }
 
@@ -91,7 +92,7 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(
                       ? diag::warn_cxx98_compat_defaulted_deleted_function
                       : diag::ext_defaulted_deleted_function)
         << 1 /* deleted */;
-      StringLiteral* Message = ParseCXXDeletedFunctionMessage();
+      StringLiteral *Message = ParseCXXDeletedFunctionMessage();
       Actions.SetDeclDeleted(FnD, KWLoc, Message);
       Delete = true;
       if (auto *DeclAsFunction = dyn_cast<FunctionDecl>(FnD)) {
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index e6d653aedd061c..50eff9b02cbef8 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18108,7 +18108,8 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
   return ND;
 }
 
-void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc, StringLiteral *Message) {
+void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc,
+                          StringLiteral *Message) {
   AdjustDeclIfTemplate(Dcl);
 
   FunctionDecl *Fn = dyn_cast_or_null<FunctionDecl>(Dcl);
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 468b5ed1010457..76fbce8a95da85 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -274,7 +274,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
       else {
         StringLiteral *Msg = FD->getDeletedMessage();
         Diag(Loc, diag::err_deleted_function_use)
-          << !!Msg << (Msg ? Msg->getString() : StringRef());
+            << !!Msg << (Msg ? Msg->getString() : StringRef());
       }
       NoteDeletedFunction(FD);
       return true;
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index ff1fb146544d40..e5b8d1fbb81554 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -3321,7 +3321,7 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
       if (Diagnose) {
         StringLiteral *Msg = Operator->getDeletedMessage();
         Diag(StartLoc, diag::err_deleted_function_use)
-          << !!Msg << (Msg ? Msg->getString() : StringRef());
+            << !!Msg << (Msg ? Msg->getString() : StringRef());
         NoteDeletedFunction(Operator);
       }
       return true;

>From e48b80d477e10d4cf3b267a968eaa3cdfaf8c8d3 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 25 Mar 2024 17:28:26 +0100
Subject: [PATCH 06/22] [Clang] Add release note

---
 clang/docs/ReleaseNotes.rst | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 459f6a25aeef7b..d838e9b5f287f6 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -109,6 +109,8 @@ C++2c Feature Support
 
 - Implemented `P2662R3 Pack Indexing <https://wg21.link/P2662R3>`_.
 
+- Implemented `P2573R2: = delete("should have a reason"); <https://wg21.link/P2573R2>`_
+
 
 Resolutions to C++ Defect Reports
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

>From 9e45341bf798fc54c0d95bdd7ac585e11e2d11b1 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 25 Mar 2024 18:34:17 +0100
Subject: [PATCH 07/22] [Clang] Move test to the right directory and fix typo

---
 .../{SemaCXX => AST}/ast-print-cxx2c-delete-with-message.cpp    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
 rename clang/test/{SemaCXX => AST}/ast-print-cxx2c-delete-with-message.cpp (83%)

diff --git a/clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp b/clang/test/AST/ast-print-cxx2c-delete-with-message.cpp
similarity index 83%
rename from clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp
rename to clang/test/AST/ast-print-cxx2c-delete-with-message.cpp
index edc9544d718d19..11e037e4d7443e 100644
--- a/clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp
+++ b/clang/test/AST/ast-print-cxx2c-delete-with-message.cpp
@@ -3,7 +3,7 @@
 //
 // With serialization:
 // RUN: %clang_cc1 -emit-pch -o %t %s
-// RUN: %clang_cc1 -x c++ -include-pch %t -ast-print-all  /dev/null | FileCheck %s
+// RUN: %clang_cc1 -x c++ -include-pch %t -ast-print  /dev/null | FileCheck %s
 
 // CHECK: struct S {
 struct S {

>From 9fc02a5e6dde90375b8355e28856ee598b3ca1fe Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 26 Mar 2024 14:31:59 +0100
Subject: [PATCH 08/22] [NFC] Spell out type

Co-authored-by: Mariya Podchishchaeva <mariya.podchishchaeva at intel.com>
---
 clang/lib/AST/DeclPrinter.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index d04cdd5a10e033..ae79398029b3c6 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -875,7 +875,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
     Out << " = 0";
   else if (D->isDeletedAsWritten()) {
     Out << " = delete";
-    if (const auto *M = D->getDeletedMessage()) {
+    if (const StringLiteral *M = D->getDeletedMessage()) {
       Out << "(";
       M->outputString(Out);
       Out << ")";

>From 7664e7d31dda3ce7d6b380a3663733dcc3148621 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 26 Mar 2024 14:32:09 +0100
Subject: [PATCH 09/22] [NFC] Spell out type

Co-authored-by: Mariya Podchishchaeva <mariya.podchishchaeva at intel.com>
---
 clang/lib/AST/TextNodeDumper.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 4a2369330a35e7..22c157eb11684d 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1936,7 +1936,7 @@ void TextNodeDumper::VisitFunctionDecl(const FunctionDecl *D) {
   if (D->isTrivial())
     OS << " trivial";
 
-  if (const auto *M = D->getDeletedMessage())
+  if (const StringLiteral *M = D->getDeletedMessage())
     AddChild("delete message", [=] { Visit(M); });
 
   if (D->isIneligibleOrNotSelected())

>From 41bb3ddcc3ee86241e6e54d1240a008788be8c0e Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 28 Mar 2024 22:05:23 +0100
Subject: [PATCH 10/22] [Clang] Store message in DefaultFunctionInfo

---
 clang/include/clang/AST/Decl.h                | 57 +++++++++-------
 clang/include/clang/AST/DeclBase.h            |  2 +-
 clang/lib/AST/ASTImporter.cpp                 | 12 ++++
 clang/lib/AST/Decl.cpp                        | 66 +++++++++++++------
 clang/lib/Sema/SemaDecl.cpp                   |  5 +-
 clang/lib/Sema/SemaDeclCXX.cpp                | 10 +--
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 10 +--
 clang/lib/Serialization/ASTReaderDecl.cpp     | 22 ++++---
 clang/lib/Serialization/ASTWriterDecl.cpp     | 18 ++---
 clang/test/PCH/cxx2a-defaulted-comparison.cpp |  2 +-
 10 files changed, 132 insertions(+), 72 deletions(-)

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 9c445eccdf8992..2fbab6b83a8c51 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1981,21 +1981,35 @@ class FunctionDecl : public DeclaratorDecl,
 
   };
 
-  /// Stashed information about a defaulted function definition whose body has
-  /// not yet been lazily generated.
-  class DefaultedFunctionInfo final
-      : llvm::TrailingObjects<DefaultedFunctionInfo, DeclAccessPair> {
+  /// Stashed information about a defaulted/deleted function body.
+  class ExtraFunctionInfo final
+      : llvm::TrailingObjects<ExtraFunctionInfo, DeclAccessPair,
+                              StringLiteral *> {
     friend TrailingObjects;
     unsigned NumLookups;
+    bool HasDeletedMessage;
+
+    size_t numTrailingObjects(OverloadToken<DeclAccessPair>) const {
+      return NumLookups;
+    }
 
   public:
-    static DefaultedFunctionInfo *Create(ASTContext &Context,
-                                         ArrayRef<DeclAccessPair> Lookups);
+    static ExtraFunctionInfo *Create(ASTContext &Context,
+                                     ArrayRef<DeclAccessPair> Lookups,
+                                     StringLiteral *DeletedMessage = nullptr);
+
     /// Get the unqualified lookup results that should be used in this
     /// defaulted function definition.
     ArrayRef<DeclAccessPair> getUnqualifiedLookups() const {
       return {getTrailingObjects<DeclAccessPair>(), NumLookups};
     }
+
+    StringLiteral *getDeletedMessage() const {
+      return HasDeletedMessage ? *getTrailingObjects<StringLiteral *>()
+                               : nullptr;
+    }
+
+    void setDeletedMessage(StringLiteral *Message);
   };
 
 private:
@@ -2005,20 +2019,14 @@ class FunctionDecl : public DeclaratorDecl,
   ParmVarDecl **ParamInfo = nullptr;
 
   /// The active member of this union is determined by
-  /// FunctionDeclBits.HasDefaultedFunctionInfo.
+  /// FunctionDeclBits.HasExtraFunctionInfo.
   union {
     /// The body of the function.
     LazyDeclStmtPtr Body;
     /// Information about a future defaulted function definition.
-    DefaultedFunctionInfo *DefaultedInfo;
+    ExtraFunctionInfo *ExtraInfo;
   };
 
-  /// Message that indicates why this function was deleted.
-  ///
-  /// FIXME: Figure out where to actually put this; maybe in the
-  /// 'DefaultedInfo' above?
-  StringLiteral *DeletedMessage;
-
   unsigned ODRHash;
 
   /// End part of this FunctionDecl's source range.
@@ -2274,18 +2282,18 @@ class FunctionDecl : public DeclaratorDecl,
 
   /// Returns whether this specific declaration of the function has a body.
   bool doesThisDeclarationHaveABody() const {
-    return (!FunctionDeclBits.HasDefaultedFunctionInfo && Body) ||
+    return (!FunctionDeclBits.HasExtraFunctionInfo && Body) ||
            isLateTemplateParsed();
   }
 
   void setBody(Stmt *B);
   void setLazyBody(uint64_t Offset) {
-    FunctionDeclBits.HasDefaultedFunctionInfo = false;
+    FunctionDeclBits.HasExtraFunctionInfo = false;
     Body = LazyDeclStmtPtr(Offset);
   }
 
-  void setDefaultedFunctionInfo(DefaultedFunctionInfo *Info);
-  DefaultedFunctionInfo *getDefaultedFunctionInfo() const;
+  void setExtraFunctionInfo(ExtraFunctionInfo *Info);
+  ExtraFunctionInfo *getExtraFunctionInfo() const;
 
   /// Whether this function is variadic.
   bool isVariadic() const;
@@ -2489,10 +2497,9 @@ class FunctionDecl : public DeclaratorDecl,
   }
 
   void setDeletedAsWritten(bool D = true) { FunctionDeclBits.IsDeleted = D; }
-  void setDeletedWithMessage(StringLiteral *Message) {
-    FunctionDeclBits.IsDeleted = true;
-    DeletedMessage = Message;
-  }
+
+  /// Only valid if isDeletedAsWritten() returns true.
+  void setDeletedMessage(StringLiteral *Message);
 
   /// Determines whether this function is "main", which is the
   /// entry point into an executable program.
@@ -2649,7 +2656,11 @@ class FunctionDecl : public DeclaratorDecl,
   }
 
   /// Get the message that indicates why this function was deleted.
-  StringLiteral *getDeletedMessage() const { return DeletedMessage; }
+  StringLiteral *getDeletedMessage() const {
+    return FunctionDeclBits.HasExtraFunctionInfo
+               ? ExtraInfo->getDeletedMessage()
+               : nullptr;
+  }
 
   void setPreviousDeclaration(FunctionDecl * PrevDecl);
 
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 47ed6d0d1db0df..db007a624c7337 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -1740,7 +1740,7 @@ class DeclContext {
     LLVM_PREFERRED_TYPE(bool)
     uint64_t IsExplicitlyDefaulted : 1;
     LLVM_PREFERRED_TYPE(bool)
-    uint64_t HasDefaultedFunctionInfo : 1;
+    uint64_t HasExtraFunctionInfo : 1;
 
     /// For member functions of complete types, whether this is an ineligible
     /// special member function or an unselected destructor. See
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 023aaa7f0572b4..2f8eef57cb958a 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -3916,6 +3916,14 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
     // decl and its redeclarations may be required.
   }
 
+  StringLiteral *Msg = D->getDeletedMessage();
+  if (Msg) {
+    auto Imported = import(Msg);
+    if (!Imported)
+      return Imported.takeError();
+    Msg = *Imported;
+  }
+
   ToFunction->setQualifierInfo(ToQualifierLoc);
   ToFunction->setAccess(D->getAccess());
   ToFunction->setLexicalDeclContext(LexicalDC);
@@ -3930,6 +3938,10 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
   ToFunction->setRangeEnd(ToEndLoc);
   ToFunction->setDefaultLoc(ToDefaultLoc);
 
+  if (Msg)
+    ToFunction->setExtraFunctionInfo(FunctionDecl::ExtraFunctionInfo::Create(
+        Importer.getToContext(), {}, Msg));
+
   // Set the parameters.
   for (auto *Param : Parameters) {
     Param->setOwningFunction(ToFunction);
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index b5e61f0eceb6e4..ef3d7de62092d5 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3043,8 +3043,8 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
                            Expr *TrailingRequiresClause)
     : DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo,
                      StartLoc),
-      DeclContext(DK), redeclarable_base(C), Body(), DeletedMessage(nullptr),
-      ODRHash(0), EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) {
+      DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0),
+      EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) {
   assert(T.isNull() || T->isFunctionType());
   FunctionDeclBits.SClass = S;
   FunctionDeclBits.IsInline = isInlineSpecified;
@@ -3058,7 +3058,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
   FunctionDeclBits.IsTrivialForCall = false;
   FunctionDeclBits.IsDefaulted = false;
   FunctionDeclBits.IsExplicitlyDefaulted = false;
-  FunctionDeclBits.HasDefaultedFunctionInfo = false;
+  FunctionDeclBits.HasExtraFunctionInfo = false;
   FunctionDeclBits.IsIneligibleOrNotSelected = false;
   FunctionDeclBits.HasImplicitReturnZero = false;
   FunctionDeclBits.IsLateTemplateParsed = false;
@@ -3092,30 +3092,58 @@ bool FunctionDecl::isVariadic() const {
   return false;
 }
 
-FunctionDecl::DefaultedFunctionInfo *
-FunctionDecl::DefaultedFunctionInfo::Create(ASTContext &Context,
-                                            ArrayRef<DeclAccessPair> Lookups) {
-  DefaultedFunctionInfo *Info = new (Context.Allocate(
-      totalSizeToAlloc<DeclAccessPair>(Lookups.size()),
-      std::max(alignof(DefaultedFunctionInfo), alignof(DeclAccessPair))))
-      DefaultedFunctionInfo;
+FunctionDecl::ExtraFunctionInfo *
+FunctionDecl::ExtraFunctionInfo::Create(ASTContext &Context,
+                                        ArrayRef<DeclAccessPair> Lookups,
+                                        StringLiteral *DeletedMessage) {
+  static constexpr size_t Alignment =
+      std::max({alignof(ExtraFunctionInfo), alignof(DeclAccessPair),
+                alignof(StringLiteral *)});
+  size_t Size = totalSizeToAlloc<DeclAccessPair, StringLiteral *>(
+      Lookups.size(), !!DeletedMessage);
+
+  ExtraFunctionInfo *Info =
+      new (Context.Allocate(Size, Alignment)) ExtraFunctionInfo;
   Info->NumLookups = Lookups.size();
+  Info->HasDeletedMessage = !!DeletedMessage;
+
   std::uninitialized_copy(Lookups.begin(), Lookups.end(),
                           Info->getTrailingObjects<DeclAccessPair>());
+  if (DeletedMessage)
+    *Info->getTrailingObjects<StringLiteral *>() = DeletedMessage;
   return Info;
 }
 
-void FunctionDecl::setDefaultedFunctionInfo(DefaultedFunctionInfo *Info) {
-  assert(!FunctionDeclBits.HasDefaultedFunctionInfo && "already have this");
+void FunctionDecl::setExtraFunctionInfo(ExtraFunctionInfo *Info) {
+  assert(!FunctionDeclBits.HasExtraFunctionInfo && "already have this");
   assert(!Body && "can't replace function body with defaulted function info");
 
-  FunctionDeclBits.HasDefaultedFunctionInfo = true;
-  DefaultedInfo = Info;
+  FunctionDeclBits.HasExtraFunctionInfo = true;
+  ExtraInfo = Info;
+}
+
+void FunctionDecl::ExtraFunctionInfo::setDeletedMessage(
+    StringLiteral *Message) {
+  // We should never get here with the ExtraInfo populated, but no space
+  // allocated for the deleted message, since that would require recreating
+  // this, but setExtraFunctionInfo() disallows overwriting an already existing
+  // ExtraFunctionInfo.
+  assert(HasDeletedMessage && "Explicitly deleting defaulted function?");
+  *getTrailingObjects<StringLiteral *>() = Message;
 }
 
-FunctionDecl::DefaultedFunctionInfo *
-FunctionDecl::getDefaultedFunctionInfo() const {
-  return FunctionDeclBits.HasDefaultedFunctionInfo ? DefaultedInfo : nullptr;
+FunctionDecl::ExtraFunctionInfo *FunctionDecl::getExtraFunctionInfo() const {
+  return FunctionDeclBits.HasExtraFunctionInfo ? ExtraInfo : nullptr;
+}
+
+void FunctionDecl::setDeletedMessage(StringLiteral *Message) {
+  assert(Message && "Should not be called with nullptr");
+  assert(isDeletedAsWritten() && "Function must be deleted");
+  if (FunctionDeclBits.HasExtraFunctionInfo)
+    ExtraInfo->setDeletedMessage(Message);
+  else
+    setExtraFunctionInfo(
+        ExtraFunctionInfo::Create(getASTContext(), {}, Message));
 }
 
 bool FunctionDecl::hasBody(const FunctionDecl *&Definition) const {
@@ -3202,7 +3230,7 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const {
   if (!hasBody(Definition))
     return nullptr;
 
-  assert(!Definition->FunctionDeclBits.HasDefaultedFunctionInfo &&
+  assert(!Definition->FunctionDeclBits.HasExtraFunctionInfo &&
          "definition should not have a body");
   if (Definition->Body)
     return Definition->Body.get(getASTContext().getExternalSource());
@@ -3211,7 +3239,7 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const {
 }
 
 void FunctionDecl::setBody(Stmt *B) {
-  FunctionDeclBits.HasDefaultedFunctionInfo = false;
+  FunctionDeclBits.HasExtraFunctionInfo = false;
   Body = LazyDeclStmtPtr(B);
   if (B)
     EndRangeLoc = B->getEndLoc();
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7acce77458a372..a67b282d026668 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16092,7 +16092,10 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
     // This is meant to pop the context added in ActOnStartOfFunctionDef().
     ExitFunctionBodyRAII ExitRAII(*this, isLambdaCallOperator(FD));
     if (FD) {
-      FD->setBody(Body);
+      // Do not overwrite the potentially present ExtraInfo of a deleted
+      // function declaration.
+      if (!FD->isDeletedAsWritten())
+        FD->setBody(Body);
       FD->setWillHaveBody(false);
       CheckImmediateEscalatingFunctionDefinition(FD, FSI);
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 50eff9b02cbef8..bf8de0ea4fd7ab 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7962,7 +7962,7 @@ class DefaultedComparisonVisitor {
   DefaultedComparisonVisitor(Sema &S, CXXRecordDecl *RD, FunctionDecl *FD,
                              DefaultedComparisonKind DCK)
       : S(S), RD(RD), FD(FD), DCK(DCK) {
-    if (auto *Info = FD->getDefaultedFunctionInfo()) {
+    if (auto *Info = FD->getExtraFunctionInfo()) {
       // FIXME: Change CreateOverloadedBinOp to take an ArrayRef instead of an
       // UnresolvedSet to avoid this copy.
       Fns.assign(Info->getUnqualifiedLookups().begin(),
@@ -8830,8 +8830,8 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
     UnresolvedSet<32> Operators;
     lookupOperatorsForDefaultedComparison(*this, S, Operators,
                                           FD->getOverloadedOperator());
-    FD->setDefaultedFunctionInfo(FunctionDecl::DefaultedFunctionInfo::Create(
-        Context, Operators.pairs()));
+    FD->setExtraFunctionInfo(
+        FunctionDecl::ExtraFunctionInfo::Create(Context, Operators.pairs()));
   }
 
   // C++2a [class.compare.default]p1:
@@ -18158,7 +18158,9 @@ void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc,
   // C++11 [dcl.fct.def.delete]p4:
   //  A deleted function is implicitly inline.
   Fn->setImplicitlyInline();
-  Fn->setDeletedWithMessage(Message);
+  Fn->setDeletedAsWritten();
+  if (Message)
+    Fn->setDeletedMessage(Message);
 }
 
 void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index dfc6edc003e641..f6a5353aec3ecd 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4856,7 +4856,7 @@ TemplateDeclInstantiator::InitMethodInstantiation(CXXMethodDecl *New,
 bool TemplateDeclInstantiator::SubstDefaultedFunction(FunctionDecl *New,
                                                       FunctionDecl *Tmpl) {
   // Transfer across any unqualified lookups.
-  if (auto *DFI = Tmpl->getDefaultedFunctionInfo()) {
+  if (auto *DFI = Tmpl->getExtraFunctionInfo()) {
     SmallVector<DeclAccessPair, 32> Lookups;
     Lookups.reserve(DFI->getUnqualifiedLookups().size());
     bool AnyChanged = false;
@@ -4871,10 +4871,10 @@ bool TemplateDeclInstantiator::SubstDefaultedFunction(FunctionDecl *New,
 
     // It's unlikely that substitution will change any declarations. Don't
     // store an unnecessary copy in that case.
-    New->setDefaultedFunctionInfo(
-        AnyChanged ? FunctionDecl::DefaultedFunctionInfo::Create(
-                         SemaRef.Context, Lookups)
-                   : DFI);
+    New->setExtraFunctionInfo(
+        AnyChanged
+            ? FunctionDecl::ExtraFunctionInfo::Create(SemaRef.Context, Lookups)
+            : DFI);
   }
 
   SemaRef.SetDeclDefaulted(New, Tmpl->getLocation());
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 37bc61b9a63a34..aad6747eca6be2 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1109,24 +1109,28 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
     FD->setHasODRHash(true);
   }
 
-  if (FD->isDefaulted()) {
-    if (unsigned NumLookups = Record.readInt()) {
+  if (FD->isDefaulted() || FD->isDeletedAsWritten()) {
+    // If 'Info' is nonzero, we need to read an ExtraFunctionInfo; if,
+    // additionally, the second bit is also set, we also need to read
+    // a DeletedMessage for the ExtraFunctionInfo.
+    if (auto Info = Record.readInt()) {
+      bool HasMessage = Info & 2;
+      StringLiteral *DeletedMessage =
+          HasMessage ? cast<StringLiteral>(Record.readExpr()) : nullptr;
+
+      unsigned NumLookups = Record.readInt();
       SmallVector<DeclAccessPair, 8> Lookups;
       for (unsigned I = 0; I != NumLookups; ++I) {
         NamedDecl *ND = Record.readDeclAs<NamedDecl>();
         AccessSpecifier AS = (AccessSpecifier)Record.readInt();
         Lookups.push_back(DeclAccessPair::make(ND, AS));
       }
-      FD->setDefaultedFunctionInfo(FunctionDecl::DefaultedFunctionInfo::Create(
-          Reader.getContext(), Lookups));
+
+      FD->setExtraFunctionInfo(FunctionDecl::ExtraFunctionInfo::Create(
+          Reader.getContext(), Lookups, DeletedMessage));
     }
   }
 
-  // FIXME: See ASTWriterDecl::VisitFunctionDecl.
-  if (FD->isDeletedAsWritten())
-    FD->setDeletedWithMessage(
-        cast_if_present<StringLiteral>(Record.readStmt()));
-
   if (Existing)
     mergeRedeclarable(FD, Existing, Redecl);
   else if (auto Kind = FD->getTemplatedKind();
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index af7dbe916419eb..2dd2f1f14a8b6c 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -749,8 +749,15 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
   if (!ShouldSkipCheckingODR)
     Record.push_back(D->getODRHash());
 
-  if (D->isDefaulted()) {
-    if (auto *FDI = D->getDefaultedFunctionInfo()) {
+  if (D->isDefaulted() || D->isDeletedAsWritten()) {
+    if (auto *FDI = D->getExtraFunctionInfo()) {
+      // Store both that there is an ExtraFunctionInfo and whether it contains
+      // a DeletedMessage.
+      StringLiteral *DeletedMessage = FDI->getDeletedMessage();
+      Record.push_back(1 | (DeletedMessage ? 2 : 0));
+      if (DeletedMessage)
+        Record.AddStmt(DeletedMessage);
+
       Record.push_back(FDI->getUnqualifiedLookups().size());
       for (DeclAccessPair P : FDI->getUnqualifiedLookups()) {
         Record.AddDeclRef(P.getDecl());
@@ -761,13 +768,6 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
     }
   }
 
-  // FIXME: Hack: We're out of bits in FunctionDeclBits, so always
-  // add this even though it's 0 in the vast majority of cases. We
-  // might really want to consider storing this in the DefaultedFunctionInfo
-  // instead.
-  if (D->isDeletedAsWritten())
-    Record.AddStmt(D->getDeletedMessage());
-
   Record.push_back(D->param_size());
   for (auto *P : D->parameters())
     Record.AddDeclRef(P);
diff --git a/clang/test/PCH/cxx2a-defaulted-comparison.cpp b/clang/test/PCH/cxx2a-defaulted-comparison.cpp
index 8aeb1683961af3..cb1e8ef0d7c900 100644
--- a/clang/test/PCH/cxx2a-defaulted-comparison.cpp
+++ b/clang/test/PCH/cxx2a-defaulted-comparison.cpp
@@ -22,7 +22,7 @@ namespace std {
   constexpr strong_ordering strong_ordering::less = {-1};
 }
 
-// Ensure that we can round-trip DefaultedFunctionInfo through an AST file.
+// Ensure that we can round-trip ExtraFunctionInfo through an AST file.
 namespace LookupContext {
   struct A {};
 

>From b1c930c2f2d87e73ddd1ebc378d5c69c9b50e789 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 28 Mar 2024 23:35:59 +0100
Subject: [PATCH 11/22] [Clang] Add some unicode tests

---
 .../SemaCXX/cxx2c-delete-with-message.cpp     | 40 +++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/clang/test/SemaCXX/cxx2c-delete-with-message.cpp b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
index d0f62859c547e6..7183a9513c31ff 100644
--- a/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
+++ b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
@@ -20,6 +20,39 @@ void f() = delete("deleted (5)"); // expected-note {{explicitly deleted}}
 template <typename T>
 T g() = delete("deleted (6)"); // expected-note {{explicitly deleted}}
 
+void u1() = delete(L"\xFFFFFFFF"); // expected-error {{an unevaluated string literal cannot have an encoding prefix}} \
+                                   // expected-error {{invalid escape sequence '\xFFFFFFFF' in an unevaluated string literal}}
+void u2() = delete(u"\U000317FF"); // expected-error {{an unevaluated string literal cannot have an encoding prefix}}
+
+void u3() = delete("Ω"); // expected-note {{explicitly deleted}}
+void u4() = delete("\u1234"); // expected-note {{explicitly deleted}}
+
+void u5() = delete("\x1ff"       // expected-error {{hex escape sequence out of range}} \
+                                 // expected-error {{invalid escape sequence '\x1ff' in an unevaluated string literal}}
+                     "0\x123"    // expected-error {{invalid escape sequence '\x123' in an unevaluated string literal}}
+                     "fx\xfffff" // expected-error {{invalid escape sequence '\xfffff' in an unevaluated string literal}}
+                     "goop");
+
+void u6() = delete("\'\"\?\\\a\b\f\n\r\t\v"); // expected-note {{explicitly deleted}}
+void u7() = delete("\xFF"); // expected-error {{invalid escape sequence '\xFF' in an unevaluated string literal}}
+void u8() = delete("\123"); // expected-error {{invalid escape sequence '\123' in an unevaluated string literal}}
+void u9() = delete("\pOh no, a Pascal string!"); // expected-warning {{unknown escape sequence '\p'}} \
+                                                 // expected-error {{invalid escape sequence '\p' in an unevaluated string literal}}
+// expected-note at +1 {{explicitly deleted}}
+void u10() = delete(R"(a
+\tb
+c
+)");
+
+void u11() = delete("\u0080\u0081\u0082\u0083\u0099\u009A\u009B\u009C\u009D\u009E\u009F"); // expected-note {{explicitly deleted}}
+
+
+//! Contains RTL/LTR marks
+void u12() = delete("\u200Eabc\u200Fdef\u200Fgh"); // expected-note {{explicitly deleted}}
+
+//! Contains ZWJ/regional indicators
+void u13() = delete("🏳️‍🌈 🏴󠁧󠁢󠁥󠁮󠁧󠁿 🇪🇺"); // expected-note {{explicitly deleted}}
+
 void h() {
   S{}.f(); // expected-error {{attempt to use a deleted function: deleted (1)}}
   S{}.g<int>(); // expected-error {{call to deleted member function 'g': deleted (2)}}
@@ -27,4 +60,11 @@ void h() {
   TS<int>{}.g<int>(0); // expected-error {{call to deleted member function 'g': deleted (4)}}
   f(); // expected-error {{call to deleted function 'f': deleted (5)}}
   g<int>(); // expected-error {{call to deleted function 'g': deleted (6)}}
+  u3(); // expected-error {{call to deleted function 'u3': Ω}}
+  u4(); // expected-error {{call to deleted function 'u4': ሴ}}
+  u6(); // expected-error {{call to deleted function 'u6': '"?\<U+0007><U+0008>}}
+  u10(); // expected-error {{call to deleted function 'u10': a\n\tb\nc\n}}
+  u11(); // expected-error {{call to deleted function 'u11': <U+0080><U+0081><U+0082><U+0083><U+0099><U+009A><U+009B><U+009C><U+009D><U+009E><U+009F>}}
+  u12(); // expected-error {{call to deleted function 'u12': ‎abc‏def‏gh}}
+  u13(); // expected-error {{call to deleted function 'u13': 🏳️‍🌈 🏴󠁧󠁢󠁥󠁮󠁧󠁿 🇪🇺}}
 }

>From 29cdff9e5b19643210ea9ec23a6036f3c59da092 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 28 Mar 2024 23:45:23 +0100
Subject: [PATCH 12/22] [NFC] Update delete syntax in comment

---
 clang/include/clang/Sema/Sema.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 9f6e2639508b2a..c2964a89097e91 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3207,7 +3207,11 @@ class Sema final {
     Other,
     ///   = default ;
     Default,
+    ///   deleted-function-body
+    ///
+    /// deleted-function-body:
     ///   = delete ;
+    ///   = delete ( unevaluated-string ) ;
     Delete
   };
 

>From 7807c717fa017b68a8dcd2cb77bbeac323e2ed12 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Thu, 28 Mar 2024 23:58:39 +0100
Subject: [PATCH 13/22] [Clang] Add feature test macro

---
 clang/lib/Frontend/InitPreprocessor.cpp | 3 +++
 clang/test/Lexer/cxx-features.cpp       | 4 ++++
 2 files changed, 7 insertions(+)

diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 48ad92063bd461..be60e9ce7270f1 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -750,6 +750,9 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
   Builder.defineMacro("__cpp_named_character_escapes", "202207L");
   Builder.defineMacro("__cpp_placeholder_variables", "202306L");
 
+  // C++26 features supported in earlier language modes.
+  Builder.defineMacro("__cpp_deleted_function", "202403L");
+
   if (LangOpts.Char8)
     Builder.defineMacro("__cpp_char8_t", "202207L");
   Builder.defineMacro("__cpp_impl_destroying_delete", "201806L");
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index 9496746c6fd663..a0e60c592401e9 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -34,6 +34,10 @@
 
 // --- C++26 features ---
 
+#if check(deleted_function, 202403, 202403, 202403, 202403, 202403, 202403, 202403)
+#error "wrong value for __cpp_deleted_function"
+#endif
+
 #if check(placeholder_variables, 202306, 202306, 202306, 202306, 202306, 202306, 202306)
 #error "wrong value for __cpp_placeholder_variables"
 #endif

>From 40a23307e6404fd0603a2cf72b0b6df5db5b9598 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 29 Mar 2024 00:05:17 +0100
Subject: [PATCH 14/22] [Clang] Update cxx_status

---
 clang/www/cxx_status.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index c1d95dadbb27e2..edd0b4fc6c2502 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -197,7 +197,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
  <tr>
   <td><tt>= delete("should have a reason");</tt></td>
   <td><a href="https://wg21.link/P2573R2">P2573R2</a></td>
-  <td class="none" align="center">No</td>
+  <td class="unreleased" align="center">Clang 19</td>
  </tr>
  <tr>
   <td>Variadic friends</td>

>From d6537a2fd0b7839085c070bbc55e3a6742bf2f9d Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 29 Mar 2024 00:36:58 +0100
Subject: [PATCH 15/22] [Clang] Add extension+compatibility diags

---
 .../clang/Basic/DiagnosticParseKinds.td       |  6 +++++
 clang/lib/Parse/ParseCXXInlineMethods.cpp     |  5 +++++
 clang/lib/Parse/Parser.cpp                    |  5 +++++
 .../test/Parser/cxx2c-delete-with-message.cpp | 22 ++++++++++---------
 4 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 46a44418a3153b..bb9ca2a50cc06c 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -941,6 +941,12 @@ def warn_cxx98_compat_defaulted_deleted_function : Warning<
   "%select{defaulted|deleted}0 function definitions are incompatible with C++98">,
   InGroup<CXX98Compat>, DefaultIgnore;
 
+def ext_delete_with_message : ExtWarn<
+  "'= delete' with a message is a C++2c extension">, InGroup<CXX26>;
+def warn_cxx23_delete_with_message : Warning<
+  "'= delete' with a message is incompatible with C++ standards before C++2c">,
+  DefaultIgnore, InGroup<CXXPre26Compat>;
+
 // C++11 default member initialization
 def ext_nonstatic_member_init : ExtWarn<
   "default member initializer for non-static data member is a C++11 "
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index 9e2f4ce562a4ea..5f6cb54feffd80 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -95,6 +95,11 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(
       StringLiteral *Message = ParseCXXDeletedFunctionMessage();
       Actions.SetDeclDeleted(FnD, KWLoc, Message);
       Delete = true;
+      if (Message)
+        Diag(Message->getBeginLoc(), getLangOpts().CPlusPlus26
+                                         ? diag::warn_cxx23_delete_with_message
+                                         : diag::ext_delete_with_message)
+            << Message->getSourceRange();
       if (auto *DeclAsFunction = dyn_cast<FunctionDecl>(FnD)) {
         DeclAsFunction->setRangeEnd(KWEndLoc);
       }
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index d6f2b9f448cd52..63f45d657ca28a 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1417,6 +1417,11 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
           << 1 /* deleted */;
       BodyKind = Sema::FnBodyKind::Delete;
       DeletedMessage = ParseCXXDeletedFunctionMessage();
+      if (DeletedMessage)
+        Diag(DeletedMessage->getBeginLoc(),
+             getLangOpts().CPlusPlus26 ? diag::warn_cxx23_delete_with_message
+                                       : diag::ext_delete_with_message)
+            << DeletedMessage->getSourceRange();
     } else if (TryConsumeToken(tok::kw_default, KWLoc)) {
       Diag(KWLoc, getLangOpts().CPlusPlus11
                       ? diag::warn_cxx98_compat_defaulted_deleted_function
diff --git a/clang/test/Parser/cxx2c-delete-with-message.cpp b/clang/test/Parser/cxx2c-delete-with-message.cpp
index 67a4c944187d7f..7baa1581eb369e 100644
--- a/clang/test/Parser/cxx2c-delete-with-message.cpp
+++ b/clang/test/Parser/cxx2c-delete-with-message.cpp
@@ -1,3 +1,5 @@
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=expected,pre26 -pedantic %s
+// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify=expected,compat -Wpre-c++26-compat %s
 // RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s
 
 struct S {
@@ -5,15 +7,15 @@ struct S {
   void b() = delete(; // expected-error {{expected string literal}} expected-error {{expected ')'}} expected-note {{to match this '('}}
   void c() = delete(); // expected-error {{expected string literal}}
   void d() = delete(42); // expected-error {{expected string literal}}
-  void e() = delete("foo"[0]); // expected-error {{expected ')'}} expected-note {{to match this '('}}
-  void f() = delete("foo");
+  void e() = delete("foo"[0]); // expected-error {{expected ')'}} expected-note {{to match this '('}} // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}}
+  void f() = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}}
 
-  S() = delete("foo");
-  ~S() = delete("foo");
-  S(const S&) = delete("foo");
-  S(S&&) = delete("foo");
-  S& operator=(const S&) = delete("foo");
-  S& operator=(S&&) = delete("foo");
+  S() = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}}
+  ~S() = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}}
+  S(const S&) = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}}
+  S(S&&) = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}}
+  S& operator=(const S&) = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}}
+  S& operator=(S&&) = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}}
 };
 
 struct T {
@@ -29,5 +31,5 @@ void a() = delete;
 void b() = delete(; // expected-error {{expected string literal}} expected-error {{expected ')'}} expected-note {{to match this '('}}
 void c() = delete(); // expected-error {{expected string literal}}
 void d() = delete(42); // expected-error {{expected string literal}}
-void e() = delete("foo"[0]); // expected-error {{expected ')'}} expected-note {{to match this '('}}
-void f() = delete("foo");
+void e() = delete("foo"[0]); // expected-error {{expected ')'}} expected-note {{to match this '('}} // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}}
+void f() = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}}

>From c1ff133cd7a61350bd2a3f24d6a5502cab7d3238 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 3 Apr 2024 16:01:08 +0200
Subject: [PATCH 16/22] [NFC] Rename ExtraFunctionInfo and update comments

---
 clang/include/clang/AST/Decl.h                | 26 ++++-----
 clang/include/clang/AST/DeclBase.h            |  2 +-
 clang/include/clang/Sema/Sema.h               |  2 +-
 clang/lib/AST/ASTImporter.cpp                 |  5 +-
 clang/lib/AST/Decl.cpp                        | 58 ++++++++++---------
 clang/lib/Sema/SemaDecl.cpp                   | 11 +++-
 clang/lib/Sema/SemaDeclCXX.cpp                |  7 ++-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 10 ++--
 clang/lib/Serialization/ASTReaderDecl.cpp     |  9 +--
 clang/lib/Serialization/ASTWriterDecl.cpp     |  6 +-
 clang/test/PCH/cxx2a-defaulted-comparison.cpp |  2 +-
 11 files changed, 76 insertions(+), 62 deletions(-)

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 2fbab6b83a8c51..97801565a7dc74 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1982,8 +1982,8 @@ class FunctionDecl : public DeclaratorDecl,
   };
 
   /// Stashed information about a defaulted/deleted function body.
-  class ExtraFunctionInfo final
-      : llvm::TrailingObjects<ExtraFunctionInfo, DeclAccessPair,
+  class DefaultedOrDeletedFunctionInfo final
+      : llvm::TrailingObjects<DefaultedOrDeletedFunctionInfo, DeclAccessPair,
                               StringLiteral *> {
     friend TrailingObjects;
     unsigned NumLookups;
@@ -1994,9 +1994,9 @@ class FunctionDecl : public DeclaratorDecl,
     }
 
   public:
-    static ExtraFunctionInfo *Create(ASTContext &Context,
-                                     ArrayRef<DeclAccessPair> Lookups,
-                                     StringLiteral *DeletedMessage = nullptr);
+    static DefaultedOrDeletedFunctionInfo *
+    Create(ASTContext &Context, ArrayRef<DeclAccessPair> Lookups,
+           StringLiteral *DeletedMessage = nullptr);
 
     /// Get the unqualified lookup results that should be used in this
     /// defaulted function definition.
@@ -2019,12 +2019,12 @@ class FunctionDecl : public DeclaratorDecl,
   ParmVarDecl **ParamInfo = nullptr;
 
   /// The active member of this union is determined by
-  /// FunctionDeclBits.HasExtraFunctionInfo.
+  /// FunctionDeclBits.HasDefaultedOrDeletedInfo.
   union {
     /// The body of the function.
     LazyDeclStmtPtr Body;
     /// Information about a future defaulted function definition.
-    ExtraFunctionInfo *ExtraInfo;
+    DefaultedOrDeletedFunctionInfo *DefaultedOrDeletedInfo;
   };
 
   unsigned ODRHash;
@@ -2282,18 +2282,18 @@ class FunctionDecl : public DeclaratorDecl,
 
   /// Returns whether this specific declaration of the function has a body.
   bool doesThisDeclarationHaveABody() const {
-    return (!FunctionDeclBits.HasExtraFunctionInfo && Body) ||
+    return (!FunctionDeclBits.HasDefaultedOrDeletedInfo && Body) ||
            isLateTemplateParsed();
   }
 
   void setBody(Stmt *B);
   void setLazyBody(uint64_t Offset) {
-    FunctionDeclBits.HasExtraFunctionInfo = false;
+    FunctionDeclBits.HasDefaultedOrDeletedInfo = false;
     Body = LazyDeclStmtPtr(Offset);
   }
 
-  void setExtraFunctionInfo(ExtraFunctionInfo *Info);
-  ExtraFunctionInfo *getExtraFunctionInfo() const;
+  void setDefaultedOrDeletedInfo(DefaultedOrDeletedFunctionInfo *Info);
+  DefaultedOrDeletedFunctionInfo *getDefalutedOrDeletedInfo() const;
 
   /// Whether this function is variadic.
   bool isVariadic() const;
@@ -2657,8 +2657,8 @@ class FunctionDecl : public DeclaratorDecl,
 
   /// Get the message that indicates why this function was deleted.
   StringLiteral *getDeletedMessage() const {
-    return FunctionDeclBits.HasExtraFunctionInfo
-               ? ExtraInfo->getDeletedMessage()
+    return FunctionDeclBits.HasDefaultedOrDeletedInfo
+               ? DefaultedOrDeletedInfo->getDeletedMessage()
                : nullptr;
   }
 
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index db007a624c7337..57ecd8ac54a449 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -1740,7 +1740,7 @@ class DeclContext {
     LLVM_PREFERRED_TYPE(bool)
     uint64_t IsExplicitlyDefaulted : 1;
     LLVM_PREFERRED_TYPE(bool)
-    uint64_t HasExtraFunctionInfo : 1;
+    uint64_t HasDefaultedOrDeletedInfo : 1;
 
     /// For member functions of complete types, whether this is an ineligible
     /// special member function or an unselected destructor. See
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 6cf1bb2a11c46d..e860b621847808 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3203,7 +3203,7 @@ class Sema final {
   void ActOnDocumentableDecls(ArrayRef<Decl *> Group);
 
   enum class FnBodyKind {
-    /// C++ [dcl.fct.def.general]p1
+    /// C++26 [dcl.fct.def.general]p1
     /// function-body:
     ///   ctor-initializer[opt] compound-statement
     ///   function-try-block
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 8a9427632c3be3..68427c15686fe1 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -3961,8 +3961,9 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
   ToFunction->setDefaultLoc(ToDefaultLoc);
 
   if (Msg)
-    ToFunction->setExtraFunctionInfo(FunctionDecl::ExtraFunctionInfo::Create(
-        Importer.getToContext(), {}, Msg));
+    ToFunction->setDefaultedOrDeletedInfo(
+        FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
+            Importer.getToContext(), {}, Msg));
 
   // Set the parameters.
   for (auto *Param : Parameters) {
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 79af97c8a1f3b9..0a37eac22e3c9b 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3058,7 +3058,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
   FunctionDeclBits.IsTrivialForCall = false;
   FunctionDeclBits.IsDefaulted = false;
   FunctionDeclBits.IsExplicitlyDefaulted = false;
-  FunctionDeclBits.HasExtraFunctionInfo = false;
+  FunctionDeclBits.HasDefaultedOrDeletedInfo = false;
   FunctionDeclBits.IsIneligibleOrNotSelected = false;
   FunctionDeclBits.HasImplicitReturnZero = false;
   FunctionDeclBits.IsLateTemplateParsed = false;
@@ -3092,18 +3092,18 @@ bool FunctionDecl::isVariadic() const {
   return false;
 }
 
-FunctionDecl::ExtraFunctionInfo *
-FunctionDecl::ExtraFunctionInfo::Create(ASTContext &Context,
-                                        ArrayRef<DeclAccessPair> Lookups,
-                                        StringLiteral *DeletedMessage) {
+FunctionDecl::DefaultedOrDeletedFunctionInfo *
+FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
+    ASTContext &Context, ArrayRef<DeclAccessPair> Lookups,
+    StringLiteral *DeletedMessage) {
   static constexpr size_t Alignment =
-      std::max({alignof(ExtraFunctionInfo), alignof(DeclAccessPair),
-                alignof(StringLiteral *)});
+      std::max({alignof(DefaultedOrDeletedFunctionInfo),
+                alignof(DeclAccessPair), alignof(StringLiteral *)});
   size_t Size = totalSizeToAlloc<DeclAccessPair, StringLiteral *>(
       Lookups.size(), !!DeletedMessage);
 
-  ExtraFunctionInfo *Info =
-      new (Context.Allocate(Size, Alignment)) ExtraFunctionInfo;
+  DefaultedOrDeletedFunctionInfo *Info =
+      new (Context.Allocate(Size, Alignment)) DefaultedOrDeletedFunctionInfo;
   Info->NumLookups = Lookups.size();
   Info->HasDeletedMessage = !!DeletedMessage;
 
@@ -3114,36 +3114,40 @@ FunctionDecl::ExtraFunctionInfo::Create(ASTContext &Context,
   return Info;
 }
 
-void FunctionDecl::setExtraFunctionInfo(ExtraFunctionInfo *Info) {
-  assert(!FunctionDeclBits.HasExtraFunctionInfo && "already have this");
+void FunctionDecl::setDefaultedOrDeletedInfo(
+    DefaultedOrDeletedFunctionInfo *Info) {
+  assert(!FunctionDeclBits.HasDefaultedOrDeletedInfo && "already have this");
   assert(!Body && "can't replace function body with defaulted function info");
 
-  FunctionDeclBits.HasExtraFunctionInfo = true;
-  ExtraInfo = Info;
+  FunctionDeclBits.HasDefaultedOrDeletedInfo = true;
+  DefaultedOrDeletedInfo = Info;
 }
 
-void FunctionDecl::ExtraFunctionInfo::setDeletedMessage(
+void FunctionDecl::DefaultedOrDeletedFunctionInfo::setDeletedMessage(
     StringLiteral *Message) {
-  // We should never get here with the ExtraInfo populated, but no space
-  // allocated for the deleted message, since that would require recreating
-  // this, but setExtraFunctionInfo() disallows overwriting an already existing
-  // ExtraFunctionInfo.
-  assert(HasDeletedMessage && "Explicitly deleting defaulted function?");
+  // We should never get here with the DefaultedOrDeletedInfo populated, but
+  // no space allocated for the deleted message, since that would require
+  // recreating this, but setDefaultedOrDeletedInfo() disallows overwriting
+  // an already existing DefaultedOrDeletedFunctionInfo.
+  assert(HasDeletedMessage &&
+         "No space to store a delete message in this DefaultedOrDeletedInfo");
   *getTrailingObjects<StringLiteral *>() = Message;
 }
 
-FunctionDecl::ExtraFunctionInfo *FunctionDecl::getExtraFunctionInfo() const {
-  return FunctionDeclBits.HasExtraFunctionInfo ? ExtraInfo : nullptr;
+FunctionDecl::DefaultedOrDeletedFunctionInfo *
+FunctionDecl::getDefalutedOrDeletedInfo() const {
+  return FunctionDeclBits.HasDefaultedOrDeletedInfo ? DefaultedOrDeletedInfo
+                                                    : nullptr;
 }
 
 void FunctionDecl::setDeletedMessage(StringLiteral *Message) {
   assert(Message && "Should not be called with nullptr");
   assert(isDeletedAsWritten() && "Function must be deleted");
-  if (FunctionDeclBits.HasExtraFunctionInfo)
-    ExtraInfo->setDeletedMessage(Message);
+  if (FunctionDeclBits.HasDefaultedOrDeletedInfo)
+    DefaultedOrDeletedInfo->setDeletedMessage(Message);
   else
-    setExtraFunctionInfo(
-        ExtraFunctionInfo::Create(getASTContext(), {}, Message));
+    setDefaultedOrDeletedInfo(DefaultedOrDeletedFunctionInfo::Create(
+        getASTContext(), /*Lookups=*/{}, Message));
 }
 
 bool FunctionDecl::hasBody(const FunctionDecl *&Definition) const {
@@ -3230,7 +3234,7 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const {
   if (!hasBody(Definition))
     return nullptr;
 
-  assert(!Definition->FunctionDeclBits.HasExtraFunctionInfo &&
+  assert(!Definition->FunctionDeclBits.HasDefaultedOrDeletedInfo &&
          "definition should not have a body");
   if (Definition->Body)
     return Definition->Body.get(getASTContext().getExternalSource());
@@ -3239,7 +3243,7 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const {
 }
 
 void FunctionDecl::setBody(Stmt *B) {
-  FunctionDeclBits.HasExtraFunctionInfo = false;
+  FunctionDeclBits.HasDefaultedOrDeletedInfo = false;
   Body = LazyDeclStmtPtr(B);
   if (B)
     EndRangeLoc = B->getEndLoc();
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index cd033ab87aa5ff..1b75b1ca693c64 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16155,8 +16155,15 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
     // This is meant to pop the context added in ActOnStartOfFunctionDef().
     ExitFunctionBodyRAII ExitRAII(*this, isLambdaCallOperator(FD));
     if (FD) {
-      // Do not overwrite the potentially present ExtraInfo of a deleted
-      // function declaration.
+      // If this is called by Parser::ParseFunctionDefinition() after marking
+      // the declaration as deleted, and if the deleted-function-body contains
+      // a message (C++26), then a DefaultedOrDeletedInfo will have already been
+      // added to store that message; do not overwrite it in that case.
+      //
+      // Since this would always set the body to 'nullptr' in that case anyway,
+      // which is already done when the function decl is initially created,
+      // always skipping this irrespective of whether there is a delete message
+      // should not be a problem.
       if (!FD->isDeletedAsWritten())
         FD->setBody(Body);
       FD->setWillHaveBody(false);
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index fb91b42e1b1663..67e298c40cd517 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7962,7 +7962,7 @@ class DefaultedComparisonVisitor {
   DefaultedComparisonVisitor(Sema &S, CXXRecordDecl *RD, FunctionDecl *FD,
                              DefaultedComparisonKind DCK)
       : S(S), RD(RD), FD(FD), DCK(DCK) {
-    if (auto *Info = FD->getExtraFunctionInfo()) {
+    if (auto *Info = FD->getDefalutedOrDeletedInfo()) {
       // FIXME: Change CreateOverloadedBinOp to take an ArrayRef instead of an
       // UnresolvedSet to avoid this copy.
       Fns.assign(Info->getUnqualifiedLookups().begin(),
@@ -8830,8 +8830,9 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
     UnresolvedSet<32> Operators;
     lookupOperatorsForDefaultedComparison(*this, S, Operators,
                                           FD->getOverloadedOperator());
-    FD->setExtraFunctionInfo(
-        FunctionDecl::ExtraFunctionInfo::Create(Context, Operators.pairs()));
+    FD->setDefaultedOrDeletedInfo(
+        FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
+            Context, Operators.pairs()));
   }
 
   // C++2a [class.compare.default]p1:
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index f6a5353aec3ecd..c0d4531cb32110 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4856,7 +4856,7 @@ TemplateDeclInstantiator::InitMethodInstantiation(CXXMethodDecl *New,
 bool TemplateDeclInstantiator::SubstDefaultedFunction(FunctionDecl *New,
                                                       FunctionDecl *Tmpl) {
   // Transfer across any unqualified lookups.
-  if (auto *DFI = Tmpl->getExtraFunctionInfo()) {
+  if (auto *DFI = Tmpl->getDefalutedOrDeletedInfo()) {
     SmallVector<DeclAccessPair, 32> Lookups;
     Lookups.reserve(DFI->getUnqualifiedLookups().size());
     bool AnyChanged = false;
@@ -4871,10 +4871,10 @@ bool TemplateDeclInstantiator::SubstDefaultedFunction(FunctionDecl *New,
 
     // It's unlikely that substitution will change any declarations. Don't
     // store an unnecessary copy in that case.
-    New->setExtraFunctionInfo(
-        AnyChanged
-            ? FunctionDecl::ExtraFunctionInfo::Create(SemaRef.Context, Lookups)
-            : DFI);
+    New->setDefaultedOrDeletedInfo(
+        AnyChanged ? FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
+                         SemaRef.Context, Lookups)
+                   : DFI);
   }
 
   SemaRef.SetDeclDefaulted(New, Tmpl->getLocation());
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index aad6747eca6be2..998fc74ca5916d 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1110,9 +1110,9 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
   }
 
   if (FD->isDefaulted() || FD->isDeletedAsWritten()) {
-    // If 'Info' is nonzero, we need to read an ExtraFunctionInfo; if,
+    // If 'Info' is nonzero, we need to read an DefaultedOrDeletedInfo; if,
     // additionally, the second bit is also set, we also need to read
-    // a DeletedMessage for the ExtraFunctionInfo.
+    // a DeletedMessage for the DefaultedOrDeletedInfo.
     if (auto Info = Record.readInt()) {
       bool HasMessage = Info & 2;
       StringLiteral *DeletedMessage =
@@ -1126,8 +1126,9 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
         Lookups.push_back(DeclAccessPair::make(ND, AS));
       }
 
-      FD->setExtraFunctionInfo(FunctionDecl::ExtraFunctionInfo::Create(
-          Reader.getContext(), Lookups, DeletedMessage));
+      FD->setDefaultedOrDeletedInfo(
+          FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
+              Reader.getContext(), Lookups, DeletedMessage));
     }
   }
 
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 2dd2f1f14a8b6c..a9f71929a3b23d 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -750,9 +750,9 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
     Record.push_back(D->getODRHash());
 
   if (D->isDefaulted() || D->isDeletedAsWritten()) {
-    if (auto *FDI = D->getExtraFunctionInfo()) {
-      // Store both that there is an ExtraFunctionInfo and whether it contains
-      // a DeletedMessage.
+    if (auto *FDI = D->getDefalutedOrDeletedInfo()) {
+      // Store both that there is an DefaultedOrDeletedInfo and whether it
+      // contains a DeletedMessage.
       StringLiteral *DeletedMessage = FDI->getDeletedMessage();
       Record.push_back(1 | (DeletedMessage ? 2 : 0));
       if (DeletedMessage)
diff --git a/clang/test/PCH/cxx2a-defaulted-comparison.cpp b/clang/test/PCH/cxx2a-defaulted-comparison.cpp
index cb1e8ef0d7c900..bdf649919659da 100644
--- a/clang/test/PCH/cxx2a-defaulted-comparison.cpp
+++ b/clang/test/PCH/cxx2a-defaulted-comparison.cpp
@@ -22,7 +22,7 @@ namespace std {
   constexpr strong_ordering strong_ordering::less = {-1};
 }
 
-// Ensure that we can round-trip ExtraFunctionInfo through an AST file.
+// Ensure that we can round-trip DefaultedOrDeletedInfo through an AST file.
 namespace LookupContext {
   struct A {};
 

>From 99c8dd1e06fc674a49eabc25365354517d3be977 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 3 Apr 2024 19:31:59 +0200
Subject: [PATCH 17/22] [Clang] Handle remaining diagnostics

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  10 +-
 clang/lib/Sema/SemaCast.cpp                   |  20 +-
 clang/lib/Sema/SemaInit.cpp                   |  19 +-
 clang/lib/Sema/SemaOverload.cpp               |  42 +++-
 .../SemaCXX/cxx2c-delete-with-message.cpp     | 203 ++++++++++++++++++
 5 files changed, 270 insertions(+), 24 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d49ba971d31ea2..49c9ac137a121c 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4908,12 +4908,12 @@ def err_ovl_ambiguous_conversion_in_cast : Error<
   "dynamic_cast|C-style cast|functional-style cast|}0 from %1 to %2">;
 def err_ovl_deleted_conversion_in_cast : Error<
   "%select{|static_cast|reinterpret_cast|dynamic_cast|C-style cast|"
-  "functional-style cast|}0 from %1 to %2 uses deleted function">;
+  "functional-style cast|}0 from %1 to %2 uses deleted function%select{|: %4}3">;
 def err_ovl_ambiguous_init : Error<"call to constructor of %0 is ambiguous">;
 def err_ref_init_ambiguous : Error<
   "reference initialization of type %0 with initializer of type %1 is ambiguous">;
 def err_ovl_deleted_init : Error<
-  "call to deleted constructor of %0">;
+  "call to deleted constructor of %0%select{|: %2}1">;
 def err_ovl_deleted_special_init : Error<
   "call to implicitly-deleted %select{default constructor|copy constructor|"
   "move constructor|copy assignment operator|move assignment operator|"
@@ -4939,7 +4939,7 @@ def note_ovl_ambiguous_oper_binary_reversed_candidate : Note<
 def err_ovl_no_viable_oper : Error<"no viable overloaded '%0'">;
 def note_assign_lhs_incomplete : Note<"type %0 is incomplete">;
 def err_ovl_deleted_oper : Error<
-  "overload resolution selected deleted operator '%0'">;
+  "overload resolution selected deleted operator '%0'%select{|: %2}1">;
 def err_ovl_deleted_special_oper : Error<
   "object of type %0 cannot be %select{constructed|copied|moved|assigned|"
   "assigned|destroyed}1 because its %sub{select_special_member_kind}1 is "
@@ -4976,7 +4976,7 @@ def err_ovl_ambiguous_object_call : Error<
 def err_ovl_ambiguous_subscript_call : Error<
   "call to subscript operator of type %0 is ambiguous">;
 def err_ovl_deleted_object_call : Error<
-  "call to deleted function call operator in type %0">;
+  "call to deleted function call operator in type %0%select{|: %2}1">;
 def note_ovl_surrogate_cand : Note<"conversion candidate of type %0">;
 def err_member_call_without_object : Error<
   "call to %select{non-static|explicit}0 member function without an object argument">;
@@ -8276,7 +8276,7 @@ def err_typecheck_nonviable_condition_incomplete : Error<
   "no viable conversion%diff{ from $ to incomplete type $|}0,1">;
 def err_typecheck_deleted_function : Error<
   "conversion function %diff{from $ to $|between types}0,1 "
-  "invokes a deleted function">;
+  "invokes a deleted function%select{|: %3}2">;
 
 def err_expected_class_or_namespace : Error<"%0 is not a class"
   "%select{ or namespace|, namespace, or enumeration}1">;
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index 9d85568d97b2d2..9042d31a58e7fc 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -498,10 +498,22 @@ static bool tryDiagnoseOverloadedCast(Sema &S, CastType CT,
     howManyCandidates = OCD_AmbiguousCandidates;
     break;
 
-  case OR_Deleted:
-    msg = diag::err_ovl_deleted_conversion_in_cast;
-    howManyCandidates = OCD_ViableCandidates;
-    break;
+  case OR_Deleted: {
+    OverloadCandidateSet::iterator Best;
+    OverloadingResult Res =
+        candidates.BestViableFunction(S, range.getBegin(), Best);
+    assert(Res == OR_Deleted && "Inconsistent overload resolution");
+
+    StringLiteral *Msg = Best->Function->getDeletedMessage();
+    candidates.NoteCandidates(
+        PartialDiagnosticAt(range.getBegin(),
+                            S.PDiag(diag::err_ovl_deleted_conversion_in_cast)
+                                << CT << srcType << destType << !!Msg
+                                << (Msg ? Msg->getString() : StringRef())
+                                << range << src->getSourceRange()),
+        S, OCD_ViableCandidates, src);
+    return true;
+  }
   }
 
   candidates.NoteCandidates(
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index e2a1951f1062cb..9e3f6f1fa96217 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -9762,12 +9762,15 @@ bool InitializationSequence::Diagnose(Sema &S,
       break;
     }
     case OR_Deleted: {
-      S.Diag(Kind.getLocation(), diag::err_typecheck_deleted_function)
-        << OnlyArg->getType() << DestType.getNonReferenceType()
-        << Args[0]->getSourceRange();
       OverloadCandidateSet::iterator Best;
       OverloadingResult Ovl
         = FailedCandidateSet.BestViableFunction(S, Kind.getLocation(), Best);
+
+      StringLiteral *Msg = Best->Function->getDeletedMessage();
+      S.Diag(Kind.getLocation(), diag::err_typecheck_deleted_function)
+          << OnlyArg->getType() << DestType.getNonReferenceType() << !!Msg
+          << (Msg ? Msg->getString() : StringRef())
+          << Args[0]->getSourceRange();
       if (Ovl == OR_Deleted) {
         S.NoteDeletedFunction(Best->Function);
       } else {
@@ -10025,9 +10028,12 @@ bool InitializationSequence::Diagnose(Sema &S,
           S.Diag(Kind.getLocation(), diag::err_ovl_deleted_special_init)
             << S.getSpecialMember(cast<CXXMethodDecl>(Best->Function))
             << DestType << ArgsRange;
-        else
+        else {
+          StringLiteral *Msg = Best->Function->getDeletedMessage();
           S.Diag(Kind.getLocation(), diag::err_ovl_deleted_init)
-              << DestType << ArgsRange;
+              << DestType << !!Msg << (Msg ? Msg->getString() : StringRef())
+              << ArgsRange;
+        }
 
         S.NoteDeletedFunction(Best->Function);
         break;
@@ -11075,6 +11081,9 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
   }
 
   case OR_Deleted: {
+    // FIXME: There are no tests for this diagnostic, and it doesn't seem
+    // like we ever get here; attempts to trigger this seem to yield a
+    // generic c'all to deleted function' diagnostic instead.
     Diag(Kind.getLocation(), diag::err_deduced_class_template_deleted)
       << TemplateName;
     NoteDeletedFunction(Best->Function);
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 92236d8b74d340..c698fea658530c 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -14525,20 +14525,24 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
         UnaryOperator::getOpcodeStr(Opc), OpLoc);
     return ExprError();
 
-  case OR_Deleted:
+  case OR_Deleted: {
     // CreateOverloadedUnaryOp fills the first element of ArgsArray with the
     // object whose method was called. Later in NoteCandidates size of ArgsArray
     // is passed further and it eventually ends up compared to number of
     // function candidate parameters which never includes the object parameter,
     // so slice ArgsArray to make sure apples are compared to apples.
+    StringLiteral *Msg = Best->Function->getDeletedMessage();
     CandidateSet.NoteCandidates(
         PartialDiagnosticAt(OpLoc, PDiag(diag::err_ovl_deleted_oper)
                                        << UnaryOperator::getOpcodeStr(Opc)
+                                       << !!Msg
+                                       << (Msg ? Msg->getString() : StringRef())
                                        << Input->getSourceRange()),
         *this, OCD_AllCandidates, ArgsArray.drop_front(),
         UnaryOperator::getOpcodeStr(Opc), OpLoc);
     return ExprError();
   }
+  }
 
   // Either we found no viable overloaded operator or we matched a
   // built-in operator. In either case, fall through to trying to
@@ -15055,7 +15059,7 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
           OpLoc);
       return ExprError();
 
-    case OR_Deleted:
+    case OR_Deleted: {
       if (isImplicitlyDeleted(Best->Function)) {
         FunctionDecl *DeletedFD = Best->Function;
         DefaultedFunctionKind DFK = getDefaultedFunctionKind(DeletedFD);
@@ -15073,16 +15077,20 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
         NoteDeletedFunction(DeletedFD);
         return ExprError();
       }
+
+      StringLiteral *Msg = Best->Function->getDeletedMessage();
       CandidateSet.NoteCandidates(
           PartialDiagnosticAt(
               OpLoc, PDiag(diag::err_ovl_deleted_oper)
                          << getOperatorSpelling(Best->Function->getDeclName()
                                                     .getCXXOverloadedOperator())
+                         << !!Msg << (Msg ? Msg->getString() : StringRef())
                          << Args[0]->getSourceRange()
                          << Args[1]->getSourceRange()),
           *this, OCD_AllCandidates, Args, BinaryOperator::getOpcodeStr(Opc),
           OpLoc);
       return ExprError();
+    }
   }
 
   // We matched a built-in operator; build it.
@@ -15394,14 +15402,18 @@ ExprResult Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
       }
       return ExprError();
 
-    case OR_Deleted:
+    case OR_Deleted: {
+      StringLiteral *Msg = Best->Function->getDeletedMessage();
       CandidateSet.NoteCandidates(
-          PartialDiagnosticAt(LLoc, PDiag(diag::err_ovl_deleted_oper)
-                                        << "[]" << Args[0]->getSourceRange()
-                                        << Range),
+          PartialDiagnosticAt(LLoc,
+                              PDiag(diag::err_ovl_deleted_oper)
+                                  << "[]" << !!Msg
+                                  << (Msg ? Msg->getString() : StringRef())
+                                  << Args[0]->getSourceRange() << Range),
           *this, OCD_AllCandidates, Args, "[]", LLoc);
       return ExprError();
     }
+    }
 
   // We matched a built-in operator; build it.
   return CreateBuiltinArraySubscriptExpr(Args[0], LLoc, Args[1], RLoc);
@@ -15881,15 +15893,21 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
           *this, OCD_AmbiguousCandidates, Args);
     break;
 
-  case OR_Deleted:
+  case OR_Deleted: {
+    // FIXME: Is this diagnostic here really necessary? It seems that
+    //   1. we don't have any tests for this diagnostic, and
+    //   2. we already issue err_deleted_function_use for this later on anyway.
+    StringLiteral *Msg = Best->Function->getDeletedMessage();
     CandidateSet.NoteCandidates(
         PartialDiagnosticAt(Object.get()->getBeginLoc(),
                             PDiag(diag::err_ovl_deleted_object_call)
-                                << Object.get()->getType()
+                                << Object.get()->getType() << !!Msg
+                                << (Msg ? Msg->getString() : StringRef())
                                 << Object.get()->getSourceRange()),
         *this, OCD_AllCandidates, Args);
     break;
   }
+  }
 
   if (Best == CandidateSet.end())
     return true;
@@ -16088,13 +16106,17 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
           *this, OCD_AmbiguousCandidates, Base);
     return ExprError();
 
-  case OR_Deleted:
+  case OR_Deleted: {
+    StringLiteral *Msg = Best->Function->getDeletedMessage();
     CandidateSet.NoteCandidates(
         PartialDiagnosticAt(OpLoc, PDiag(diag::err_ovl_deleted_oper)
-                                       << "->" << Base->getSourceRange()),
+                                       << "->" << !!Msg
+                                       << (Msg ? Msg->getString() : StringRef())
+                                       << Base->getSourceRange()),
         *this, OCD_AllCandidates, Base);
     return ExprError();
   }
+  }
 
   CheckMemberOperatorAccess(OpLoc, Base, nullptr, Best->FoundDecl);
 
diff --git a/clang/test/SemaCXX/cxx2c-delete-with-message.cpp b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
index 7183a9513c31ff..22e65d902ecd4a 100644
--- a/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
+++ b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
@@ -20,6 +20,21 @@ void f() = delete("deleted (5)"); // expected-note {{explicitly deleted}}
 template <typename T>
 T g() = delete("deleted (6)"); // expected-note {{explicitly deleted}}
 
+template <typename T, typename U>
+struct TSp {
+  T f() = delete("deleted (7)"); // expected-note 2 {{explicitly marked deleted}}
+};
+
+template <typename T>
+struct TSp<T, int> {
+  T f() = delete("deleted (8)"); // expected-note {{explicitly marked deleted}}
+};
+
+template <>
+struct TSp<int, int> {
+  int f() = delete("deleted (9)"); // expected-note {{explicitly marked deleted}}
+};
+
 void u1() = delete(L"\xFFFFFFFF"); // expected-error {{an unevaluated string literal cannot have an encoding prefix}} \
                                    // expected-error {{invalid escape sequence '\xFFFFFFFF' in an unevaluated string literal}}
 void u2() = delete(u"\U000317FF"); // expected-error {{an unevaluated string literal cannot have an encoding prefix}}
@@ -60,6 +75,10 @@ void h() {
   TS<int>{}.g<int>(0); // expected-error {{call to deleted member function 'g': deleted (4)}}
   f(); // expected-error {{call to deleted function 'f': deleted (5)}}
   g<int>(); // expected-error {{call to deleted function 'g': deleted (6)}}
+  TSp<double, double>{}.f(); // expected-error {{attempt to use a deleted function: deleted (7)}}
+  TSp<int, double>{}.f(); // expected-error {{attempt to use a deleted function: deleted (7)}}
+  TSp<double, int>{}.f(); // expected-error {{attempt to use a deleted function: deleted (8)}}
+  TSp<int, int>{}.f(); // expected-error {{attempt to use a deleted function: deleted (9)}}
   u3(); // expected-error {{call to deleted function 'u3': Ω}}
   u4(); // expected-error {{call to deleted function 'u4': ሴ}}
   u6(); // expected-error {{call to deleted function 'u6': '"?\<U+0007><U+0008>}}
@@ -68,3 +87,187 @@ void h() {
   u12(); // expected-error {{call to deleted function 'u12': ‎abc‏def‏gh}}
   u13(); // expected-error {{call to deleted function 'u13': 🏳️‍🌈 🏴󠁧󠁢󠁥󠁮󠁧󠁿 🇪🇺}}
 }
+
+struct C {
+  C() = delete("deleted (C, Constructor)"); // expected-note {{explicitly marked deleted}}
+  C(int) = delete("deleted (C, C(int))"); // expected-note {{explicitly marked deleted}}
+  C(const C&) = delete("deleted (C, Copy Constructor)"); // expected-note {{explicitly marked deleted}}
+  C(C&&) = delete("deleted (C, Move Constructor)"); // expected-note {{explicitly marked deleted}}
+  C& operator=(const C&) = delete("deleted (C, Copy Assignment)"); // expected-note 2 {{explicitly deleted}}
+  C& operator=(C&&) = delete("deleted (C, Move Assignment)"); // expected-note {{explicitly deleted}} expected-note {{not viable}}
+  ~C() = delete("deleted (C, Destructor)"); // expected-note {{explicitly marked deleted}}
+  void* operator new(__SIZE_TYPE__) = delete("deleted (C, New)"); // expected-note {{explicitly deleted}}
+  void operator delete(void*) = delete("deleted (C, Delete)"); // expected-note {{explicitly marked deleted}}
+};
+
+template <typename T, typename U>
+struct TC {
+  TC() = delete("deleted (TC, Constructor)"); // expected-note {{explicitly marked deleted}}
+  TC(int) = delete("deleted (TC, TC(int))"); // expected-note {{explicitly marked deleted}}
+  TC(const TC&) = delete("deleted (TC, Copy Constructor)"); // expected-note {{explicitly marked deleted}}
+  TC(TC&&) = delete("deleted (TC, Move Constructor)"); // expected-note {{explicitly marked deleted}}
+  TC& operator=(const TC&) = delete("deleted (TC, Copy Assignment)"); // expected-note 2 {{explicitly deleted}}
+  TC& operator=(TC&&) = delete("deleted (TC, Move Assignment)");  // expected-note {{explicitly deleted}} expected-note {{not viable}}
+  ~TC() = delete("deleted (TC, Destructor)"); // expected-note {{explicitly marked deleted}}
+  void* operator new(__SIZE_TYPE__) = delete("deleted (TC, New)"); // expected-note {{explicitly deleted}}
+  void operator delete(void*) = delete("deleted (TC, Delete)"); // expected-note {{explicitly marked deleted}}
+};
+
+template <typename T>
+struct TC<T, int> {
+  TC() = delete("deleted (TC<T, int>, Constructor)"); // expected-note {{explicitly marked deleted}}
+  TC(int) = delete("deleted (TC<T, int>, TC(int))"); // expected-note {{explicitly marked deleted}}
+  TC(const TC&) = delete("deleted (TC<T, int>, Copy Constructor)"); // expected-note {{explicitly marked deleted}}
+  TC(TC&&) = delete("deleted (TC<T, int>, Move Constructor)"); // expected-note {{explicitly marked deleted}}
+  TC& operator=(const TC&) = delete("deleted (TC<T, int>, Copy Assignment)"); // expected-note 2 {{explicitly deleted}}
+  TC& operator=(TC&&) = delete("deleted (TC<T, int>, Move Assignment)"); // expected-note {{explicitly deleted}} expected-note {{not viable}}
+  ~TC() = delete("deleted (TC<T, int>, Destructor)"); // expected-note {{explicitly marked deleted}}
+  void* operator new(__SIZE_TYPE__) = delete("deleted (TC<T, int>, New)"); // expected-note {{explicitly deleted}}
+  void operator delete(void*) = delete("deleted (TC<T, int>, Delete)"); // expected-note {{explicitly marked deleted}}
+};
+
+template <>
+struct TC<int, int> {
+  TC() = delete("deleted (TC<int, int>, Constructor)"); // expected-note {{explicitly marked deleted}}
+  TC(int) = delete("deleted (TC<int, int>, TC(int))"); // expected-note {{explicitly marked deleted}}
+  TC(const TC&) = delete("deleted (TC<int, int>, Copy Constructor)"); // expected-note {{explicitly marked deleted}}
+  TC(TC&&) = delete("deleted (TC<int, int>, Move Constructor)"); // expected-note {{explicitly marked deleted}}
+  TC& operator=(const TC&) = delete("deleted (TC<int, int>, Copy Assignment)"); // expected-note 2 {{explicitly deleted}}
+  TC& operator=(TC&&) = delete("deleted (TC<int, int>, Move Assignment)"); // expected-note {{explicitly deleted}} expected-note {{not viable}}
+  ~TC() = delete("deleted (TC<int, int>, Destructor)"); // expected-note {{explicitly marked deleted}}
+  void* operator new(__SIZE_TYPE__) = delete("deleted (TC<int, int>, New)"); // expected-note {{explicitly deleted}}
+  void operator delete(void*) = delete("deleted (TC<int, int>, Delete)"); // expected-note {{explicitly marked deleted}}
+};
+
+void special_members(
+  C& c1,
+  C& c2,
+  TC<double, double>& tc1,
+  TC<double, double>& tc2,
+  TC<double, int>& tc_int1,
+  TC<double, int>& tc_int2,
+  TC<int, int>& tc_int_int1,
+  TC<int, int>& tc_int_int2
+) {
+  C{}; // expected-error {{call to deleted constructor of 'C': deleted (C, Constructor)}}
+  C{c1}; // expected-error {{call to deleted constructor of 'C': deleted (C, Copy Constructor)}}
+  C{static_cast<C&&>(c1)}; // expected-error {{call to deleted constructor of 'C': deleted (C, Move Constructor)}}
+  c1 = c2; // expected-error {{overload resolution selected deleted operator '=': deleted (C, Copy Assignment)}}
+  c1 = static_cast<C&&>(c2); // expected-error {{overload resolution selected deleted operator '=': deleted (C, Move Assignment)}}
+  c1.~C(); // expected-error {{attempt to use a deleted function: deleted (C, Destructor)}}
+  new C{}; // expected-error {{call to deleted function 'operator new': deleted (C, New)}}
+  delete &c2; // expected-error {{attempt to use a deleted function: deleted (C, Delete)}}
+
+  TC<double, double>{}; // expected-error {{call to deleted constructor of 'TC<double, double>': deleted (TC, Constructor)}}
+  TC<double, double>{tc1}; // expected-error {{call to deleted constructor of 'TC<double, double>': deleted (TC, Copy Constructor)}}
+  TC<double, double>{static_cast<TC<double, double>&&>(tc1)}; // expected-error {{call to deleted constructor of 'TC<double, double>': deleted (TC, Move Constructor)}}
+  tc1 = tc2; // expected-error {{overload resolution selected deleted operator '=': deleted (TC, Copy Assignment)}}
+  tc1 = static_cast<TC<double, double>&&>(tc2); // expected-error {{overload resolution selected deleted operator '=': deleted (TC, Move Assignment)}}
+  tc1.~TC(); // expected-error {{attempt to use a deleted function: deleted (TC, Destructor)}}
+  new TC<double, double>{}; // expected-error {{call to deleted function 'operator new': deleted (TC, New)}}
+  delete &tc2; // expected-error {{attempt to use a deleted function: deleted (TC, Delete)}}
+
+  TC<double, int>{}; // expected-error {{call to deleted constructor of 'TC<double, int>': deleted (TC<T, int>, Constructor)}}
+  TC<double, int>{tc_int1}; // expected-error {{call to deleted constructor of 'TC<double, int>': deleted (TC<T, int>, Copy Constructor)}}
+  TC<double, int>{static_cast<TC<double, int>&&>(tc_int1)}; // expected-error {{call to deleted constructor of 'TC<double, int>': deleted (TC<T, int>, Move Constructor)}}
+  tc_int1 = tc_int2; // expected-error {{overload resolution selected deleted operator '=': deleted (TC<T, int>, Copy Assignment)}}
+  tc_int1 = static_cast<TC<double, int>&&>(tc_int2); // expected-error {{overload resolution selected deleted operator '=': deleted (TC<T, int>, Move Assignment)}}
+  tc_int1.~TC(); // expected-error {{attempt to use a deleted function: deleted (TC<T, int>, Destructor)}}
+  new TC<double, int>{}; // expected-error {{call to deleted function 'operator new': deleted (TC<T, int>, New)}}
+  delete &tc_int2; // expected-error {{attempt to use a deleted function: deleted (TC<T, int>, Delete)}}
+
+  TC<int, int>{}; // expected-error {{call to deleted constructor of 'TC<int, int>': deleted (TC<int, int>, Constructor)}}
+  TC<int, int>{tc_int_int1}; // expected-error {{call to deleted constructor of 'TC<int, int>': deleted (TC<int, int>, Copy Constructor)}}
+  TC<int, int>{static_cast<TC<int, int>&&>(tc_int_int1)}; // expected-error {{call to deleted constructor of 'TC<int, int>': deleted (TC<int, int>, Move Constructor)}}
+  tc_int_int1 = tc_int_int2; // expected-error {{overload resolution selected deleted operator '=': deleted (TC<int, int>, Copy Assignment)}}
+  tc_int_int1 = static_cast<TC<int, int>&&>(tc_int_int2); // expected-error {{overload resolution selected deleted operator '=': deleted (TC<int, int>, Move Assignment)}}
+  tc_int_int1.~TC(); // expected-error {{attempt to use a deleted function: deleted (TC<int, int>, Destructor)}}
+  new TC<int, int>{}; // expected-error {{call to deleted function 'operator new': deleted (TC<int, int>, New)}}
+  delete &tc_int_int2; // expected-error {{attempt to use a deleted function: deleted (TC<int, int>, Delete)}}
+}
+
+C conv1() { return 1; } // expected-error {{conversion function from 'int' to 'C' invokes a deleted function: deleted (C, C(int))}}
+TC<double, double> conv2() { return 1; } // expected-error {{conversion function from 'int' to 'TC<double, double>' invokes a deleted function: deleted (TC, TC(int))}}
+TC<double, int> conv3() { return 1; } // expected-error {{conversion function from 'int' to 'TC<double, int>' invokes a deleted function: deleted (TC<T, int>, TC(int))}}
+TC<int, int> conv4() { return 1; } // expected-error {{conversion function from 'int' to 'TC<int, int>' invokes a deleted function: deleted (TC<int, int>, TC(int))}}
+
+struct O {
+  int x;
+  int operator+() = delete("deleted (O, +)"); // expected-note {{explicitly deleted}}
+  O* operator->() = delete("deleted (O, ->)"); // expected-note {{explicitly deleted}}
+  int operator-(O) = delete("deleted (O, -)"); // expected-note {{explicitly deleted}}
+  int operator[](O) = delete("deleted (O, [])"); // expected-note {{explicitly deleted}}
+  int operator()(O) = delete("deleted (O, ())"); // expected-note {{explicitly marked deleted}} expected-note {{explicitly deleted}}
+  explicit operator bool() = delete("deleted (O, operator bool)"); // expected-note {{explicitly marked deleted}} expected-note {{explicitly deleted}}
+};
+
+template <typename T, typename U>
+struct TO {
+  T x;
+  T operator+() = delete("deleted (TO, +)"); // expected-note {{explicitly deleted}}
+  T* operator->() = delete("deleted (TO, ->)"); // expected-note {{explicitly deleted}}
+  T operator-(TO) = delete("deleted (TO, -)"); // expected-note {{explicitly deleted}}
+  T operator[](TO) = delete("deleted (TO, [])"); // expected-note {{explicitly deleted}}
+  T operator()(TO) = delete("deleted (TO, ())"); // expected-note {{explicitly marked deleted}} expected-note {{explicitly deleted}}
+  explicit operator bool() = delete("deleted (TO, operator bool)"); // expected-note {{explicitly marked deleted}} expected-note {{explicitly deleted}}
+};
+
+template <typename T>
+struct TO<T, int> {
+  T x;
+  T operator+() = delete("deleted (TO<T, int>, +)"); // expected-note {{explicitly deleted}}
+  T* operator->() = delete("deleted (TO<T, int>, ->)"); // expected-note {{explicitly deleted}}
+  T operator-(TO) = delete("deleted (TO<T, int>, -)"); // expected-note {{explicitly deleted}}
+  T operator[](TO) = delete("deleted (TO<T, int>, [])"); // expected-note {{explicitly deleted}}
+  T operator()(TO) = delete("deleted (TO<T, int>, ())"); // expected-note {{explicitly marked deleted}} expected-note {{explicitly deleted}}
+  explicit operator bool() = delete("deleted (TO<T, int>, operator bool)"); // expected-note {{explicitly marked deleted}} expected-note {{explicitly deleted}}
+};
+
+template <>
+struct TO<int, int> {
+  int x;
+  int operator+() = delete("deleted (TO<int, int>, +)"); // expected-note {{explicitly deleted}}
+  int* operator->() = delete("deleted (TO<int, int>, ->)"); // expected-note {{explicitly deleted}}
+  int operator-(TO) = delete("deleted (TO<int, int>, -)"); // expected-note {{explicitly deleted}}
+  int operator[](TO) = delete("deleted (TO<int, int>, [])"); // expected-note {{explicitly deleted}}
+  int operator()(TO) = delete("deleted (TO<int, int>, ())"); // expected-note {{explicitly marked deleted}} expected-note {{explicitly deleted}}
+  explicit operator bool() = delete("deleted (TO<int, int>, operator bool)"); // expected-note {{explicitly marked deleted}} expected-note {{explicitly deleted}}
+};
+
+void operators() {
+  O o;
+  +o; // expected-error {{overload resolution selected deleted operator '+': deleted (O, +)}}
+  o->x; // expected-error {{overload resolution selected deleted operator '->': deleted (O, ->)}}
+  o - o; // expected-error {{overload resolution selected deleted operator '-': deleted (O, -)}}
+  o[o]; // expected-error {{overload resolution selected deleted operator '[]': deleted (O, [])}}
+  o(o); // expected-error {{call to deleted function call operator in type 'O': deleted (O, ())}} expected-error {{attempt to use a deleted function: deleted (O, ())}}
+  if (o) {} // expected-error {{attempt to use a deleted function: deleted (O, operator bool)}}
+  static_cast<bool>(o); // expected-error {{static_cast from 'O' to 'bool' uses deleted function: deleted (O, operator bool)}}
+
+  TO<double, double> to;
+  +to; // expected-error {{overload resolution selected deleted operator '+': deleted (TO, +)}}
+  to->x; // expected-error {{overload resolution selected deleted operator '->': deleted (TO, ->)}}
+  to - to; // expected-error {{overload resolution selected deleted operator '-': deleted (TO, -)}}
+  to[to]; // expected-error {{overload resolution selected deleted operator '[]': deleted (TO, [])}}
+  to(to); // expected-error {{call to deleted function call operator in type 'TO<double, double>': deleted (TO, ())}} expected-error {{attempt to use a deleted function: deleted (TO, ())}}
+  if (to) {} // expected-error {{attempt to use a deleted function: deleted (TO, operator bool)}}
+  static_cast<bool>(to); // expected-error {{static_cast from 'TO<double, double>' to 'bool' uses deleted function: deleted (TO, operator bool)}}
+
+  TO<double, int> to_int;
+  +to_int; // expected-error {{overload resolution selected deleted operator '+': deleted (TO<T, int>, +)}}
+  to_int->x; // expected-error {{overload resolution selected deleted operator '->': deleted (TO<T, int>, ->)}}
+  to_int - to_int; // expected-error {{overload resolution selected deleted operator '-': deleted (TO<T, int>, -)}}
+  to_int[to_int]; // expected-error {{overload resolution selected deleted operator '[]': deleted (TO<T, int>, [])}}
+  to_int(to_int); // expected-error {{call to deleted function call operator in type 'TO<double, int>': deleted (TO<T, int>, ())}} expected-error {{attempt to use a deleted function: deleted (TO<T, int>, ())}}
+  if (to_int) {} // expected-error {{attempt to use a deleted function: deleted (TO<T, int>, operator bool)}}
+  static_cast<bool>(to_int); // expected-error {{static_cast from 'TO<double, int>' to 'bool' uses deleted function: deleted (TO<T, int>, operator bool)}}
+
+  TO<int, int> to_int_int;
+  +to_int_int; // expected-error {{overload resolution selected deleted operator '+': deleted (TO<int, int>, +)}}
+  to_int_int->x; // expected-error {{overload resolution selected deleted operator '->': deleted (TO<int, int>, ->)}}
+  to_int_int - to_int_int; // expected-error {{overload resolution selected deleted operator '-': deleted (TO<int, int>, -)}}
+  to_int_int[to_int_int]; // expected-error {{overload resolution selected deleted operator '[]': deleted (TO<int, int>, [])}}
+  to_int_int(to_int_int); // expected-error {{call to deleted function call operator in type 'TO<int, int>': deleted (TO<int, int>, ())}} expected-error {{attempt to use a deleted function: deleted (TO<int, int>, ())}}
+  if (to_int_int) {} // expected-error {{attempt to use a deleted function: deleted (TO<int, int>, operator bool)}}
+  static_cast<bool>(to_int_int); // expected-error {{static_cast from 'TO<int, int>' to 'bool' uses deleted function: deleted (TO<int, int>, operator bool)}}
+};

>From 9df9e9f1f337f1aa60a92303d6a974bfe14a3527 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 9 Apr 2024 07:52:51 +0200
Subject: [PATCH 18/22] [Clang] Skip deleted function body on error

---
 clang/include/clang/Parse/Parser.h              |  1 +
 clang/lib/Parse/ParseCXXInlineMethods.cpp       | 16 ++++++++++++++++
 clang/lib/Parse/ParseDecl.cpp                   |  1 +
 clang/lib/Parse/ParseDeclCXX.cpp                |  1 +
 clang/test/Parser/cxx2c-delete-with-message.cpp | 13 +++++++++++++
 5 files changed, 32 insertions(+)

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 3e1ea69586307f..04ca7c28b4ab95 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1601,6 +1601,7 @@ class Parser : public CodeCompletionHandler {
                                      const VirtSpecifiers &VS,
                                      SourceLocation PureSpecLoc);
   StringLiteral *ParseCXXDeletedFunctionMessage();
+  void SkipDeletedFunctionBody();
   void ParseCXXNonStaticMemberInitializer(Decl *VarD);
   void ParseLexedAttributes(ParsingClass &Class);
   void ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D,
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index 5f6cb54feffd80..d3d2bd8af52f36 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -42,6 +42,22 @@ StringLiteral *Parser::ParseCXXDeletedFunctionMessage() {
   return Message;
 }
 
+/// If we've encountered '= delete' in a context where it is ill-formed, such
+/// as in the declaration of a non-function, also skip the ("message") part if
+/// it is present to avoid issuing further diagnostics.
+void Parser::SkipDeletedFunctionBody() {
+  if (!Tok.is(tok::l_paren))
+    return;
+
+  BalancedDelimiterTracker BT{*this, tok::l_paren};
+  BT.consumeOpen();
+
+  // Just skip to the end of the current declaration.
+  SkipUntil(tok::r_paren, tok::comma, StopAtSemi | StopBeforeMatch);
+  if (Tok.is(tok::r_paren))
+    BT.consumeClose();
+}
+
 /// ParseCXXInlineMethodDef - We parsed and verified that the specified
 /// Declarator is a well formed C++ inline method definition. Now lex its body
 /// and store its tokens for parsing after the C++ class is complete.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 0aa14b0510746b..a9233201a9064e 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2676,6 +2676,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
           << 1 /* delete */;
       else
         Diag(ConsumeToken(), diag::err_deleted_non_function);
+      SkipDeletedFunctionBody();
     } else if (Tok.is(tok::kw_default)) {
       if (D.isFunctionDeclarator())
         Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration)
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 861a25dc5103c1..9a4a1b18fb5e01 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -3397,6 +3397,7 @@ ExprResult Parser::ParseCXXMemberInitializer(Decl *D, bool IsFunction,
               << 1 /* delete */;
         else
           Diag(ConsumeToken(), diag::err_deleted_non_function);
+        SkipDeletedFunctionBody();
         return ExprError();
       }
     } else if (Tok.is(tok::kw_default)) {
diff --git a/clang/test/Parser/cxx2c-delete-with-message.cpp b/clang/test/Parser/cxx2c-delete-with-message.cpp
index 7baa1581eb369e..2f36260708adc7 100644
--- a/clang/test/Parser/cxx2c-delete-with-message.cpp
+++ b/clang/test/Parser/cxx2c-delete-with-message.cpp
@@ -33,3 +33,16 @@ void c() = delete(); // expected-error {{expected string literal}}
 void d() = delete(42); // expected-error {{expected string literal}}
 void e() = delete("foo"[0]); // expected-error {{expected ')'}} expected-note {{to match this '('}} // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}}
 void f() = delete("foo"); // pre26-warning {{'= delete' with a message is a C++2c extension}} compat-warning {{'= delete' with a message is incompatible with C++ standards before C++2c}}
+
+namespace CWG2876 {
+using T = void ();
+using U = int;
+
+T a = delete ("hello"); // expected-error {{only functions can have deleted definitions}}
+U b = delete ("hello"), c, d = delete ("hello"); // expected-error 2 {{only functions can have deleted definitions}}
+
+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]'}}
+};
+}

>From b442173b9f52d619c079e878ff89075ba18bf844 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 9 Apr 2024 13:30:16 +0200
Subject: [PATCH 19/22] [Clang] Include deleted message in ODR hash

---
 clang/lib/AST/ODRHash.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index e159a1b00be552..efd7a4abb941b9 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -696,6 +696,9 @@ void ODRHash::AddFunctionDecl(const FunctionDecl *Function,
   AddBoolean(Function->isDeletedAsWritten());
   AddBoolean(Function->isExplicitlyDefaulted());
 
+  StringLiteral *DeletedMessage = Function->getDeletedMessage();
+  AddBoolean(DeletedMessage);
+
   AddDecl(Function);
 
   AddQualType(Function->getReturnType());
@@ -709,6 +712,11 @@ void ODRHash::AddFunctionDecl(const FunctionDecl *Function,
     return;
   }
 
+  if (DeletedMessage) {
+    ID.AddString(DeletedMessage->getBytes());
+    return;
+  }
+
   const bool HasBody = Function->isThisDeclarationADefinition() &&
                        !Function->isDefaulted() && !Function->isDeleted() &&
                        !Function->isLateTemplateParsed();

>From 800b643684942d1e6ff4e3cd6646eb519cd2ccbd Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 10 Apr 2024 10:25:13 +0200
Subject: [PATCH 20/22] [Docs] Update LanguageExtensions.rst

---
 clang/docs/LanguageExtensions.rst | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 7b23e4d1c2f30c..030f0017d7d962 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1493,6 +1493,7 @@ Conditional ``explicit``                     __cpp_conditional_explicit       C+
 ``if consteval``                             __cpp_if_consteval               C++23         C++20
 ``static operator()``                        __cpp_static_call_operator       C++23         C++03
 Attributes on Lambda-Expressions                                              C++23         C++11
+``= delete ("should have a reason");``       __cpp_deleted_function           C++26         C++03
 -------------------------------------------- -------------------------------- ------------- -------------
 Designated initializers (N494)                                                C99           C89
 Array & element qualification (N2607)                                         C23           C89

>From c12f90e8f0e7b3e07937cbe734d48d746e027d4e Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 10 Apr 2024 10:49:53 +0200
Subject: [PATCH 21/22] [Clang] Always add deleted message to ODR hash

---
 clang/lib/AST/ODRHash.cpp | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index efd7a4abb941b9..6f04739cf6693d 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -699,6 +699,9 @@ void ODRHash::AddFunctionDecl(const FunctionDecl *Function,
   StringLiteral *DeletedMessage = Function->getDeletedMessage();
   AddBoolean(DeletedMessage);
 
+  if (DeletedMessage)
+    ID.AddString(DeletedMessage->getBytes());
+
   AddDecl(Function);
 
   AddQualType(Function->getReturnType());
@@ -712,11 +715,6 @@ void ODRHash::AddFunctionDecl(const FunctionDecl *Function,
     return;
   }
 
-  if (DeletedMessage) {
-    ID.AddString(DeletedMessage->getBytes());
-    return;
-  }
-
   const bool HasBody = Function->isThisDeclarationADefinition() &&
                        !Function->isDefaulted() && !Function->isDeleted() &&
                        !Function->isLateTemplateParsed();

>From 8602457b6a8f5b71390e709def356e04d780d945 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 10 Apr 2024 11:58:07 +0200
Subject: [PATCH 22/22] [Clang] Set deleted message in setDeletedAsWritten()

---
 clang/include/clang/AST/Decl.h |  5 +----
 clang/lib/AST/Decl.cpp         | 23 +++++++++++++----------
 clang/lib/Sema/SemaDeclCXX.cpp |  4 +---
 3 files changed, 15 insertions(+), 17 deletions(-)

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 97801565a7dc74..910e366121bbad 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -2496,10 +2496,7 @@ class FunctionDecl : public DeclaratorDecl,
     return FunctionDeclBits.IsDeleted && !isDefaulted();
   }
 
-  void setDeletedAsWritten(bool D = true) { FunctionDeclBits.IsDeleted = D; }
-
-  /// Only valid if isDeletedAsWritten() returns true.
-  void setDeletedMessage(StringLiteral *Message);
+  void setDeletedAsWritten(bool D = true, StringLiteral *Message = nullptr);
 
   /// Determines whether this function is "main", which is the
   /// entry point into an executable program.
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 0a37eac22e3c9b..6c5cd623f32fb6 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3123,6 +3123,19 @@ void FunctionDecl::setDefaultedOrDeletedInfo(
   DefaultedOrDeletedInfo = Info;
 }
 
+void FunctionDecl::setDeletedAsWritten(bool D, StringLiteral *Message) {
+  FunctionDeclBits.IsDeleted = D;
+
+  if (Message) {
+    assert(isDeletedAsWritten() && "Function must be deleted");
+    if (FunctionDeclBits.HasDefaultedOrDeletedInfo)
+      DefaultedOrDeletedInfo->setDeletedMessage(Message);
+    else
+      setDefaultedOrDeletedInfo(DefaultedOrDeletedFunctionInfo::Create(
+          getASTContext(), /*Lookups=*/{}, Message));
+  }
+}
+
 void FunctionDecl::DefaultedOrDeletedFunctionInfo::setDeletedMessage(
     StringLiteral *Message) {
   // We should never get here with the DefaultedOrDeletedInfo populated, but
@@ -3140,16 +3153,6 @@ FunctionDecl::getDefalutedOrDeletedInfo() const {
                                                     : nullptr;
 }
 
-void FunctionDecl::setDeletedMessage(StringLiteral *Message) {
-  assert(Message && "Should not be called with nullptr");
-  assert(isDeletedAsWritten() && "Function must be deleted");
-  if (FunctionDeclBits.HasDefaultedOrDeletedInfo)
-    DefaultedOrDeletedInfo->setDeletedMessage(Message);
-  else
-    setDefaultedOrDeletedInfo(DefaultedOrDeletedFunctionInfo::Create(
-        getASTContext(), /*Lookups=*/{}, Message));
-}
-
 bool FunctionDecl::hasBody(const FunctionDecl *&Definition) const {
   for (const auto *I : redecls()) {
     if (I->doesThisDeclarationHaveABody()) {
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 67e298c40cd517..498bfd4319a97d 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18162,9 +18162,7 @@ void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc,
   // C++11 [dcl.fct.def.delete]p4:
   //  A deleted function is implicitly inline.
   Fn->setImplicitlyInline();
-  Fn->setDeletedAsWritten();
-  if (Message)
-    Fn->setDeletedMessage(Message);
+  Fn->setDeletedAsWritten(true, Message);
 }
 
 void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) {



More information about the cfe-commits mailing list