https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/86526
>From 98af47e8ccc633016c14b91c221f9f8fc620f068 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Mon, 25 Mar 2024 14:46:52 +0100 Subject: [PATCH 01/14] [Clang] Parse `= delete("message")` --- clang/include/clang/AST/Decl.h | 13 ++++++++ clang/include/clang/Parse/Parser.h | 1 + clang/include/clang/Sema/Sema.h | 6 ++-- clang/lib/AST/Decl.cpp | 4 +-- clang/lib/AST/DeclPrinter.cpp | 9 +++-- clang/lib/AST/TextNodeDumper.cpp | 3 ++ clang/lib/Parse/ParseCXXInlineMethods.cpp | 24 +++++++++++++- clang/lib/Parse/Parser.cpp | 4 ++- clang/lib/Sema/SemaDeclCXX.cpp | 10 +++--- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 6 ++++ .../test/Parser/cxx2c-delete-with-message.cpp | 33 +++++++++++++++++++ 11 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 clang/test/Parser/cxx2c-delete-with-message.cpp diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index a5879591f4c659..24f0394577e228 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2013,6 +2013,12 @@ class FunctionDecl : public DeclaratorDecl, DefaultedFunctionInfo *DefaultedInfo; }; + /// Message that indicates why this function was deleted. + /// + /// FIXME: Figure out where to actually put this; maybe in the + /// 'DefaultedInfo' above? + StringLiteral *DeletedMessage; + unsigned ODRHash; /// End part of this FunctionDecl's source range. @@ -2483,6 +2489,10 @@ class FunctionDecl : public DeclaratorDecl, } void setDeletedAsWritten(bool D = true) { FunctionDeclBits.IsDeleted = D; } + void setDeletedWithMessage(StringLiteral* Message) { + FunctionDeclBits.IsDeleted = true; + DeletedMessage = Message; + } /// Determines whether this function is "main", which is the /// entry point into an executable program. @@ -2638,6 +2648,9 @@ class FunctionDecl : public DeclaratorDecl, AC.push_back(TRC); } + /// Get the message that indicates why this function was deleted. + StringLiteral *getDeletedMessage() const { return DeletedMessage; } + void setPreviousDeclaration(FunctionDecl * PrevDecl); FunctionDecl *getCanonicalDecl() override; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 14df75180ef321..559e6416b7dfb3 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1600,6 +1600,7 @@ class Parser : public CodeCompletionHandler { const ParsedTemplateInfo &TemplateInfo, const VirtSpecifiers &VS, SourceLocation PureSpecLoc); + StringLiteral *ParseCXXDeletedFunctionMessage(); void ParseCXXNonStaticMemberInitializer(Decl *VarD); void ParseLexedAttributes(ParsingClass &Class); void ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 64de39acc72176..919f8f6b0a35a2 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4926,10 +4926,12 @@ class Sema final { SourceLocation EqualLoc); void ActOnPureSpecifier(Decl *D, SourceLocation PureSpecLoc); - void SetDeclDeleted(Decl *dcl, SourceLocation DelLoc); + void SetDeclDeleted(Decl *dcl, SourceLocation DelLoc, + StringLiteral *Message = nullptr); void SetDeclDefaulted(Decl *dcl, SourceLocation DefaultLoc); - void SetFunctionBodyKind(Decl *D, SourceLocation Loc, FnBodyKind BodyKind); + void SetFunctionBodyKind(Decl *D, SourceLocation Loc, FnBodyKind BodyKind, + StringLiteral *DeletedMessage = nullptr); void ActOnStartTrailingRequiresClause(Scope *S, Declarator &D); ExprResult ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr); ExprResult ActOnRequiresClause(ExprResult ConstraintExpr); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 95900afdd2c5d8..b5e61f0eceb6e4 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3043,8 +3043,8 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, Expr *TrailingRequiresClause) : DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo, StartLoc), - DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0), - EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) { + DeclContext(DK), redeclarable_base(C), Body(), DeletedMessage(nullptr), + ODRHash(0), EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) { assert(T.isNull() || T->isFunctionType()); FunctionDeclBits.SClass = S; FunctionDeclBits.IsInline = isInlineSpecified; diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index b701581b2474a9..d04cdd5a10e033 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -873,9 +873,14 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { if (D->isPureVirtual()) Out << " = 0"; - else if (D->isDeletedAsWritten()) + else if (D->isDeletedAsWritten()) { Out << " = delete"; - else if (D->isExplicitlyDefaulted()) + if (const auto *M = D->getDeletedMessage()) { + Out << "("; + M->outputString(Out); + Out << ")"; + } + } else if (D->isExplicitlyDefaulted()) Out << " = default"; else if (D->doesThisDeclarationHaveABody()) { if (!Policy.TerseOutput) { diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index b683eb1edd8f13..4a2369330a35e7 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1936,6 +1936,9 @@ void TextNodeDumper::VisitFunctionDecl(const FunctionDecl *D) { if (D->isTrivial()) OS << " trivial"; + if (const auto *M = D->getDeletedMessage()) + AddChild("delete message", [=] { Visit(M); }); + if (D->isIneligibleOrNotSelected()) OS << (isa<CXXDestructorDecl>(D) ? " not_selected" : " ineligible"); diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index d790060c17c049..c551023a1e9dbc 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -20,6 +20,27 @@ using namespace clang; +/// Parse the optional ("message") part of a deleted-function-body. +StringLiteral *Parser::ParseCXXDeletedFunctionMessage() { + if (!Tok.is(tok::l_paren)) return nullptr; + StringLiteral *Message = nullptr; + BalancedDelimiterTracker BT{*this, tok::l_paren}; + BT.consumeOpen(); + + if (isTokenStringLiteral()) { + ExprResult Res = ParseUnevaluatedStringLiteralExpression(); + if (Res.isUsable()) + Message = Res.getAs<StringLiteral>(); + } else { + Diag(Tok.getLocation(), diag::err_expected_string_literal) + << /*Source='in'*/ 0 << "'delete'"; + SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch); + } + + BT.consumeClose(); + return Message; +} + /// ParseCXXInlineMethodDef - We parsed and verified that the specified /// Declarator is a well formed C++ inline method definition. Now lex its body /// and store its tokens for parsing after the C++ class is complete. @@ -70,7 +91,8 @@ NamedDecl *Parser::ParseCXXInlineMethodDef( ? diag::warn_cxx98_compat_defaulted_deleted_function : diag::ext_defaulted_deleted_function) << 1 /* deleted */; - Actions.SetDeclDeleted(FnD, KWLoc); + StringLiteral* Message = ParseCXXDeletedFunctionMessage(); + Actions.SetDeclDeleted(FnD, KWLoc, Message); Delete = true; if (auto *DeclAsFunction = dyn_cast<FunctionDecl>(FnD)) { DeclAsFunction->setRangeEnd(KWEndLoc); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index cc0e41ed221c4f..d6f2b9f448cd52 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1404,6 +1404,7 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, // Parse function body eagerly if it is either '= delete;' or '= default;' as // ActOnStartOfFunctionDef needs to know whether the function is deleted. + StringLiteral *DeletedMessage = nullptr; Sema::FnBodyKind BodyKind = Sema::FnBodyKind::Other; SourceLocation KWLoc; if (TryConsumeToken(tok::equal)) { @@ -1415,6 +1416,7 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, : diag::ext_defaulted_deleted_function) << 1 /* deleted */; BodyKind = Sema::FnBodyKind::Delete; + DeletedMessage = ParseCXXDeletedFunctionMessage(); } else if (TryConsumeToken(tok::kw_default, KWLoc)) { Diag(KWLoc, getLangOpts().CPlusPlus11 ? diag::warn_cxx98_compat_defaulted_deleted_function @@ -1473,7 +1475,7 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, D.getMutableDeclSpec().abort(); if (BodyKind != Sema::FnBodyKind::Other) { - Actions.SetFunctionBodyKind(Res, KWLoc, BodyKind); + Actions.SetFunctionBodyKind(Res, KWLoc, BodyKind, DeletedMessage); Stmt *GeneratedBody = Res ? Res->getBody() : nullptr; Actions.ActOnFinishFunctionBody(Res, GeneratedBody, false); return Res; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index e258a4f7c89415..e6d653aedd061c 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18108,7 +18108,7 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, return ND; } -void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc) { +void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc, StringLiteral *Message) { AdjustDeclIfTemplate(Dcl); FunctionDecl *Fn = dyn_cast_or_null<FunctionDecl>(Dcl); @@ -18157,7 +18157,7 @@ void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc) { // C++11 [dcl.fct.def.delete]p4: // A deleted function is implicitly inline. Fn->setImplicitlyInline(); - Fn->setDeletedAsWritten(); + Fn->setDeletedWithMessage(Message); } void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { @@ -18270,11 +18270,11 @@ void Sema::DiagnoseReturnInConstructorExceptionHandler(CXXTryStmt *TryBlock) { } } -void Sema::SetFunctionBodyKind(Decl *D, SourceLocation Loc, - FnBodyKind BodyKind) { +void Sema::SetFunctionBodyKind(Decl *D, SourceLocation Loc, FnBodyKind BodyKind, + StringLiteral *DeletedMessage) { switch (BodyKind) { case FnBodyKind::Delete: - SetDeclDeleted(D, Loc); + SetDeclDeleted(D, Loc, DeletedMessage); break; case FnBodyKind::Default: SetDeclDefaulted(D, Loc); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index dc972018e7b281..549ffd4b0b6df5 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2202,6 +2202,9 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl( if (D->isLocalExternDecl()) Function->setLocalExternDecl(); + if (D->isDeletedAsWritten()) + Function->setDeletedWithMessage(D->getDeletedMessage()); + DeclContext *LexicalDC = Owner; if (!isFriend && D->isOutOfLine() && !D->isLocalExternDecl()) { assert(D->getDeclContext()->isFileContext()); @@ -2627,6 +2630,9 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( if (QualifierLoc) Method->setQualifierInfo(QualifierLoc); + if (D->isDeletedAsWritten()) + Method->setDeletedWithMessage(D->getDeletedMessage()); + if (TemplateParams) { // Our resulting instantiation is actually a function template, since we // are substituting only the outer template parameters. For example, given diff --git a/clang/test/Parser/cxx2c-delete-with-message.cpp b/clang/test/Parser/cxx2c-delete-with-message.cpp new file mode 100644 index 00000000000000..67a4c944187d7f --- /dev/null +++ b/clang/test/Parser/cxx2c-delete-with-message.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s + +struct S { + void a() = delete; + void b() = delete(; // expected-error {{expected string literal}} expected-error {{expected ')'}} expected-note {{to match this '('}} + void c() = delete(); // expected-error {{expected string literal}} + void d() = delete(42); // expected-error {{expected string literal}} + void e() = delete("foo"[0]); // expected-error {{expected ')'}} expected-note {{to match this '('}} + void f() = delete("foo"); + + S() = delete("foo"); + ~S() = delete("foo"); + S(const S&) = delete("foo"); + S(S&&) = delete("foo"); + S& operator=(const S&) = delete("foo"); + S& operator=(S&&) = delete("foo"); +}; + +struct T { + T() = delete(); // expected-error {{expected string literal}} + ~T() = delete(); // expected-error {{expected string literal}} + T(const T&) = delete(); // expected-error {{expected string literal}} + T(T&&) = delete(); // expected-error {{expected string literal}} + T& operator=(const T&) = delete(); // expected-error {{expected string literal}} + T& operator=(T&&) = delete(); // expected-error {{expected string literal}} +}; + +void a() = delete; +void b() = delete(; // expected-error {{expected string literal}} expected-error {{expected ')'}} expected-note {{to match this '('}} +void c() = delete(); // expected-error {{expected string literal}} +void d() = delete(42); // expected-error {{expected string literal}} +void e() = delete("foo"[0]); // expected-error {{expected ')'}} expected-note {{to match this '('}} +void f() = delete("foo"); >From 5ffac609e2cd0a365632974a72f944ed7f1a3779 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Mon, 25 Mar 2024 15:06:01 +0100 Subject: [PATCH 02/14] [Clang] Tentative serialisation --- clang/lib/Serialization/ASTReaderDecl.cpp | 5 ++++ clang/lib/Serialization/ASTWriterDecl.cpp | 7 ++++++ .../ast-dump-cxx2c-delete-with-message.cpp | 23 +++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 clang/test/AST/ast-dump-cxx2c-delete-with-message.cpp diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index a22f760408c634..37bc61b9a63a34 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1122,6 +1122,11 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) { } } + // FIXME: See ASTWriterDecl::VisitFunctionDecl. + if (FD->isDeletedAsWritten()) + FD->setDeletedWithMessage( + cast_if_present<StringLiteral>(Record.readStmt())); + if (Existing) mergeRedeclarable(FD, Existing, Redecl); else if (auto Kind = FD->getTemplatedKind(); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 86f64bf2a24250..af7dbe916419eb 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -761,6 +761,13 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { } } + // FIXME: Hack: We're out of bits in FunctionDeclBits, so always + // add this even though it's 0 in the vast majority of cases. We + // might really want to consider storing this in the DefaultedFunctionInfo + // instead. + if (D->isDeletedAsWritten()) + Record.AddStmt(D->getDeletedMessage()); + Record.push_back(D->param_size()); for (auto *P : D->parameters()) Record.AddDeclRef(P); diff --git a/clang/test/AST/ast-dump-cxx2c-delete-with-message.cpp b/clang/test/AST/ast-dump-cxx2c-delete-with-message.cpp new file mode 100644 index 00000000000000..ea16b97da23e40 --- /dev/null +++ b/clang/test/AST/ast-dump-cxx2c-delete-with-message.cpp @@ -0,0 +1,23 @@ +// Without serialization: +// RUN: %clang_cc1 -ast-dump %s | FileCheck %s +// +// With serialization: +// RUN: %clang_cc1 -emit-pch -o %t %s +// RUN: %clang_cc1 -x c++ -include-pch %t -ast-dump-all /dev/null | FileCheck %s + +struct S { + // CHECK: CXXMethodDecl {{.*}} a 'void ()' delete + // CHECK-NEXT: delete message: StringLiteral {{.*}} "foo" + void a() = delete("foo"); + + // CHECK: FunctionTemplateDecl {{.*}} b + // CHECK-NEXT: TemplateTypeParmDecl + // CHECK-NEXT: CXXMethodDecl {{.*}} b 'void ()' delete + // CHECK-NEXT: delete message: StringLiteral {{.*}} "bar" + template <typename> + void b() = delete("bar"); +}; + +// CHECK: FunctionDecl {{.*}} c 'void ()' delete +// CHECK-NEXT: delete message: StringLiteral {{.*}} "baz" +void c() = delete("baz"); >From 8dc5f8b4eaca3ea7a4c41044af9e4329478403df Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Mon, 25 Mar 2024 16:55:03 +0100 Subject: [PATCH 03/14] [Clang] Sema and tests --- .../clang/Basic/DiagnosticSemaKinds.td | 7 ++-- clang/include/clang/Sema/Sema.h | 5 +++ clang/lib/Sema/SemaExpr.cpp | 7 ++-- clang/lib/Sema/SemaExprCXX.cpp | 23 ++++++------- clang/lib/Sema/SemaOverload.cpp | 32 ++++++++++++------- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 11 ++----- .../SemaCXX/cxx2c-delete-with-message.cpp | 30 +++++++++++++++++ 7 files changed, 76 insertions(+), 39 deletions(-) create mode 100644 clang/test/SemaCXX/cxx2c-delete-with-message.cpp diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d7ab1635cf12bc..37197b60ddfbb8 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4661,11 +4661,10 @@ def err_ovl_no_viable_member_function_in_call : Error< "no matching member function for call to %0">; def err_ovl_ambiguous_call : Error< "call to %0 is ambiguous">; -def err_ovl_deleted_call : Error<"call to deleted function %0">; +def err_ovl_deleted_call : Error<"call to deleted" + "%select{| member}0 function %1%select{|: %3}2">; def err_ovl_ambiguous_member_call : Error< "call to member function %0 is ambiguous">; -def err_ovl_deleted_member_call : Error< - "call to deleted member function %0">; def note_ovl_too_many_candidates : Note< "remaining %0 candidate%s0 omitted; " "pass -fshow-overloads=all to show them">; @@ -8857,7 +8856,7 @@ def err_nontemporal_builtin_must_be_pointer_intfltptr_or_vector : Error< "address argument to nontemporal builtin must be a pointer to integer, float, " "pointer, or a vector of such types (%0 invalid)">; -def err_deleted_function_use : Error<"attempt to use a deleted function">; +def err_deleted_function_use : Error<"attempt to use a deleted function%select{|: %1}0">; def err_deleted_inherited_ctor_use : Error< "constructor inherited by %0 from base class %1 is implicitly deleted">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 919f8f6b0a35a2..9f6e2639508b2a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8264,6 +8264,11 @@ class Sema final { bool IsFunctionConversion(QualType FromType, QualType ToType, QualType &ResultTy); bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType); + void DiagnoseUseOfDeletedFunction(SourceLocation Loc, SourceRange Range, + DeclarationName Name, + OverloadCandidateSet &CandidateSet, + FunctionDecl *Fn, MultiExprArg Args, + bool IsMember = false); ExprResult InitializeExplicitObjectArgument(Sema &S, Expr *Obj, FunctionDecl *Fun); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 8725b09f8546cf..468b5ed1010457 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -271,8 +271,11 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs, Diag(Loc, diag::err_deleted_inherited_ctor_use) << Ctor->getParent() << Ctor->getInheritedConstructor().getConstructor()->getParent(); - else - Diag(Loc, diag::err_deleted_function_use); + else { + StringLiteral *Msg = FD->getDeletedMessage(); + Diag(Loc, diag::err_deleted_function_use) + << !!Msg << (Msg ? Msg->getString() : StringRef()); + } NoteDeletedFunction(FD); return true; } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index c34a40fa7c81ac..ff1fb146544d40 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -2663,13 +2663,9 @@ static bool resolveAllocationOverload( return true; case OR_Deleted: { - if (Diagnose) { - Candidates.NoteCandidates( - PartialDiagnosticAt(R.getNameLoc(), - S.PDiag(diag::err_ovl_deleted_call) - << R.getLookupName() << Range), - S, OCD_AllCandidates, Args); - } + if (Diagnose) + S.DiagnoseUseOfDeletedFunction(R.getNameLoc(), Range, R.getLookupName(), + Candidates, Best->Function, Args); return true; } } @@ -3323,7 +3319,9 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, // FIXME: DiagnoseUseOfDecl? if (Operator->isDeleted()) { if (Diagnose) { - Diag(StartLoc, diag::err_deleted_function_use); + StringLiteral *Msg = Operator->getDeletedMessage(); + Diag(StartLoc, diag::err_deleted_function_use) + << !!Msg << (Msg ? Msg->getString() : StringRef()); NoteDeletedFunction(Operator); } return true; @@ -3927,14 +3925,11 @@ static bool resolveBuiltinNewDeleteOverload(Sema &S, CallExpr *TheCall, S, OCD_AmbiguousCandidates, Args); return true; - case OR_Deleted: { - Candidates.NoteCandidates( - PartialDiagnosticAt(R.getNameLoc(), S.PDiag(diag::err_ovl_deleted_call) - << R.getLookupName() << Range), - S, OCD_AllCandidates, Args); + case OR_Deleted: + S.DiagnoseUseOfDeletedFunction(R.getNameLoc(), Range, R.getLookupName(), + Candidates, Best->Function, Args); return true; } - } llvm_unreachable("Unreachable, bad result from BestViableFunction"); } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 4e03c3124e39ab..e4350df47c3570 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -14138,15 +14138,13 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, break; case OR_Deleted: { - CandidateSet->NoteCandidates( - PartialDiagnosticAt(Fn->getBeginLoc(), - SemaRef.PDiag(diag::err_ovl_deleted_call) - << ULE->getName() << Fn->getSourceRange()), - SemaRef, OCD_AllCandidates, Args); + FunctionDecl *FDecl = (*Best)->Function; + SemaRef.DiagnoseUseOfDeletedFunction(Fn->getBeginLoc(), + Fn->getSourceRange(), ULE->getName(), + *CandidateSet, FDecl, Args); // We emitted an error for the unavailable/deleted function call but keep // the call in the AST. - FunctionDecl *FDecl = (*Best)->Function; ExprResult Res = SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl); if (Res.isInvalid()) @@ -15588,11 +15586,9 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, *this, OCD_AmbiguousCandidates, Args); break; case OR_Deleted: - CandidateSet.NoteCandidates( - PartialDiagnosticAt(UnresExpr->getMemberLoc(), - PDiag(diag::err_ovl_deleted_member_call) - << DeclName << MemExprE->getSourceRange()), - *this, OCD_AllCandidates, Args); + DiagnoseUseOfDeletedFunction( + UnresExpr->getMemberLoc(), MemExprE->getSourceRange(), DeclName, + CandidateSet, Best->Function, Args, /*IsMember=*/true); break; } // Overload resolution fails, try to recover. @@ -16483,3 +16479,17 @@ bool clang::shouldEnforceArgLimit(bool PartialOverloading, return false; return true; } + +void Sema::DiagnoseUseOfDeletedFunction(SourceLocation Loc, SourceRange Range, + DeclarationName Name, + OverloadCandidateSet &CandidateSet, + FunctionDecl *Fn, MultiExprArg Args, + bool IsMember) { + StringLiteral *Msg = Fn->getDeletedMessage(); + CandidateSet.NoteCandidates( + PartialDiagnosticAt(Loc, PDiag(diag::err_ovl_deleted_call) + << IsMember << Name << !!Msg + << (Msg ? Msg->getString() : StringRef()) + << Range), + *this, OCD_AllCandidates, Args); +} diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 549ffd4b0b6df5..dfc6edc003e641 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2202,9 +2202,6 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl( if (D->isLocalExternDecl()) Function->setLocalExternDecl(); - if (D->isDeletedAsWritten()) - Function->setDeletedWithMessage(D->getDeletedMessage()); - DeclContext *LexicalDC = Owner; if (!isFriend && D->isOutOfLine() && !D->isLocalExternDecl()) { assert(D->getDeclContext()->isFileContext()); @@ -2432,7 +2429,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl( return nullptr; } if (D->isDeleted()) - SemaRef.SetDeclDeleted(Function, D->getLocation()); + SemaRef.SetDeclDeleted(Function, D->getLocation(), D->getDeletedMessage()); NamedDecl *PrincipalDecl = (TemplateParams ? cast<NamedDecl>(FunctionTemplate) : Function); @@ -2630,9 +2627,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( if (QualifierLoc) Method->setQualifierInfo(QualifierLoc); - if (D->isDeletedAsWritten()) - Method->setDeletedWithMessage(D->getDeletedMessage()); - if (TemplateParams) { // Our resulting instantiation is actually a function template, since we // are substituting only the outer template parameters. For example, given @@ -2811,7 +2805,8 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( return nullptr; } if (D->isDeletedAsWritten()) - SemaRef.SetDeclDeleted(Method, Method->getLocation()); + SemaRef.SetDeclDeleted(Method, Method->getLocation(), + D->getDeletedMessage()); // If this is an explicit specialization, mark the implicitly-instantiated // template specialization as being an explicit specialization too. diff --git a/clang/test/SemaCXX/cxx2c-delete-with-message.cpp b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp new file mode 100644 index 00000000000000..d0f62859c547e6 --- /dev/null +++ b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s + +struct S { + void f() = delete("deleted (1)"); // expected-note {{explicitly marked deleted}} + + template <typename T> + T g() = delete("deleted (2)"); // expected-note {{explicitly deleted}} +}; + +template <typename T> +struct TS { + T f() = delete("deleted (3)"); // expected-note {{explicitly marked deleted}} + + template <typename U> + T g(U) = delete("deleted (4)"); // expected-note {{explicitly deleted}} +}; + +void f() = delete("deleted (5)"); // expected-note {{explicitly deleted}} + +template <typename T> +T g() = delete("deleted (6)"); // expected-note {{explicitly deleted}} + +void h() { + S{}.f(); // expected-error {{attempt to use a deleted function: deleted (1)}} + S{}.g<int>(); // expected-error {{call to deleted member function 'g': deleted (2)}} + TS<int>{}.f(); // expected-error {{attempt to use a deleted function: deleted (3)}} + TS<int>{}.g<int>(0); // expected-error {{call to deleted member function 'g': deleted (4)}} + f(); // expected-error {{call to deleted function 'f': deleted (5)}} + g<int>(); // expected-error {{call to deleted function 'g': deleted (6)}} +} >From a018c8814b024ca08f5487be5be9132aa5c99dfe Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Mon, 25 Mar 2024 17:00:14 +0100 Subject: [PATCH 04/14] [Clang] Add ast printer tests --- .../ast-print-cxx2c-delete-with-message.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp diff --git a/clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp b/clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp new file mode 100644 index 00000000000000..edc9544d718d19 --- /dev/null +++ b/clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp @@ -0,0 +1,18 @@ +// Without serialization: +// RUN: %clang_cc1 -ast-print %s | FileCheck %s +// +// With serialization: +// RUN: %clang_cc1 -emit-pch -o %t %s +// RUN: %clang_cc1 -x c++ -include-pch %t -ast-print-all /dev/null | FileCheck %s + +// CHECK: struct S { +struct S { + // CHECK-NEXT: void a() = delete("foo"); + void a() = delete("foo"); + + // CHECK-NEXT: template <typename T> T b() = delete("bar"); + template <typename T> T b() = delete("bar"); +}; + +// CHECK: void c() = delete("baz"); +void c() = delete("baz"); >From b5a50beebc1f54fd106ebeda086b73c6875591a5 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Mon, 25 Mar 2024 17:08:04 +0100 Subject: [PATCH 05/14] [NFC] clang-format --- clang/include/clang/AST/Decl.h | 2 +- clang/lib/Parse/ParseCXXInlineMethods.cpp | 7 ++++--- clang/lib/Sema/SemaDeclCXX.cpp | 3 ++- clang/lib/Sema/SemaExpr.cpp | 2 +- clang/lib/Sema/SemaExprCXX.cpp | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 24f0394577e228..9c445eccdf8992 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2489,7 +2489,7 @@ class FunctionDecl : public DeclaratorDecl, } void setDeletedAsWritten(bool D = true) { FunctionDeclBits.IsDeleted = D; } - void setDeletedWithMessage(StringLiteral* Message) { + void setDeletedWithMessage(StringLiteral *Message) { FunctionDeclBits.IsDeleted = true; DeletedMessage = Message; } diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index c551023a1e9dbc..9e2f4ce562a4ea 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -22,7 +22,8 @@ using namespace clang; /// Parse the optional ("message") part of a deleted-function-body. StringLiteral *Parser::ParseCXXDeletedFunctionMessage() { - if (!Tok.is(tok::l_paren)) return nullptr; + if (!Tok.is(tok::l_paren)) + return nullptr; StringLiteral *Message = nullptr; BalancedDelimiterTracker BT{*this, tok::l_paren}; BT.consumeOpen(); @@ -33,7 +34,7 @@ StringLiteral *Parser::ParseCXXDeletedFunctionMessage() { Message = Res.getAs<StringLiteral>(); } else { Diag(Tok.getLocation(), diag::err_expected_string_literal) - << /*Source='in'*/ 0 << "'delete'"; + << /*Source='in'*/ 0 << "'delete'"; SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch); } @@ -91,7 +92,7 @@ NamedDecl *Parser::ParseCXXInlineMethodDef( ? diag::warn_cxx98_compat_defaulted_deleted_function : diag::ext_defaulted_deleted_function) << 1 /* deleted */; - StringLiteral* Message = ParseCXXDeletedFunctionMessage(); + StringLiteral *Message = ParseCXXDeletedFunctionMessage(); Actions.SetDeclDeleted(FnD, KWLoc, Message); Delete = true; if (auto *DeclAsFunction = dyn_cast<FunctionDecl>(FnD)) { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index e6d653aedd061c..50eff9b02cbef8 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18108,7 +18108,8 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, return ND; } -void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc, StringLiteral *Message) { +void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc, + StringLiteral *Message) { AdjustDeclIfTemplate(Dcl); FunctionDecl *Fn = dyn_cast_or_null<FunctionDecl>(Dcl); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 468b5ed1010457..76fbce8a95da85 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -274,7 +274,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs, else { StringLiteral *Msg = FD->getDeletedMessage(); Diag(Loc, diag::err_deleted_function_use) - << !!Msg << (Msg ? Msg->getString() : StringRef()); + << !!Msg << (Msg ? Msg->getString() : StringRef()); } NoteDeletedFunction(FD); return true; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index ff1fb146544d40..e5b8d1fbb81554 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -3321,7 +3321,7 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, if (Diagnose) { StringLiteral *Msg = Operator->getDeletedMessage(); Diag(StartLoc, diag::err_deleted_function_use) - << !!Msg << (Msg ? Msg->getString() : StringRef()); + << !!Msg << (Msg ? Msg->getString() : StringRef()); NoteDeletedFunction(Operator); } return true; >From e48b80d477e10d4cf3b267a968eaa3cdfaf8c8d3 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Mon, 25 Mar 2024 17:28:26 +0100 Subject: [PATCH 06/14] [Clang] Add release note --- clang/docs/ReleaseNotes.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 459f6a25aeef7b..d838e9b5f287f6 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -109,6 +109,8 @@ C++2c Feature Support - Implemented `P2662R3 Pack Indexing <https://wg21.link/P2662R3>`_. +- Implemented `P2573R2: = delete("should have a reason"); <https://wg21.link/P2573R2>`_ + Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >From 9e45341bf798fc54c0d95bdd7ac585e11e2d11b1 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Mon, 25 Mar 2024 18:34:17 +0100 Subject: [PATCH 07/14] [Clang] Move test to the right directory and fix typo --- .../{SemaCXX => AST}/ast-print-cxx2c-delete-with-message.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename clang/test/{SemaCXX => AST}/ast-print-cxx2c-delete-with-message.cpp (83%) diff --git a/clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp b/clang/test/AST/ast-print-cxx2c-delete-with-message.cpp similarity index 83% rename from clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp rename to clang/test/AST/ast-print-cxx2c-delete-with-message.cpp index edc9544d718d19..11e037e4d7443e 100644 --- a/clang/test/SemaCXX/ast-print-cxx2c-delete-with-message.cpp +++ b/clang/test/AST/ast-print-cxx2c-delete-with-message.cpp @@ -3,7 +3,7 @@ // // With serialization: // RUN: %clang_cc1 -emit-pch -o %t %s -// RUN: %clang_cc1 -x c++ -include-pch %t -ast-print-all /dev/null | FileCheck %s +// RUN: %clang_cc1 -x c++ -include-pch %t -ast-print /dev/null | FileCheck %s // CHECK: struct S { struct S { >From 9fc02a5e6dde90375b8355e28856ee598b3ca1fe Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Tue, 26 Mar 2024 14:31:59 +0100 Subject: [PATCH 08/14] [NFC] Spell out type Co-authored-by: Mariya Podchishchaeva <mariya.podchishcha...@intel.com> --- clang/lib/AST/DeclPrinter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index d04cdd5a10e033..ae79398029b3c6 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -875,7 +875,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { Out << " = 0"; else if (D->isDeletedAsWritten()) { Out << " = delete"; - if (const auto *M = D->getDeletedMessage()) { + if (const StringLiteral *M = D->getDeletedMessage()) { Out << "("; M->outputString(Out); Out << ")"; >From 7664e7d31dda3ce7d6b380a3663733dcc3148621 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Tue, 26 Mar 2024 14:32:09 +0100 Subject: [PATCH 09/14] [NFC] Spell out type Co-authored-by: Mariya Podchishchaeva <mariya.podchishcha...@intel.com> --- clang/lib/AST/TextNodeDumper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 4a2369330a35e7..22c157eb11684d 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1936,7 +1936,7 @@ void TextNodeDumper::VisitFunctionDecl(const FunctionDecl *D) { if (D->isTrivial()) OS << " trivial"; - if (const auto *M = D->getDeletedMessage()) + if (const StringLiteral *M = D->getDeletedMessage()) AddChild("delete message", [=] { Visit(M); }); if (D->isIneligibleOrNotSelected()) >From 41bb3ddcc3ee86241e6e54d1240a008788be8c0e Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Thu, 28 Mar 2024 22:05:23 +0100 Subject: [PATCH 10/14] [Clang] Store message in DefaultFunctionInfo --- clang/include/clang/AST/Decl.h | 57 +++++++++------- clang/include/clang/AST/DeclBase.h | 2 +- clang/lib/AST/ASTImporter.cpp | 12 ++++ clang/lib/AST/Decl.cpp | 66 +++++++++++++------ clang/lib/Sema/SemaDecl.cpp | 5 +- clang/lib/Sema/SemaDeclCXX.cpp | 10 +-- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 10 +-- clang/lib/Serialization/ASTReaderDecl.cpp | 22 ++++--- clang/lib/Serialization/ASTWriterDecl.cpp | 18 ++--- clang/test/PCH/cxx2a-defaulted-comparison.cpp | 2 +- 10 files changed, 132 insertions(+), 72 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 9c445eccdf8992..2fbab6b83a8c51 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1981,21 +1981,35 @@ class FunctionDecl : public DeclaratorDecl, }; - /// Stashed information about a defaulted function definition whose body has - /// not yet been lazily generated. - class DefaultedFunctionInfo final - : llvm::TrailingObjects<DefaultedFunctionInfo, DeclAccessPair> { + /// Stashed information about a defaulted/deleted function body. + class ExtraFunctionInfo final + : llvm::TrailingObjects<ExtraFunctionInfo, DeclAccessPair, + StringLiteral *> { friend TrailingObjects; unsigned NumLookups; + bool HasDeletedMessage; + + size_t numTrailingObjects(OverloadToken<DeclAccessPair>) const { + return NumLookups; + } public: - static DefaultedFunctionInfo *Create(ASTContext &Context, - ArrayRef<DeclAccessPair> Lookups); + static ExtraFunctionInfo *Create(ASTContext &Context, + ArrayRef<DeclAccessPair> Lookups, + StringLiteral *DeletedMessage = nullptr); + /// Get the unqualified lookup results that should be used in this /// defaulted function definition. ArrayRef<DeclAccessPair> getUnqualifiedLookups() const { return {getTrailingObjects<DeclAccessPair>(), NumLookups}; } + + StringLiteral *getDeletedMessage() const { + return HasDeletedMessage ? *getTrailingObjects<StringLiteral *>() + : nullptr; + } + + void setDeletedMessage(StringLiteral *Message); }; private: @@ -2005,20 +2019,14 @@ class FunctionDecl : public DeclaratorDecl, ParmVarDecl **ParamInfo = nullptr; /// The active member of this union is determined by - /// FunctionDeclBits.HasDefaultedFunctionInfo. + /// FunctionDeclBits.HasExtraFunctionInfo. union { /// The body of the function. LazyDeclStmtPtr Body; /// Information about a future defaulted function definition. - DefaultedFunctionInfo *DefaultedInfo; + ExtraFunctionInfo *ExtraInfo; }; - /// Message that indicates why this function was deleted. - /// - /// FIXME: Figure out where to actually put this; maybe in the - /// 'DefaultedInfo' above? - StringLiteral *DeletedMessage; - unsigned ODRHash; /// End part of this FunctionDecl's source range. @@ -2274,18 +2282,18 @@ class FunctionDecl : public DeclaratorDecl, /// Returns whether this specific declaration of the function has a body. bool doesThisDeclarationHaveABody() const { - return (!FunctionDeclBits.HasDefaultedFunctionInfo && Body) || + return (!FunctionDeclBits.HasExtraFunctionInfo && Body) || isLateTemplateParsed(); } void setBody(Stmt *B); void setLazyBody(uint64_t Offset) { - FunctionDeclBits.HasDefaultedFunctionInfo = false; + FunctionDeclBits.HasExtraFunctionInfo = false; Body = LazyDeclStmtPtr(Offset); } - void setDefaultedFunctionInfo(DefaultedFunctionInfo *Info); - DefaultedFunctionInfo *getDefaultedFunctionInfo() const; + void setExtraFunctionInfo(ExtraFunctionInfo *Info); + ExtraFunctionInfo *getExtraFunctionInfo() const; /// Whether this function is variadic. bool isVariadic() const; @@ -2489,10 +2497,9 @@ class FunctionDecl : public DeclaratorDecl, } void setDeletedAsWritten(bool D = true) { FunctionDeclBits.IsDeleted = D; } - void setDeletedWithMessage(StringLiteral *Message) { - FunctionDeclBits.IsDeleted = true; - DeletedMessage = Message; - } + + /// Only valid if isDeletedAsWritten() returns true. + void setDeletedMessage(StringLiteral *Message); /// Determines whether this function is "main", which is the /// entry point into an executable program. @@ -2649,7 +2656,11 @@ class FunctionDecl : public DeclaratorDecl, } /// Get the message that indicates why this function was deleted. - StringLiteral *getDeletedMessage() const { return DeletedMessage; } + StringLiteral *getDeletedMessage() const { + return FunctionDeclBits.HasExtraFunctionInfo + ? ExtraInfo->getDeletedMessage() + : nullptr; + } void setPreviousDeclaration(FunctionDecl * PrevDecl); diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 47ed6d0d1db0df..db007a624c7337 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1740,7 +1740,7 @@ class DeclContext { LLVM_PREFERRED_TYPE(bool) uint64_t IsExplicitlyDefaulted : 1; LLVM_PREFERRED_TYPE(bool) - uint64_t HasDefaultedFunctionInfo : 1; + uint64_t HasExtraFunctionInfo : 1; /// For member functions of complete types, whether this is an ineligible /// special member function or an unselected destructor. See diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 023aaa7f0572b4..2f8eef57cb958a 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3916,6 +3916,14 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { // decl and its redeclarations may be required. } + StringLiteral *Msg = D->getDeletedMessage(); + if (Msg) { + auto Imported = import(Msg); + if (!Imported) + return Imported.takeError(); + Msg = *Imported; + } + ToFunction->setQualifierInfo(ToQualifierLoc); ToFunction->setAccess(D->getAccess()); ToFunction->setLexicalDeclContext(LexicalDC); @@ -3930,6 +3938,10 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { ToFunction->setRangeEnd(ToEndLoc); ToFunction->setDefaultLoc(ToDefaultLoc); + if (Msg) + ToFunction->setExtraFunctionInfo(FunctionDecl::ExtraFunctionInfo::Create( + Importer.getToContext(), {}, Msg)); + // Set the parameters. for (auto *Param : Parameters) { Param->setOwningFunction(ToFunction); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index b5e61f0eceb6e4..ef3d7de62092d5 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3043,8 +3043,8 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, Expr *TrailingRequiresClause) : DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo, StartLoc), - DeclContext(DK), redeclarable_base(C), Body(), DeletedMessage(nullptr), - ODRHash(0), EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) { + DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0), + EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) { assert(T.isNull() || T->isFunctionType()); FunctionDeclBits.SClass = S; FunctionDeclBits.IsInline = isInlineSpecified; @@ -3058,7 +3058,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, FunctionDeclBits.IsTrivialForCall = false; FunctionDeclBits.IsDefaulted = false; FunctionDeclBits.IsExplicitlyDefaulted = false; - FunctionDeclBits.HasDefaultedFunctionInfo = false; + FunctionDeclBits.HasExtraFunctionInfo = false; FunctionDeclBits.IsIneligibleOrNotSelected = false; FunctionDeclBits.HasImplicitReturnZero = false; FunctionDeclBits.IsLateTemplateParsed = false; @@ -3092,30 +3092,58 @@ bool FunctionDecl::isVariadic() const { return false; } -FunctionDecl::DefaultedFunctionInfo * -FunctionDecl::DefaultedFunctionInfo::Create(ASTContext &Context, - ArrayRef<DeclAccessPair> Lookups) { - DefaultedFunctionInfo *Info = new (Context.Allocate( - totalSizeToAlloc<DeclAccessPair>(Lookups.size()), - std::max(alignof(DefaultedFunctionInfo), alignof(DeclAccessPair)))) - DefaultedFunctionInfo; +FunctionDecl::ExtraFunctionInfo * +FunctionDecl::ExtraFunctionInfo::Create(ASTContext &Context, + ArrayRef<DeclAccessPair> Lookups, + StringLiteral *DeletedMessage) { + static constexpr size_t Alignment = + std::max({alignof(ExtraFunctionInfo), alignof(DeclAccessPair), + alignof(StringLiteral *)}); + size_t Size = totalSizeToAlloc<DeclAccessPair, StringLiteral *>( + Lookups.size(), !!DeletedMessage); + + ExtraFunctionInfo *Info = + new (Context.Allocate(Size, Alignment)) ExtraFunctionInfo; Info->NumLookups = Lookups.size(); + Info->HasDeletedMessage = !!DeletedMessage; + std::uninitialized_copy(Lookups.begin(), Lookups.end(), Info->getTrailingObjects<DeclAccessPair>()); + if (DeletedMessage) + *Info->getTrailingObjects<StringLiteral *>() = DeletedMessage; return Info; } -void FunctionDecl::setDefaultedFunctionInfo(DefaultedFunctionInfo *Info) { - assert(!FunctionDeclBits.HasDefaultedFunctionInfo && "already have this"); +void FunctionDecl::setExtraFunctionInfo(ExtraFunctionInfo *Info) { + assert(!FunctionDeclBits.HasExtraFunctionInfo && "already have this"); assert(!Body && "can't replace function body with defaulted function info"); - FunctionDeclBits.HasDefaultedFunctionInfo = true; - DefaultedInfo = Info; + FunctionDeclBits.HasExtraFunctionInfo = true; + ExtraInfo = Info; +} + +void FunctionDecl::ExtraFunctionInfo::setDeletedMessage( + StringLiteral *Message) { + // We should never get here with the ExtraInfo populated, but no space + // allocated for the deleted message, since that would require recreating + // this, but setExtraFunctionInfo() disallows overwriting an already existing + // ExtraFunctionInfo. + assert(HasDeletedMessage && "Explicitly deleting defaulted function?"); + *getTrailingObjects<StringLiteral *>() = Message; } -FunctionDecl::DefaultedFunctionInfo * -FunctionDecl::getDefaultedFunctionInfo() const { - return FunctionDeclBits.HasDefaultedFunctionInfo ? DefaultedInfo : nullptr; +FunctionDecl::ExtraFunctionInfo *FunctionDecl::getExtraFunctionInfo() const { + return FunctionDeclBits.HasExtraFunctionInfo ? ExtraInfo : nullptr; +} + +void FunctionDecl::setDeletedMessage(StringLiteral *Message) { + assert(Message && "Should not be called with nullptr"); + assert(isDeletedAsWritten() && "Function must be deleted"); + if (FunctionDeclBits.HasExtraFunctionInfo) + ExtraInfo->setDeletedMessage(Message); + else + setExtraFunctionInfo( + ExtraFunctionInfo::Create(getASTContext(), {}, Message)); } bool FunctionDecl::hasBody(const FunctionDecl *&Definition) const { @@ -3202,7 +3230,7 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const { if (!hasBody(Definition)) return nullptr; - assert(!Definition->FunctionDeclBits.HasDefaultedFunctionInfo && + assert(!Definition->FunctionDeclBits.HasExtraFunctionInfo && "definition should not have a body"); if (Definition->Body) return Definition->Body.get(getASTContext().getExternalSource()); @@ -3211,7 +3239,7 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const { } void FunctionDecl::setBody(Stmt *B) { - FunctionDeclBits.HasDefaultedFunctionInfo = false; + FunctionDeclBits.HasExtraFunctionInfo = false; Body = LazyDeclStmtPtr(B); if (B) EndRangeLoc = B->getEndLoc(); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 7acce77458a372..a67b282d026668 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16092,7 +16092,10 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // This is meant to pop the context added in ActOnStartOfFunctionDef(). ExitFunctionBodyRAII ExitRAII(*this, isLambdaCallOperator(FD)); if (FD) { - FD->setBody(Body); + // Do not overwrite the potentially present ExtraInfo of a deleted + // function declaration. + if (!FD->isDeletedAsWritten()) + FD->setBody(Body); FD->setWillHaveBody(false); CheckImmediateEscalatingFunctionDefinition(FD, FSI); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 50eff9b02cbef8..bf8de0ea4fd7ab 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -7962,7 +7962,7 @@ class DefaultedComparisonVisitor { DefaultedComparisonVisitor(Sema &S, CXXRecordDecl *RD, FunctionDecl *FD, DefaultedComparisonKind DCK) : S(S), RD(RD), FD(FD), DCK(DCK) { - if (auto *Info = FD->getDefaultedFunctionInfo()) { + if (auto *Info = FD->getExtraFunctionInfo()) { // FIXME: Change CreateOverloadedBinOp to take an ArrayRef instead of an // UnresolvedSet to avoid this copy. Fns.assign(Info->getUnqualifiedLookups().begin(), @@ -8830,8 +8830,8 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD, UnresolvedSet<32> Operators; lookupOperatorsForDefaultedComparison(*this, S, Operators, FD->getOverloadedOperator()); - FD->setDefaultedFunctionInfo(FunctionDecl::DefaultedFunctionInfo::Create( - Context, Operators.pairs())); + FD->setExtraFunctionInfo( + FunctionDecl::ExtraFunctionInfo::Create(Context, Operators.pairs())); } // C++2a [class.compare.default]p1: @@ -18158,7 +18158,9 @@ void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc, // C++11 [dcl.fct.def.delete]p4: // A deleted function is implicitly inline. Fn->setImplicitlyInline(); - Fn->setDeletedWithMessage(Message); + Fn->setDeletedAsWritten(); + if (Message) + Fn->setDeletedMessage(Message); } void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) { diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index dfc6edc003e641..f6a5353aec3ecd 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -4856,7 +4856,7 @@ TemplateDeclInstantiator::InitMethodInstantiation(CXXMethodDecl *New, bool TemplateDeclInstantiator::SubstDefaultedFunction(FunctionDecl *New, FunctionDecl *Tmpl) { // Transfer across any unqualified lookups. - if (auto *DFI = Tmpl->getDefaultedFunctionInfo()) { + if (auto *DFI = Tmpl->getExtraFunctionInfo()) { SmallVector<DeclAccessPair, 32> Lookups; Lookups.reserve(DFI->getUnqualifiedLookups().size()); bool AnyChanged = false; @@ -4871,10 +4871,10 @@ bool TemplateDeclInstantiator::SubstDefaultedFunction(FunctionDecl *New, // It's unlikely that substitution will change any declarations. Don't // store an unnecessary copy in that case. - New->setDefaultedFunctionInfo( - AnyChanged ? FunctionDecl::DefaultedFunctionInfo::Create( - SemaRef.Context, Lookups) - : DFI); + New->setExtraFunctionInfo( + AnyChanged + ? FunctionDecl::ExtraFunctionInfo::Create(SemaRef.Context, Lookups) + : DFI); } SemaRef.SetDeclDefaulted(New, Tmpl->getLocation()); diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 37bc61b9a63a34..aad6747eca6be2 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1109,24 +1109,28 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) { FD->setHasODRHash(true); } - if (FD->isDefaulted()) { - if (unsigned NumLookups = Record.readInt()) { + if (FD->isDefaulted() || FD->isDeletedAsWritten()) { + // If 'Info' is nonzero, we need to read an ExtraFunctionInfo; if, + // additionally, the second bit is also set, we also need to read + // a DeletedMessage for the ExtraFunctionInfo. + if (auto Info = Record.readInt()) { + bool HasMessage = Info & 2; + StringLiteral *DeletedMessage = + HasMessage ? cast<StringLiteral>(Record.readExpr()) : nullptr; + + unsigned NumLookups = Record.readInt(); SmallVector<DeclAccessPair, 8> Lookups; for (unsigned I = 0; I != NumLookups; ++I) { NamedDecl *ND = Record.readDeclAs<NamedDecl>(); AccessSpecifier AS = (AccessSpecifier)Record.readInt(); Lookups.push_back(DeclAccessPair::make(ND, AS)); } - FD->setDefaultedFunctionInfo(FunctionDecl::DefaultedFunctionInfo::Create( - Reader.getContext(), Lookups)); + + FD->setExtraFunctionInfo(FunctionDecl::ExtraFunctionInfo::Create( + Reader.getContext(), Lookups, DeletedMessage)); } } - // FIXME: See ASTWriterDecl::VisitFunctionDecl. - if (FD->isDeletedAsWritten()) - FD->setDeletedWithMessage( - cast_if_present<StringLiteral>(Record.readStmt())); - if (Existing) mergeRedeclarable(FD, Existing, Redecl); else if (auto Kind = FD->getTemplatedKind(); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index af7dbe916419eb..2dd2f1f14a8b6c 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -749,8 +749,15 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { if (!ShouldSkipCheckingODR) Record.push_back(D->getODRHash()); - if (D->isDefaulted()) { - if (auto *FDI = D->getDefaultedFunctionInfo()) { + if (D->isDefaulted() || D->isDeletedAsWritten()) { + if (auto *FDI = D->getExtraFunctionInfo()) { + // Store both that there is an ExtraFunctionInfo and whether it contains + // a DeletedMessage. + StringLiteral *DeletedMessage = FDI->getDeletedMessage(); + Record.push_back(1 | (DeletedMessage ? 2 : 0)); + if (DeletedMessage) + Record.AddStmt(DeletedMessage); + Record.push_back(FDI->getUnqualifiedLookups().size()); for (DeclAccessPair P : FDI->getUnqualifiedLookups()) { Record.AddDeclRef(P.getDecl()); @@ -761,13 +768,6 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { } } - // FIXME: Hack: We're out of bits in FunctionDeclBits, so always - // add this even though it's 0 in the vast majority of cases. We - // might really want to consider storing this in the DefaultedFunctionInfo - // instead. - if (D->isDeletedAsWritten()) - Record.AddStmt(D->getDeletedMessage()); - Record.push_back(D->param_size()); for (auto *P : D->parameters()) Record.AddDeclRef(P); diff --git a/clang/test/PCH/cxx2a-defaulted-comparison.cpp b/clang/test/PCH/cxx2a-defaulted-comparison.cpp index 8aeb1683961af3..cb1e8ef0d7c900 100644 --- a/clang/test/PCH/cxx2a-defaulted-comparison.cpp +++ b/clang/test/PCH/cxx2a-defaulted-comparison.cpp @@ -22,7 +22,7 @@ namespace std { constexpr strong_ordering strong_ordering::less = {-1}; } -// Ensure that we can round-trip DefaultedFunctionInfo through an AST file. +// Ensure that we can round-trip ExtraFunctionInfo through an AST file. namespace LookupContext { struct A {}; >From b1c930c2f2d87e73ddd1ebc378d5c69c9b50e789 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Thu, 28 Mar 2024 23:35:59 +0100 Subject: [PATCH 11/14] [Clang] Add some unicode tests --- .../SemaCXX/cxx2c-delete-with-message.cpp | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/clang/test/SemaCXX/cxx2c-delete-with-message.cpp b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp index d0f62859c547e6..7183a9513c31ff 100644 --- a/clang/test/SemaCXX/cxx2c-delete-with-message.cpp +++ b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp @@ -20,6 +20,39 @@ void f() = delete("deleted (5)"); // expected-note {{explicitly deleted}} template <typename T> T g() = delete("deleted (6)"); // expected-note {{explicitly deleted}} +void u1() = delete(L"\xFFFFFFFF"); // expected-error {{an unevaluated string literal cannot have an encoding prefix}} \ + // expected-error {{invalid escape sequence '\xFFFFFFFF' in an unevaluated string literal}} +void u2() = delete(u"\U000317FF"); // expected-error {{an unevaluated string literal cannot have an encoding prefix}} + +void u3() = delete("Ω"); // expected-note {{explicitly deleted}} +void u4() = delete("\u1234"); // expected-note {{explicitly deleted}} + +void u5() = delete("\x1ff" // expected-error {{hex escape sequence out of range}} \ + // expected-error {{invalid escape sequence '\x1ff' in an unevaluated string literal}} + "0\x123" // expected-error {{invalid escape sequence '\x123' in an unevaluated string literal}} + "fx\xfffff" // expected-error {{invalid escape sequence '\xfffff' in an unevaluated string literal}} + "goop"); + +void u6() = delete("\'\"\?\\\a\b\f\n\r\t\v"); // expected-note {{explicitly deleted}} +void u7() = delete("\xFF"); // expected-error {{invalid escape sequence '\xFF' in an unevaluated string literal}} +void u8() = delete("\123"); // expected-error {{invalid escape sequence '\123' in an unevaluated string literal}} +void u9() = delete("\pOh no, a Pascal string!"); // expected-warning {{unknown escape sequence '\p'}} \ + // expected-error {{invalid escape sequence '\p' in an unevaluated string literal}} +// expected-note@+1 {{explicitly deleted}} +void u10() = delete(R"(a +\tb +c +)"); + +void u11() = delete("\u0080\u0081\u0082\u0083\u0099\u009A\u009B\u009C\u009D\u009E\u009F"); // expected-note {{explicitly deleted}} + + +//! Contains RTL/LTR marks +void u12() = delete("\u200Eabc\u200Fdef\u200Fgh"); // expected-note {{explicitly deleted}} + +//! Contains ZWJ/regional indicators +void u13() = delete("🏳️🌈 🏴 🇪🇺"); // expected-note {{explicitly deleted}} + void h() { S{}.f(); // expected-error {{attempt to use a deleted function: deleted (1)}} S{}.g<int>(); // expected-error {{call to deleted member function 'g': deleted (2)}} @@ -27,4 +60,11 @@ void h() { TS<int>{}.g<int>(0); // expected-error {{call to deleted member function 'g': deleted (4)}} f(); // expected-error {{call to deleted function 'f': deleted (5)}} g<int>(); // expected-error {{call to deleted function 'g': deleted (6)}} + u3(); // expected-error {{call to deleted function 'u3': Ω}} + u4(); // expected-error {{call to deleted function 'u4': ሴ}} + u6(); // expected-error {{call to deleted function 'u6': '"?\<U+0007><U+0008>}} + u10(); // expected-error {{call to deleted function 'u10': a\n\tb\nc\n}} + u11(); // expected-error {{call to deleted function 'u11': <U+0080><U+0081><U+0082><U+0083><U+0099><U+009A><U+009B><U+009C><U+009D><U+009E><U+009F>}} + u12(); // expected-error {{call to deleted function 'u12': abcdefgh}} + u13(); // expected-error {{call to deleted function 'u13': 🏳️🌈 🏴 🇪🇺}} } >From 29cdff9e5b19643210ea9ec23a6036f3c59da092 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Thu, 28 Mar 2024 23:45:23 +0100 Subject: [PATCH 12/14] [NFC] Update delete syntax in comment --- clang/include/clang/Sema/Sema.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 9f6e2639508b2a..c2964a89097e91 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3207,7 +3207,11 @@ class Sema final { Other, /// = default ; Default, + /// deleted-function-body + /// + /// deleted-function-body: /// = delete ; + /// = delete ( unevaluated-string ) ; Delete }; >From 7807c717fa017b68a8dcd2cb77bbeac323e2ed12 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Thu, 28 Mar 2024 23:58:39 +0100 Subject: [PATCH 13/14] [Clang] Add feature test macro --- clang/lib/Frontend/InitPreprocessor.cpp | 3 +++ clang/test/Lexer/cxx-features.cpp | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 48ad92063bd461..be60e9ce7270f1 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -750,6 +750,9 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, Builder.defineMacro("__cpp_named_character_escapes", "202207L"); Builder.defineMacro("__cpp_placeholder_variables", "202306L"); + // C++26 features supported in earlier language modes. + Builder.defineMacro("__cpp_deleted_function", "202403L"); + if (LangOpts.Char8) Builder.defineMacro("__cpp_char8_t", "202207L"); Builder.defineMacro("__cpp_impl_destroying_delete", "201806L"); diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp index 9496746c6fd663..a0e60c592401e9 100644 --- a/clang/test/Lexer/cxx-features.cpp +++ b/clang/test/Lexer/cxx-features.cpp @@ -34,6 +34,10 @@ // --- C++26 features --- +#if check(deleted_function, 202403, 202403, 202403, 202403, 202403, 202403, 202403) +#error "wrong value for __cpp_deleted_function" +#endif + #if check(placeholder_variables, 202306, 202306, 202306, 202306, 202306, 202306, 202306) #error "wrong value for __cpp_placeholder_variables" #endif >From 40a23307e6404fd0603a2cf72b0b6df5db5b9598 Mon Sep 17 00:00:00 2001 From: Sirraide <aeternalm...@gmail.com> Date: Fri, 29 Mar 2024 00:05:17 +0100 Subject: [PATCH 14/14] [Clang] Update cxx_status --- clang/www/cxx_status.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index c1d95dadbb27e2..edd0b4fc6c2502 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -197,7 +197,7 @@ <h2 id="cxx26">C++2c implementation status</h2> <tr> <td><tt>= delete("should have a reason");</tt></td> <td><a href="https://wg21.link/P2573R2">P2573R2</a></td> - <td class="none" align="center">No</td> + <td class="unreleased" align="center">Clang 19</td> </tr> <tr> <td>Variadic friends</td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits