diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 96691b45d63a3..05c8f765b5569 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 399d5ba796d15..ade8f4e93d5a0 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -129,6 +129,8 @@ C++2c Feature Support - Implemented `P2662R3 Pack Indexing `_. +- Implemented `P2573R2: = delete("should have a reason"); `_ + Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index ed6790acdfc7c..01af50ca694fd 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 { + /// Stashed information about a defaulted/deleted function body. + class DefaultedOrDeletedFunctionInfo final + : llvm::TrailingObjects { friend TrailingObjects; unsigned NumLookups; + bool HasDeletedMessage; + + size_t numTrailingObjects(OverloadToken) const { + return NumLookups; + } public: - static DefaultedFunctionInfo *Create(ASTContext &Context, - ArrayRef Lookups); + static DefaultedOrDeletedFunctionInfo * + Create(ASTContext &Context, ArrayRef Lookups, + StringLiteral *DeletedMessage = nullptr); + /// Get the unqualified lookup results that should be used in this /// defaulted function definition. ArrayRef getUnqualifiedLookups() const { return {getTrailingObjects(), NumLookups}; } + + StringLiteral *getDeletedMessage() const { + return HasDeletedMessage ? *getTrailingObjects() + : 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 858450926455c..2194d268fa86f 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 46a44418a3153..bb9ca2a50cc06 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, DefaultIgnore; +def ext_delete_with_message : ExtWarn< + "'= delete' with a message is a C++2c extension">, InGroup; +def warn_cxx23_delete_with_message : Warning< + "'= delete' with a message is incompatible with C++ standards before C++2c">, + DefaultIgnore, InGroup; + // 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 774d2b53a3825..5ec0218aedfe8 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 c719218731c35..5950dd74cfe83 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 3b252f94dcbee..c6e0332c3176b 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 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 a5e43fc631667..6aaa34c55ce30 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 60e0a3aecf6c8..2b2d5a2663a18 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 Lookups) { - DefaultedFunctionInfo *Info = new (Context.Allocate( - totalSizeToAlloc(Lookups.size()), - std::max(alignof(DefaultedFunctionInfo), alignof(DeclAccessPair)))) - DefaultedFunctionInfo; +FunctionDecl::DefaultedOrDeletedFunctionInfo * +FunctionDecl::DefaultedOrDeletedFunctionInfo::Create( + ASTContext &Context, ArrayRef Lookups, + StringLiteral *DeletedMessage) { + static constexpr size_t Alignment = + std::max({alignof(DefaultedOrDeletedFunctionInfo), + alignof(DeclAccessPair), alignof(StringLiteral *)}); + size_t Size = totalSizeToAlloc( + 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()); + if (DeletedMessage) + *Info->getTrailingObjects() = 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() = 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 c66774dd1df15..93857adb990bf 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 78115200f4f0d..42608476b1c19 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 e159a1b00be55..6f04739cf6693 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 0973e63183bd7..688daa64d6197 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(D) ? " not_selected" : " ineligible"); diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 84069e96f4146..4f44c3b7b89d4 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 d790060c17c04..d054eda279b8c 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(); + 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(FnD)) { DeclAsFunction->setRangeEnd(KWEndLoc); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 951e2210031a1..a990ef398c823 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 477d81cdc2c23..cd4803d51bc1d 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 cc0e41ed221c4..d6f2b9f448cd5 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 9d85568d97b2d..aac43e2cad603 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 17032d1370521..8b3b9d020db57 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 1fe10375222c5..7669171fea56f 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(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 823bf36d88bc9..505d068ac42eb 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -273,8 +273,11 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef 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 d2f18d4aa9b98..25f23a3abf171 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 6b8ce0f633a3e..fb7a80ab02846 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(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 397e7681828f3..675eb6ec05e78 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 c0469a47ab8b6..21a139e434cb7 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(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 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 ff72fc30c718f..e4b6a75c118ba 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(Record.readExpr()) : nullptr; + + unsigned NumLookups = Record.readInt(); SmallVector Lookups; for (unsigned I = 0; I != NumLookups; ++I) { NamedDecl *ND = Record.readDeclAs(); 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 4b417909751fb..276b6257f1d84 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 0000000000000..ea16b97da23e4 --- /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 + 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 0000000000000..11e037e4d7443 --- /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 T b() = delete("bar"); + template 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 1de32498cd345..baaa9d4434e9b 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 8aeb1683961af..bdf649919659d 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 0000000000000..1767a080a7dcd --- /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 0000000000000..22e65d902ecd4 --- /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 + T g() = delete("deleted (2)"); // expected-note {{explicitly deleted}} +}; + +template +struct TS { + T f() = delete("deleted (3)"); // expected-note {{explicitly marked deleted}} + + template + T g(U) = delete("deleted (4)"); // expected-note {{explicitly deleted}} +}; + +void f() = delete("deleted (5)"); // expected-note {{explicitly deleted}} + +template +T g() = delete("deleted (6)"); // expected-note {{explicitly deleted}} + +template +struct TSp { + T f() = delete("deleted (7)"); // expected-note 2 {{explicitly marked deleted}} +}; + +template +struct TSp { + T f() = delete("deleted (8)"); // expected-note {{explicitly marked deleted}} +}; + +template <> +struct TSp { + 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@+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(); // expected-error {{call to deleted member function 'g': deleted (2)}} + TS{}.f(); // expected-error {{attempt to use a deleted function: deleted (3)}} + TS{}.g(0); // expected-error {{call to deleted member function 'g': deleted (4)}} + f(); // expected-error {{call to deleted function 'f': deleted (5)}} + g(); // expected-error {{call to deleted function 'g': deleted (6)}} + TSp{}.f(); // expected-error {{attempt to use a deleted function: deleted (7)}} + TSp{}.f(); // expected-error {{attempt to use a deleted function: deleted (7)}} + TSp{}.f(); // expected-error {{attempt to use a deleted function: deleted (8)}} + TSp{}.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': '"?\}} + u10(); // expected-error {{call to deleted function 'u10': a\n\tb\nc\n}} + u11(); // expected-error {{call to deleted function 'u11': }} + 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 +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 +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 <> +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}} +}; + +void special_members( + C& c1, + C& c2, + TC& tc1, + TC& tc2, + TC& tc_int1, + TC& tc_int2, + TC& tc_int_int1, + TC& 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(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(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{}; // expected-error {{call to deleted constructor of 'TC': deleted (TC, Constructor)}} + TC{tc1}; // expected-error {{call to deleted constructor of 'TC': deleted (TC, Copy Constructor)}} + TC{static_cast&&>(tc1)}; // expected-error {{call to deleted constructor of 'TC': deleted (TC, Move Constructor)}} + tc1 = tc2; // expected-error {{overload resolution selected deleted operator '=': deleted (TC, Copy Assignment)}} + tc1 = static_cast&&>(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{}; // 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{}; // expected-error {{call to deleted constructor of 'TC': deleted (TC, Constructor)}} + TC{tc_int1}; // expected-error {{call to deleted constructor of 'TC': deleted (TC, Copy Constructor)}} + TC{static_cast&&>(tc_int1)}; // expected-error {{call to deleted constructor of 'TC': deleted (TC, Move Constructor)}} + tc_int1 = tc_int2; // expected-error {{overload resolution selected deleted operator '=': deleted (TC, Copy Assignment)}} + tc_int1 = static_cast&&>(tc_int2); // expected-error {{overload resolution selected deleted operator '=': deleted (TC, Move Assignment)}} + tc_int1.~TC(); // expected-error {{attempt to use a deleted function: deleted (TC, Destructor)}} + new TC{}; // expected-error {{call to deleted function 'operator new': deleted (TC, New)}} + delete &tc_int2; // expected-error {{attempt to use a deleted function: deleted (TC, Delete)}} + + TC{}; // expected-error {{call to deleted constructor of 'TC': deleted (TC, Constructor)}} + TC{tc_int_int1}; // expected-error {{call to deleted constructor of 'TC': deleted (TC, Copy Constructor)}} + TC{static_cast&&>(tc_int_int1)}; // expected-error {{call to deleted constructor of 'TC': deleted (TC, Move Constructor)}} + tc_int_int1 = tc_int_int2; // expected-error {{overload resolution selected deleted operator '=': deleted (TC, Copy Assignment)}} + tc_int_int1 = static_cast&&>(tc_int_int2); // expected-error {{overload resolution selected deleted operator '=': deleted (TC, Move Assignment)}} + tc_int_int1.~TC(); // expected-error {{attempt to use a deleted function: deleted (TC, Destructor)}} + new TC{}; // expected-error {{call to deleted function 'operator new': deleted (TC, New)}} + delete &tc_int_int2; // expected-error {{attempt to use a deleted function: deleted (TC, Delete)}} +} + +C conv1() { return 1; } // expected-error {{conversion function from 'int' to 'C' invokes a deleted function: deleted (C, C(int))}} +TC conv2() { return 1; } // expected-error {{conversion function from 'int' to 'TC' invokes a deleted function: deleted (TC, TC(int))}} +TC conv3() { return 1; } // expected-error {{conversion function from 'int' to 'TC' invokes a deleted function: deleted (TC, TC(int))}} +TC conv4() { return 1; } // expected-error {{conversion function from 'int' to 'TC' invokes a deleted function: deleted (TC, 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 +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 +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 <> +struct TO { + int x; + int operator+() = delete("deleted (TO, +)"); // expected-note {{explicitly deleted}} + int* operator->() = delete("deleted (TO, ->)"); // expected-note {{explicitly deleted}} + int operator-(TO) = delete("deleted (TO, -)"); // expected-note {{explicitly deleted}} + int operator[](TO) = delete("deleted (TO, [])"); // expected-note {{explicitly deleted}} + int 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}} +}; + +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(o); // expected-error {{static_cast from 'O' to 'bool' uses deleted function: deleted (O, operator bool)}} + + TO 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': 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(to); // expected-error {{static_cast from 'TO' to 'bool' uses deleted function: deleted (TO, operator bool)}} + + TO to_int; + +to_int; // expected-error {{overload resolution selected deleted operator '+': deleted (TO, +)}} + to_int->x; // expected-error {{overload resolution selected deleted operator '->': deleted (TO, ->)}} + to_int - to_int; // expected-error {{overload resolution selected deleted operator '-': deleted (TO, -)}} + to_int[to_int]; // expected-error {{overload resolution selected deleted operator '[]': deleted (TO, [])}} + to_int(to_int); // expected-error {{call to deleted function call operator in type 'TO': deleted (TO, ())}} expected-error {{attempt to use a deleted function: deleted (TO, ())}} + if (to_int) {} // expected-error {{attempt to use a deleted function: deleted (TO, operator bool)}} + static_cast(to_int); // expected-error {{static_cast from 'TO' to 'bool' uses deleted function: deleted (TO, operator bool)}} + + TO to_int_int; + +to_int_int; // expected-error {{overload resolution selected deleted operator '+': deleted (TO, +)}} + to_int_int->x; // expected-error {{overload resolution selected deleted operator '->': deleted (TO, ->)}} + to_int_int - to_int_int; // expected-error {{overload resolution selected deleted operator '-': deleted (TO, -)}} + to_int_int[to_int_int]; // expected-error {{overload resolution selected deleted operator '[]': deleted (TO, [])}} + to_int_int(to_int_int); // expected-error {{call to deleted function call operator in type 'TO': deleted (TO, ())}} expected-error {{attempt to use a deleted function: deleted (TO, ())}} + if (to_int_int) {} // expected-error {{attempt to use a deleted function: deleted (TO, operator bool)}} + static_cast(to_int_int); // expected-error {{static_cast from 'TO' to 'bool' uses deleted function: deleted (TO, operator bool)}} +}; diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 130148c7420fa..c233171e63c81 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -197,7 +197,7 @@

C++2c implementation status

= delete("should have a reason"); P2573R2 - No + Clang 19 Variadic friends