[clang] ef164ce - [Clang] [C++26] Implement P2573R2: `= delete("should have a reason");` (#86526)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Apr 14 03:30:04 PDT 2024
Author: Sirraide
Date: 2024-04-14T12:30:01+02:00
New Revision: ef164cee90477e294ff692209b4cf97a0e1958ed
URL: https://github.com/llvm/llvm-project/commit/ef164cee90477e294ff692209b4cf97a0e1958ed
DIFF: https://github.com/llvm/llvm-project/commit/ef164cee90477e294ff692209b4cf97a0e1958ed.diff
LOG: [Clang] [C++26] Implement P2573R2: `= delete("should have a reason");` (#86526)
This implements support for the `= delete("message")` syntax that was
only just added to C++26
([P2573R2](https://isocpp.org/files/papers/P2573R2.html#proposal-scope)).
Added:
clang/test/AST/ast-dump-cxx2c-delete-with-message.cpp
clang/test/AST/ast-print-cxx2c-delete-with-message.cpp
clang/test/Parser/cxx2c-delete-with-message.cpp
clang/test/SemaCXX/cxx2c-delete-with-message.cpp
Modified:
clang/docs/LanguageExtensions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/Decl.h
clang/include/clang/AST/DeclBase.h
clang/include/clang/Basic/DiagnosticParseKinds.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Parse/Parser.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/Decl.cpp
clang/lib/AST/DeclPrinter.cpp
clang/lib/AST/JSONNodeDumper.cpp
clang/lib/AST/ODRHash.cpp
clang/lib/AST/TextNodeDumper.cpp
clang/lib/Frontend/InitPreprocessor.cpp
clang/lib/Parse/ParseCXXInlineMethods.cpp
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseDeclCXX.cpp
clang/lib/Parse/Parser.cpp
clang/lib/Sema/SemaCast.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/lib/Sema/SemaInit.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
clang/test/Lexer/cxx-features.cpp
clang/test/PCH/cxx2a-defaulted-comparison.cpp
clang/www/cxx_status.html
Removed:
################################################################################
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 96691b45d63a37..05c8f765b55695 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
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 399d5ba796d15c..ade8f4e93d5a0c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -129,6 +129,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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index ed6790acdfc7cc..01af50ca694fdd 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1993,21 +1993,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 DefaultedOrDeletedFunctionInfo final
+ : llvm::TrailingObjects<DefaultedOrDeletedFunctionInfo, 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 DefaultedOrDeletedFunctionInfo *
+ 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:
@@ -2017,12 +2031,12 @@ class FunctionDecl : public DeclaratorDecl,
ParmVarDecl **ParamInfo = nullptr;
/// The active member of this union is determined by
- /// FunctionDeclBits.HasDefaultedFunctionInfo.
+ /// FunctionDeclBits.HasDefaultedOrDeletedInfo.
union {
/// The body of the function.
LazyDeclStmtPtr Body;
/// Information about a future defaulted function definition.
- DefaultedFunctionInfo *DefaultedInfo;
+ DefaultedOrDeletedFunctionInfo *DefaultedOrDeletedInfo;
};
unsigned ODRHash;
@@ -2280,18 +2294,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.HasDefaultedOrDeletedInfo && Body) ||
isLateTemplateParsed();
}
void setBody(Stmt *B);
void setLazyBody(uint64_t Offset) {
- FunctionDeclBits.HasDefaultedFunctionInfo = false;
+ FunctionDeclBits.HasDefaultedOrDeletedInfo = false;
Body = LazyDeclStmtPtr(Offset);
}
- void setDefaultedFunctionInfo(DefaultedFunctionInfo *Info);
- DefaultedFunctionInfo *getDefaultedFunctionInfo() const;
+ void setDefaultedOrDeletedInfo(DefaultedOrDeletedFunctionInfo *Info);
+ DefaultedOrDeletedFunctionInfo *getDefalutedOrDeletedInfo() const;
/// Whether this function is variadic.
bool isVariadic() const;
@@ -2494,7 +2508,7 @@ class FunctionDecl : public DeclaratorDecl,
return FunctionDeclBits.IsDeleted && !isDefaulted();
}
- void setDeletedAsWritten(bool D = true) { FunctionDeclBits.IsDeleted = D; }
+ void setDeletedAsWritten(bool D = true, StringLiteral *Message = nullptr);
/// Determines whether this function is "main", which is the
/// entry point into an executable program.
@@ -2650,6 +2664,13 @@ class FunctionDecl : public DeclaratorDecl,
AC.push_back(TRC);
}
+ /// Get the message that indicates why this function was deleted.
+ StringLiteral *getDeletedMessage() const {
+ return FunctionDeclBits.HasDefaultedOrDeletedInfo
+ ? DefaultedOrDeletedInfo->getDeletedMessage()
+ : nullptr;
+ }
+
void setPreviousDeclaration(FunctionDecl * PrevDecl);
FunctionDecl *getCanonicalDecl() override;
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 858450926455c6..2194d268fa86f0 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -1739,7 +1739,7 @@ class DeclContext {
LLVM_PREFERRED_TYPE(bool)
uint64_t IsExplicitlyDefaulted : 1;
LLVM_PREFERRED_TYPE(bool)
- uint64_t HasDefaultedFunctionInfo : 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/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/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 774d2b53a38252..5ec0218aedfe86 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4683,11 +4683,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">;
@@ -4915,12 +4914,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|"
@@ -4946,7 +4945,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 "
@@ -4983,7 +4982,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">;
@@ -8284,7 +8283,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">;
@@ -8884,7 +8883,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/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index c719218731c35b..5950dd74cfe83c 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1601,6 +1601,8 @@ class Parser : public CodeCompletionHandler {
const ParsedTemplateInfo &TemplateInfo,
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/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3b252f94dcbee7..c6e0332c3176b3 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3045,14 +3045,18 @@ class Sema final : public SemaBase {
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
Other,
/// = default ;
Default,
+ /// deleted-function-body
+ ///
+ /// deleted-function-body:
/// = delete ;
+ /// = delete ( unevaluated-string ) ;
Delete
};
@@ -4751,10 +4755,12 @@ class Sema final : public SemaBase {
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);
@@ -8093,6 +8099,11 @@ class Sema final : public SemaBase {
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/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index a5e43fc6316675..6aaa34c55ce307 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -3947,6 +3947,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);
@@ -3961,6 +3969,11 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
ToFunction->setRangeEnd(ToEndLoc);
ToFunction->setDefaultLoc(ToDefaultLoc);
+ if (Msg)
+ ToFunction->setDefaultedOrDeletedInfo(
+ FunctionDecl::DefaultedOrDeletedFunctionInfo::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 60e0a3aecf6c8e..2b2d5a2663a18b 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.HasDefaultedFunctionInfo = false;
+ FunctionDeclBits.HasDefaultedOrDeletedInfo = false;
FunctionDeclBits.IsIneligibleOrNotSelected = false;
FunctionDeclBits.HasImplicitReturnZero = false;
FunctionDeclBits.IsLateTemplateParsed = false;
@@ -3092,30 +3092,65 @@ 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::DefaultedOrDeletedFunctionInfo *
+FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
+ ASTContext &Context, ArrayRef<DeclAccessPair> Lookups,
+ StringLiteral *DeletedMessage) {
+ static constexpr size_t Alignment =
+ std::max({alignof(DefaultedOrDeletedFunctionInfo),
+ alignof(DeclAccessPair), alignof(StringLiteral *)});
+ size_t Size = totalSizeToAlloc<DeclAccessPair, StringLiteral *>(
+ Lookups.size(), DeletedMessage != nullptr);
+
+ DefaultedOrDeletedFunctionInfo *Info =
+ new (Context.Allocate(Size, Alignment)) DefaultedOrDeletedFunctionInfo;
Info->NumLookups = Lookups.size();
+ Info->HasDeletedMessage = DeletedMessage != nullptr;
+
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::setDefaultedOrDeletedInfo(
+ DefaultedOrDeletedFunctionInfo *Info) {
+ assert(!FunctionDeclBits.HasDefaultedOrDeletedInfo && "already have this");
assert(!Body && "can't replace function body with defaulted function info");
- FunctionDeclBits.HasDefaultedFunctionInfo = true;
- DefaultedInfo = Info;
+ FunctionDeclBits.HasDefaultedOrDeletedInfo = true;
+ 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
+ // 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::DefaultedFunctionInfo *
-FunctionDecl::getDefaultedFunctionInfo() const {
- return FunctionDeclBits.HasDefaultedFunctionInfo ? DefaultedInfo : nullptr;
+FunctionDecl::DefaultedOrDeletedFunctionInfo *
+FunctionDecl::getDefalutedOrDeletedInfo() const {
+ return FunctionDeclBits.HasDefaultedOrDeletedInfo ? DefaultedOrDeletedInfo
+ : nullptr;
}
bool FunctionDecl::hasBody(const FunctionDecl *&Definition) const {
@@ -3202,7 +3237,7 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const {
if (!hasBody(Definition))
return nullptr;
- assert(!Definition->FunctionDeclBits.HasDefaultedFunctionInfo &&
+ assert(!Definition->FunctionDeclBits.HasDefaultedOrDeletedInfo &&
"definition should not have a body");
if (Definition->Body)
return Definition->Body.get(getASTContext().getExternalSource());
@@ -3211,7 +3246,7 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const {
}
void FunctionDecl::setBody(Stmt *B) {
- FunctionDeclBits.HasDefaultedFunctionInfo = false;
+ FunctionDeclBits.HasDefaultedOrDeletedInfo = false;
Body = LazyDeclStmtPtr(B);
if (B)
EndRangeLoc = B->getEndLoc();
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index c66774dd1df151..93857adb990bf2 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -822,9 +822,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 StringLiteral *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/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index 78115200f4f0d9..42608476b1c195 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -975,6 +975,9 @@ void JSONNodeDumper::VisitFunctionDecl(const FunctionDecl *FD) {
if (FD->isDefaulted())
JOS.attribute("explicitlyDefaulted",
FD->isDeleted() ? "deleted" : "default");
+
+ if (StringLiteral *Msg = FD->getDeletedMessage())
+ JOS.attribute("deletedMessage", Msg->getString());
}
void JSONNodeDumper::VisitEnumDecl(const EnumDecl *ED) {
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index e159a1b00be552..6f04739cf6693d 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -696,6 +696,12 @@ void ODRHash::AddFunctionDecl(const FunctionDecl *Function,
AddBoolean(Function->isDeletedAsWritten());
AddBoolean(Function->isExplicitlyDefaulted());
+ StringLiteral *DeletedMessage = Function->getDeletedMessage();
+ AddBoolean(DeletedMessage);
+
+ if (DeletedMessage)
+ ID.AddString(DeletedMessage->getBytes());
+
AddDecl(Function);
AddQualType(Function->getReturnType());
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 0973e63183bd7c..688daa64d61974 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1961,6 +1961,9 @@ void TextNodeDumper::VisitFunctionDecl(const FunctionDecl *D) {
if (D->isTrivial())
OS << " trivial";
+ if (const StringLiteral *M = D->getDeletedMessage())
+ AddChild("delete message", [=] { Visit(M); });
+
if (D->isIneligibleOrNotSelected())
OS << (isa<CXXDestructorDecl>(D) ? " not_selected" : " ineligible");
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 84069e96f41464..4f44c3b7b89d4d 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -747,6 +747,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/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index d790060c17c049..d054eda279b8c8 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -20,6 +20,49 @@
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>();
+ Diag(Message->getBeginLoc(), getLangOpts().CPlusPlus26
+ ? diag::warn_cxx23_delete_with_message
+ : diag::ext_delete_with_message)
+ << Message->getSourceRange();
+ }
+ } else {
+ Diag(Tok.getLocation(), diag::err_expected_string_literal)
+ << /*Source='in'*/ 0 << "'delete'";
+ SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch);
+ }
+
+ BT.consumeClose();
+ 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.
@@ -70,7 +113,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/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 951e2210031a14..a990ef398c8230 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2678,6 +2678,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 477d81cdc2c230..cd4803d51bc1de 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/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/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index 9d85568d97b2d2..aac43e2cad603f 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 != nullptr)
+ << (Msg ? Msg->getString() : StringRef())
+ << range << src->getSourceRange()),
+ S, OCD_ViableCandidates, src);
+ return true;
+ }
}
candidates.NoteCandidates(
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 17032d1370521a..8b3b9d020db572 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16083,7 +16083,17 @@ 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);
+ // 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);
CheckImmediateEscalatingFunctionDefinition(FD, FSI);
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 1fe10375222c53..7669171fea56ff 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7982,7 +7982,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->getDefalutedOrDeletedInfo()) {
// FIXME: Change CreateOverloadedBinOp to take an ArrayRef instead of an
// UnresolvedSet to avoid this copy.
Fns.assign(Info->getUnqualifiedLookups().begin(),
@@ -8850,8 +8850,9 @@ 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->setDefaultedOrDeletedInfo(
+ FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
+ Context, Operators.pairs()));
}
// C++2a [class.compare.default]p1:
@@ -18159,7 +18160,8 @@ 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);
@@ -18208,7 +18210,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->setDeletedAsWritten(true, Message);
}
void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) {
@@ -18321,11 +18323,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/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 823bf36d88bc95..505d068ac42ebe 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -273,8 +273,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 != nullptr) << (Msg ? Msg->getString() : StringRef());
+ }
NoteDeletedFunction(FD);
return true;
}
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index d2f18d4aa9b980..25f23a3abf1718 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -2716,13 +2716,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;
}
}
@@ -3376,7 +3372,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 != nullptr) << (Msg ? Msg->getString() : StringRef());
NoteDeletedFunction(Operator);
}
return true;
@@ -3980,14 +3978,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/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 6b8ce0f633a3ea..fb7a80ab02846c 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -9763,12 +9763,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 != nullptr) << (Msg ? Msg->getString() : StringRef())
+ << Args[0]->getSourceRange();
if (Ovl == OR_Deleted) {
S.NoteDeletedFunction(Best->Function);
} else {
@@ -10027,9 +10030,12 @@ bool InitializationSequence::Diagnose(Sema &S,
<< llvm::to_underlying(
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 != nullptr)
+ << (Msg ? Msg->getString() : StringRef()) << ArgsRange;
+ }
S.NoteDeletedFunction(Best->Function);
break;
@@ -11061,6 +11067,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 397e7681828f39..675eb6ec05e78e 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -14171,15 +14171,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())
@@ -14531,20 +14529,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 != nullptr)
+ << (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
@@ -15061,7 +15063,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);
@@ -15080,16 +15082,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())
- << Args[0]->getSourceRange()
- << Args[1]->getSourceRange()),
+ OpLoc,
+ PDiag(diag::err_ovl_deleted_oper)
+ << getOperatorSpelling(Best->Function->getDeclName()
+ .getCXXOverloadedOperator())
+ << (Msg != nullptr) << (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.
@@ -15401,14 +15407,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 != nullptr)
+ << (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);
@@ -15622,11 +15632,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.
@@ -15890,15 +15898,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 != nullptr)
+ << (Msg ? Msg->getString() : StringRef())
<< Object.get()->getSourceRange()),
*this, OCD_AllCandidates, Args);
break;
}
+ }
if (Best == CandidateSet.end())
return true;
@@ -16097,13 +16111,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 != nullptr)
+ << (Msg ? Msg->getString() : StringRef())
+ << Base->getSourceRange()),
*this, OCD_AllCandidates, Base);
return ExprError();
}
+ }
CheckMemberOperatorAccess(OpLoc, Base, nullptr, Best->FoundDecl);
@@ -16517,3 +16535,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 != nullptr)
+ << (Msg ? Msg->getString() : StringRef())
+ << Range),
+ *this, OCD_AllCandidates, Args);
+}
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index c0469a47ab8b62..21a139e434cb73 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2439,7 +2439,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);
@@ -2815,7 +2815,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.
@@ -4867,7 +4868,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->getDefalutedOrDeletedInfo()) {
SmallVector<DeclAccessPair, 32> Lookups;
Lookups.reserve(DFI->getUnqualifiedLookups().size());
bool AnyChanged = false;
@@ -4882,8 +4883,8 @@ 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(
+ New->setDefaultedOrDeletedInfo(
+ AnyChanged ? FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
SemaRef.Context, Lookups)
: DFI);
}
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index ff72fc30c718f8..e4b6a75c118ba3 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1103,16 +1103,26 @@ 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 DefaultedOrDeletedInfo; if,
+ // additionally, the second bit is also set, we also need to read
+ // a DeletedMessage for the DefaultedOrDeletedInfo.
+ 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->setDefaultedOrDeletedInfo(
+ FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
+ Reader.getContext(), Lookups, DeletedMessage));
}
}
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 4b417909751fb1..276b6257f1d841 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->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)
+ Record.AddStmt(DeletedMessage);
+
Record.push_back(FDI->getUnqualifiedLookups().size());
for (DeclAccessPair P : FDI->getUnqualifiedLookups()) {
Record.AddDeclRef(P.getDecl());
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");
diff --git a/clang/test/AST/ast-print-cxx2c-delete-with-message.cpp b/clang/test/AST/ast-print-cxx2c-delete-with-message.cpp
new file mode 100644
index 00000000000000..11e037e4d7443e
--- /dev/null
+++ b/clang/test/AST/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 /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");
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index 1de32498cd345b..baaa9d4434e9b7 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
diff --git a/clang/test/PCH/cxx2a-defaulted-comparison.cpp b/clang/test/PCH/cxx2a-defaulted-comparison.cpp
index 8aeb1683961af3..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 DefaultedFunctionInfo through an AST file.
+// Ensure that we can round-trip DefaultedOrDeletedInfo through an AST file.
namespace LookupContext {
struct A {};
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..1767a080a7dcd8
--- /dev/null
+++ b/clang/test/Parser/cxx2c-delete-with-message.cpp
@@ -0,0 +1,51 @@
+// 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 {
+ 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 '('}} // 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"); // 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 {
+ 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 '('}} // 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}}
+
+constexpr const char *getMsg() { return "this is a message"; }
+void func() = delete(getMsg()); // expected-error {{expected string literal}}
+
+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]'}}
+};
+}
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..22e65d902ecd4a
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
@@ -0,0 +1,273 @@
+// 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}}
+
+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}}
+
+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)}}
+ 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)}}
+ 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>}}
+ 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': abcdefgh}}
+ 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)}}
+};
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 130148c7420fa1..c233171e63c811 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>
More information about the cfe-commits
mailing list