Skip to content

Commit ef164ce

Browse files
authored
[Clang] [C++26] Implement P2573R2: = delete("should have a reason"); (llvm#86526)
This implements support for the `= delete("message")` syntax that was only just added to C++26 ([P2573R2](https://isocpp.org/files/papers/P2573R2.html#proposal-scope)).
1 parent ed06b84 commit ef164ce

36 files changed

+728
-120
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,7 @@ Conditional ``explicit`` __cpp_conditional_explicit C+
14931493
``if consteval`` __cpp_if_consteval C++23 C++20
14941494
``static operator()`` __cpp_static_call_operator C++23 C++03
14951495
Attributes on Lambda-Expressions C++23 C++11
1496+
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
14961497
-------------------------------------------- -------------------------------- ------------- -------------
14971498
Designated initializers (N494) C99 C89
14981499
Array & element qualification (N2607) C23 C89

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ C++2c Feature Support
129129

130130
- Implemented `P2662R3 Pack Indexing <https://wg21.link/P2662R3>`_.
131131

132+
- Implemented `P2573R2: = delete("should have a reason"); <https://wg21.link/P2573R2>`_
133+
132134

133135
Resolutions to C++ Defect Reports
134136
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/Decl.h

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1993,21 +1993,35 @@ class FunctionDecl : public DeclaratorDecl,
19931993

19941994
};
19951995

1996-
/// Stashed information about a defaulted function definition whose body has
1997-
/// not yet been lazily generated.
1998-
class DefaultedFunctionInfo final
1999-
: llvm::TrailingObjects<DefaultedFunctionInfo, DeclAccessPair> {
1996+
/// Stashed information about a defaulted/deleted function body.
1997+
class DefaultedOrDeletedFunctionInfo final
1998+
: llvm::TrailingObjects<DefaultedOrDeletedFunctionInfo, DeclAccessPair,
1999+
StringLiteral *> {
20002000
friend TrailingObjects;
20012001
unsigned NumLookups;
2002+
bool HasDeletedMessage;
2003+
2004+
size_t numTrailingObjects(OverloadToken<DeclAccessPair>) const {
2005+
return NumLookups;
2006+
}
20022007

20032008
public:
2004-
static DefaultedFunctionInfo *Create(ASTContext &Context,
2005-
ArrayRef<DeclAccessPair> Lookups);
2009+
static DefaultedOrDeletedFunctionInfo *
2010+
Create(ASTContext &Context, ArrayRef<DeclAccessPair> Lookups,
2011+
StringLiteral *DeletedMessage = nullptr);
2012+
20062013
/// Get the unqualified lookup results that should be used in this
20072014
/// defaulted function definition.
20082015
ArrayRef<DeclAccessPair> getUnqualifiedLookups() const {
20092016
return {getTrailingObjects<DeclAccessPair>(), NumLookups};
20102017
}
2018+
2019+
StringLiteral *getDeletedMessage() const {
2020+
return HasDeletedMessage ? *getTrailingObjects<StringLiteral *>()
2021+
: nullptr;
2022+
}
2023+
2024+
void setDeletedMessage(StringLiteral *Message);
20112025
};
20122026

20132027
private:
@@ -2017,12 +2031,12 @@ class FunctionDecl : public DeclaratorDecl,
20172031
ParmVarDecl **ParamInfo = nullptr;
20182032

20192033
/// The active member of this union is determined by
2020-
/// FunctionDeclBits.HasDefaultedFunctionInfo.
2034+
/// FunctionDeclBits.HasDefaultedOrDeletedInfo.
20212035
union {
20222036
/// The body of the function.
20232037
LazyDeclStmtPtr Body;
20242038
/// Information about a future defaulted function definition.
2025-
DefaultedFunctionInfo *DefaultedInfo;
2039+
DefaultedOrDeletedFunctionInfo *DefaultedOrDeletedInfo;
20262040
};
20272041

20282042
unsigned ODRHash;
@@ -2280,18 +2294,18 @@ class FunctionDecl : public DeclaratorDecl,
22802294

22812295
/// Returns whether this specific declaration of the function has a body.
22822296
bool doesThisDeclarationHaveABody() const {
2283-
return (!FunctionDeclBits.HasDefaultedFunctionInfo && Body) ||
2297+
return (!FunctionDeclBits.HasDefaultedOrDeletedInfo && Body) ||
22842298
isLateTemplateParsed();
22852299
}
22862300

22872301
void setBody(Stmt *B);
22882302
void setLazyBody(uint64_t Offset) {
2289-
FunctionDeclBits.HasDefaultedFunctionInfo = false;
2303+
FunctionDeclBits.HasDefaultedOrDeletedInfo = false;
22902304
Body = LazyDeclStmtPtr(Offset);
22912305
}
22922306

2293-
void setDefaultedFunctionInfo(DefaultedFunctionInfo *Info);
2294-
DefaultedFunctionInfo *getDefaultedFunctionInfo() const;
2307+
void setDefaultedOrDeletedInfo(DefaultedOrDeletedFunctionInfo *Info);
2308+
DefaultedOrDeletedFunctionInfo *getDefalutedOrDeletedInfo() const;
22952309

22962310
/// Whether this function is variadic.
22972311
bool isVariadic() const;
@@ -2494,7 +2508,7 @@ class FunctionDecl : public DeclaratorDecl,
24942508
return FunctionDeclBits.IsDeleted && !isDefaulted();
24952509
}
24962510

2497-
void setDeletedAsWritten(bool D = true) { FunctionDeclBits.IsDeleted = D; }
2511+
void setDeletedAsWritten(bool D = true, StringLiteral *Message = nullptr);
24982512

24992513
/// Determines whether this function is "main", which is the
25002514
/// entry point into an executable program.
@@ -2650,6 +2664,13 @@ class FunctionDecl : public DeclaratorDecl,
26502664
AC.push_back(TRC);
26512665
}
26522666

2667+
/// Get the message that indicates why this function was deleted.
2668+
StringLiteral *getDeletedMessage() const {
2669+
return FunctionDeclBits.HasDefaultedOrDeletedInfo
2670+
? DefaultedOrDeletedInfo->getDeletedMessage()
2671+
: nullptr;
2672+
}
2673+
26532674
void setPreviousDeclaration(FunctionDecl * PrevDecl);
26542675

26552676
FunctionDecl *getCanonicalDecl() override;

clang/include/clang/AST/DeclBase.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1739,7 +1739,7 @@ class DeclContext {
17391739
LLVM_PREFERRED_TYPE(bool)
17401740
uint64_t IsExplicitlyDefaulted : 1;
17411741
LLVM_PREFERRED_TYPE(bool)
1742-
uint64_t HasDefaultedFunctionInfo : 1;
1742+
uint64_t HasDefaultedOrDeletedInfo : 1;
17431743

17441744
/// For member functions of complete types, whether this is an ineligible
17451745
/// special member function or an unselected destructor. See

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,12 @@ def warn_cxx98_compat_defaulted_deleted_function : Warning<
941941
"%select{defaulted|deleted}0 function definitions are incompatible with C++98">,
942942
InGroup<CXX98Compat>, DefaultIgnore;
943943

944+
def ext_delete_with_message : ExtWarn<
945+
"'= delete' with a message is a C++2c extension">, InGroup<CXX26>;
946+
def warn_cxx23_delete_with_message : Warning<
947+
"'= delete' with a message is incompatible with C++ standards before C++2c">,
948+
DefaultIgnore, InGroup<CXXPre26Compat>;
949+
944950
// C++11 default member initialization
945951
def ext_nonstatic_member_init : ExtWarn<
946952
"default member initializer for non-static data member is a C++11 "

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4683,11 +4683,10 @@ def err_ovl_no_viable_member_function_in_call : Error<
46834683
"no matching member function for call to %0">;
46844684
def err_ovl_ambiguous_call : Error<
46854685
"call to %0 is ambiguous">;
4686-
def err_ovl_deleted_call : Error<"call to deleted function %0">;
4686+
def err_ovl_deleted_call : Error<"call to deleted"
4687+
"%select{| member}0 function %1%select{|: %3}2">;
46874688
def err_ovl_ambiguous_member_call : Error<
46884689
"call to member function %0 is ambiguous">;
4689-
def err_ovl_deleted_member_call : Error<
4690-
"call to deleted member function %0">;
46914690
def note_ovl_too_many_candidates : Note<
46924691
"remaining %0 candidate%s0 omitted; "
46934692
"pass -fshow-overloads=all to show them">;
@@ -4915,12 +4914,12 @@ def err_ovl_ambiguous_conversion_in_cast : Error<
49154914
"dynamic_cast|C-style cast|functional-style cast|}0 from %1 to %2">;
49164915
def err_ovl_deleted_conversion_in_cast : Error<
49174916
"%select{|static_cast|reinterpret_cast|dynamic_cast|C-style cast|"
4918-
"functional-style cast|}0 from %1 to %2 uses deleted function">;
4917+
"functional-style cast|}0 from %1 to %2 uses deleted function%select{|: %4}3">;
49194918
def err_ovl_ambiguous_init : Error<"call to constructor of %0 is ambiguous">;
49204919
def err_ref_init_ambiguous : Error<
49214920
"reference initialization of type %0 with initializer of type %1 is ambiguous">;
49224921
def err_ovl_deleted_init : Error<
4923-
"call to deleted constructor of %0">;
4922+
"call to deleted constructor of %0%select{|: %2}1">;
49244923
def err_ovl_deleted_special_init : Error<
49254924
"call to implicitly-deleted %select{default constructor|copy constructor|"
49264925
"move constructor|copy assignment operator|move assignment operator|"
@@ -4946,7 +4945,7 @@ def note_ovl_ambiguous_oper_binary_reversed_candidate : Note<
49464945
def err_ovl_no_viable_oper : Error<"no viable overloaded '%0'">;
49474946
def note_assign_lhs_incomplete : Note<"type %0 is incomplete">;
49484947
def err_ovl_deleted_oper : Error<
4949-
"overload resolution selected deleted operator '%0'">;
4948+
"overload resolution selected deleted operator '%0'%select{|: %2}1">;
49504949
def err_ovl_deleted_special_oper : Error<
49514950
"object of type %0 cannot be %select{constructed|copied|moved|assigned|"
49524951
"assigned|destroyed}1 because its %sub{select_special_member_kind}1 is "
@@ -4983,7 +4982,7 @@ def err_ovl_ambiguous_object_call : Error<
49834982
def err_ovl_ambiguous_subscript_call : Error<
49844983
"call to subscript operator of type %0 is ambiguous">;
49854984
def err_ovl_deleted_object_call : Error<
4986-
"call to deleted function call operator in type %0">;
4985+
"call to deleted function call operator in type %0%select{|: %2}1">;
49874986
def note_ovl_surrogate_cand : Note<"conversion candidate of type %0">;
49884987
def err_member_call_without_object : Error<
49894988
"call to %select{non-static|explicit}0 member function without an object argument">;
@@ -8284,7 +8283,7 @@ def err_typecheck_nonviable_condition_incomplete : Error<
82848283
"no viable conversion%diff{ from $ to incomplete type $|}0,1">;
82858284
def err_typecheck_deleted_function : Error<
82868285
"conversion function %diff{from $ to $|between types}0,1 "
8287-
"invokes a deleted function">;
8286+
"invokes a deleted function%select{|: %3}2">;
82888287

82898288
def err_expected_class_or_namespace : Error<"%0 is not a class"
82908289
"%select{ or namespace|, namespace, or enumeration}1">;
@@ -8884,7 +8883,7 @@ def err_nontemporal_builtin_must_be_pointer_intfltptr_or_vector : Error<
88848883
"address argument to nontemporal builtin must be a pointer to integer, float, "
88858884
"pointer, or a vector of such types (%0 invalid)">;
88868885

8887-
def err_deleted_function_use : Error<"attempt to use a deleted function">;
8886+
def err_deleted_function_use : Error<"attempt to use a deleted function%select{|: %1}0">;
88888887
def err_deleted_inherited_ctor_use : Error<
88898888
"constructor inherited by %0 from base class %1 is implicitly deleted">;
88908889

clang/include/clang/Parse/Parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,6 +1601,8 @@ class Parser : public CodeCompletionHandler {
16011601
const ParsedTemplateInfo &TemplateInfo,
16021602
const VirtSpecifiers &VS,
16031603
SourceLocation PureSpecLoc);
1604+
StringLiteral *ParseCXXDeletedFunctionMessage();
1605+
void SkipDeletedFunctionBody();
16041606
void ParseCXXNonStaticMemberInitializer(Decl *VarD);
16051607
void ParseLexedAttributes(ParsingClass &Class);
16061608
void ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D,

clang/include/clang/Sema/Sema.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3045,14 +3045,18 @@ class Sema final : public SemaBase {
30453045
void ActOnDocumentableDecls(ArrayRef<Decl *> Group);
30463046

30473047
enum class FnBodyKind {
3048-
/// C++ [dcl.fct.def.general]p1
3048+
/// C++26 [dcl.fct.def.general]p1
30493049
/// function-body:
30503050
/// ctor-initializer[opt] compound-statement
30513051
/// function-try-block
30523052
Other,
30533053
/// = default ;
30543054
Default,
3055+
/// deleted-function-body
3056+
///
3057+
/// deleted-function-body:
30553058
/// = delete ;
3059+
/// = delete ( unevaluated-string ) ;
30563060
Delete
30573061
};
30583062

@@ -4751,10 +4755,12 @@ class Sema final : public SemaBase {
47514755
SourceLocation EqualLoc);
47524756

47534757
void ActOnPureSpecifier(Decl *D, SourceLocation PureSpecLoc);
4754-
void SetDeclDeleted(Decl *dcl, SourceLocation DelLoc);
4758+
void SetDeclDeleted(Decl *dcl, SourceLocation DelLoc,
4759+
StringLiteral *Message = nullptr);
47554760
void SetDeclDefaulted(Decl *dcl, SourceLocation DefaultLoc);
47564761

4757-
void SetFunctionBodyKind(Decl *D, SourceLocation Loc, FnBodyKind BodyKind);
4762+
void SetFunctionBodyKind(Decl *D, SourceLocation Loc, FnBodyKind BodyKind,
4763+
StringLiteral *DeletedMessage = nullptr);
47584764
void ActOnStartTrailingRequiresClause(Scope *S, Declarator &D);
47594765
ExprResult ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr);
47604766
ExprResult ActOnRequiresClause(ExprResult ConstraintExpr);
@@ -8093,6 +8099,11 @@ class Sema final : public SemaBase {
80938099
bool IsFunctionConversion(QualType FromType, QualType ToType,
80948100
QualType &ResultTy);
80958101
bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType);
8102+
void DiagnoseUseOfDeletedFunction(SourceLocation Loc, SourceRange Range,
8103+
DeclarationName Name,
8104+
OverloadCandidateSet &CandidateSet,
8105+
FunctionDecl *Fn, MultiExprArg Args,
8106+
bool IsMember = false);
80968107

80978108
ExprResult InitializeExplicitObjectArgument(Sema &S, Expr *Obj,
80988109
FunctionDecl *Fun);

clang/lib/AST/ASTImporter.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3947,6 +3947,14 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
39473947
// decl and its redeclarations may be required.
39483948
}
39493949

3950+
StringLiteral *Msg = D->getDeletedMessage();
3951+
if (Msg) {
3952+
auto Imported = import(Msg);
3953+
if (!Imported)
3954+
return Imported.takeError();
3955+
Msg = *Imported;
3956+
}
3957+
39503958
ToFunction->setQualifierInfo(ToQualifierLoc);
39513959
ToFunction->setAccess(D->getAccess());
39523960
ToFunction->setLexicalDeclContext(LexicalDC);
@@ -3961,6 +3969,11 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
39613969
ToFunction->setRangeEnd(ToEndLoc);
39623970
ToFunction->setDefaultLoc(ToDefaultLoc);
39633971

3972+
if (Msg)
3973+
ToFunction->setDefaultedOrDeletedInfo(
3974+
FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
3975+
Importer.getToContext(), {}, Msg));
3976+
39643977
// Set the parameters.
39653978
for (auto *Param : Parameters) {
39663979
Param->setOwningFunction(ToFunction);

clang/lib/AST/Decl.cpp

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3058,7 +3058,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
30583058
FunctionDeclBits.IsTrivialForCall = false;
30593059
FunctionDeclBits.IsDefaulted = false;
30603060
FunctionDeclBits.IsExplicitlyDefaulted = false;
3061-
FunctionDeclBits.HasDefaultedFunctionInfo = false;
3061+
FunctionDeclBits.HasDefaultedOrDeletedInfo = false;
30623062
FunctionDeclBits.IsIneligibleOrNotSelected = false;
30633063
FunctionDeclBits.HasImplicitReturnZero = false;
30643064
FunctionDeclBits.IsLateTemplateParsed = false;
@@ -3092,30 +3092,65 @@ bool FunctionDecl::isVariadic() const {
30923092
return false;
30933093
}
30943094

3095-
FunctionDecl::DefaultedFunctionInfo *
3096-
FunctionDecl::DefaultedFunctionInfo::Create(ASTContext &Context,
3097-
ArrayRef<DeclAccessPair> Lookups) {
3098-
DefaultedFunctionInfo *Info = new (Context.Allocate(
3099-
totalSizeToAlloc<DeclAccessPair>(Lookups.size()),
3100-
std::max(alignof(DefaultedFunctionInfo), alignof(DeclAccessPair))))
3101-
DefaultedFunctionInfo;
3095+
FunctionDecl::DefaultedOrDeletedFunctionInfo *
3096+
FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
3097+
ASTContext &Context, ArrayRef<DeclAccessPair> Lookups,
3098+
StringLiteral *DeletedMessage) {
3099+
static constexpr size_t Alignment =
3100+
std::max({alignof(DefaultedOrDeletedFunctionInfo),
3101+
alignof(DeclAccessPair), alignof(StringLiteral *)});
3102+
size_t Size = totalSizeToAlloc<DeclAccessPair, StringLiteral *>(
3103+
Lookups.size(), DeletedMessage != nullptr);
3104+
3105+
DefaultedOrDeletedFunctionInfo *Info =
3106+
new (Context.Allocate(Size, Alignment)) DefaultedOrDeletedFunctionInfo;
31023107
Info->NumLookups = Lookups.size();
3108+
Info->HasDeletedMessage = DeletedMessage != nullptr;
3109+
31033110
std::uninitialized_copy(Lookups.begin(), Lookups.end(),
31043111
Info->getTrailingObjects<DeclAccessPair>());
3112+
if (DeletedMessage)
3113+
*Info->getTrailingObjects<StringLiteral *>() = DeletedMessage;
31053114
return Info;
31063115
}
31073116

3108-
void FunctionDecl::setDefaultedFunctionInfo(DefaultedFunctionInfo *Info) {
3109-
assert(!FunctionDeclBits.HasDefaultedFunctionInfo && "already have this");
3117+
void FunctionDecl::setDefaultedOrDeletedInfo(
3118+
DefaultedOrDeletedFunctionInfo *Info) {
3119+
assert(!FunctionDeclBits.HasDefaultedOrDeletedInfo && "already have this");
31103120
assert(!Body && "can't replace function body with defaulted function info");
31113121

3112-
FunctionDeclBits.HasDefaultedFunctionInfo = true;
3113-
DefaultedInfo = Info;
3122+
FunctionDeclBits.HasDefaultedOrDeletedInfo = true;
3123+
DefaultedOrDeletedInfo = Info;
3124+
}
3125+
3126+
void FunctionDecl::setDeletedAsWritten(bool D, StringLiteral *Message) {
3127+
FunctionDeclBits.IsDeleted = D;
3128+
3129+
if (Message) {
3130+
assert(isDeletedAsWritten() && "Function must be deleted");
3131+
if (FunctionDeclBits.HasDefaultedOrDeletedInfo)
3132+
DefaultedOrDeletedInfo->setDeletedMessage(Message);
3133+
else
3134+
setDefaultedOrDeletedInfo(DefaultedOrDeletedFunctionInfo::Create(
3135+
getASTContext(), /*Lookups=*/{}, Message));
3136+
}
3137+
}
3138+
3139+
void FunctionDecl::DefaultedOrDeletedFunctionInfo::setDeletedMessage(
3140+
StringLiteral *Message) {
3141+
// We should never get here with the DefaultedOrDeletedInfo populated, but
3142+
// no space allocated for the deleted message, since that would require
3143+
// recreating this, but setDefaultedOrDeletedInfo() disallows overwriting
3144+
// an already existing DefaultedOrDeletedFunctionInfo.
3145+
assert(HasDeletedMessage &&
3146+
"No space to store a delete message in this DefaultedOrDeletedInfo");
3147+
*getTrailingObjects<StringLiteral *>() = Message;
31143148
}
31153149

3116-
FunctionDecl::DefaultedFunctionInfo *
3117-
FunctionDecl::getDefaultedFunctionInfo() const {
3118-
return FunctionDeclBits.HasDefaultedFunctionInfo ? DefaultedInfo : nullptr;
3150+
FunctionDecl::DefaultedOrDeletedFunctionInfo *
3151+
FunctionDecl::getDefalutedOrDeletedInfo() const {
3152+
return FunctionDeclBits.HasDefaultedOrDeletedInfo ? DefaultedOrDeletedInfo
3153+
: nullptr;
31193154
}
31203155

31213156
bool FunctionDecl::hasBody(const FunctionDecl *&Definition) const {
@@ -3202,7 +3237,7 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const {
32023237
if (!hasBody(Definition))
32033238
return nullptr;
32043239

3205-
assert(!Definition->FunctionDeclBits.HasDefaultedFunctionInfo &&
3240+
assert(!Definition->FunctionDeclBits.HasDefaultedOrDeletedInfo &&
32063241
"definition should not have a body");
32073242
if (Definition->Body)
32083243
return Definition->Body.get(getASTContext().getExternalSource());
@@ -3211,7 +3246,7 @@ Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const {
32113246
}
32123247

32133248
void FunctionDecl::setBody(Stmt *B) {
3214-
FunctionDeclBits.HasDefaultedFunctionInfo = false;
3249+
FunctionDeclBits.HasDefaultedOrDeletedInfo = false;
32153250
Body = LazyDeclStmtPtr(B);
32163251
if (B)
32173252
EndRangeLoc = B->getEndLoc();

0 commit comments

Comments
 (0)