Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Apr 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
98af47e
[Clang] Parse `= delete("message")`
Sirraide Mar 25, 2024
5ffac60
[Clang] Tentative serialisation
Sirraide Mar 25, 2024
8dc5f8b
[Clang] Sema and tests
Sirraide Mar 25, 2024
a018c88
[Clang] Add ast printer tests
Sirraide Mar 25, 2024
b5a50be
[NFC] clang-format
Sirraide Mar 25, 2024
e48b80d
[Clang] Add release note
Sirraide Mar 25, 2024
9e45341
[Clang] Move test to the right directory and fix typo
Sirraide Mar 25, 2024
9fc02a5
[NFC] Spell out type
Sirraide Mar 26, 2024
7664e7d
[NFC] Spell out type
Sirraide Mar 26, 2024
41bb3dd
[Clang] Store message in DefaultFunctionInfo
Sirraide Mar 28, 2024
b1c930c
[Clang] Add some unicode tests
Sirraide Mar 28, 2024
29cdff9
[NFC] Update delete syntax in comment
Sirraide Mar 28, 2024
7807c71
[Clang] Add feature test macro
Sirraide Mar 28, 2024
f1d7478
Merge branch 'main' into delete-with-message
Sirraide Mar 28, 2024
40a2330
[Clang] Update cxx_status
Sirraide Mar 28, 2024
d6537a2
[Clang] Add extension+compatibility diags
Sirraide Mar 28, 2024
c1ff133
[NFC] Rename ExtraFunctionInfo and update comments
Sirraide Apr 3, 2024
3445e48
Merge branch 'main' into delete-with-message
Sirraide Apr 3, 2024
99c8dd1
[Clang] Handle remaining diagnostics
Sirraide Apr 3, 2024
9df9e9f
[Clang] Skip deleted function body on error
Sirraide Apr 9, 2024
b442173
[Clang] Include deleted message in ODR hash
Sirraide Apr 9, 2024
800b643
[Docs] Update LanguageExtensions.rst
Sirraide Apr 10, 2024
c12f90e
[Clang] Always add deleted message to ODR hash
Sirraide Apr 10, 2024
8602457
[Clang] Set deleted message in setDeletedAsWritten()
Sirraide Apr 10, 2024
8465430
[NFC] Replace `!!` with `!= nullptr`
Sirraide Apr 10, 2024
ddd2a2a
[NFC] clang-format
Sirraide Apr 10, 2024
bba5a44
[Clang] Address Aaron’s comments
Sirraide Apr 10, 2024
b11b6fe
Merge branch 'main' into delete-with-message
Sirraide Apr 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ C++2c Feature Support

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

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


Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
47 changes: 34 additions & 13 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1993,21 +1993,35 @@ class FunctionDecl : public DeclaratorDecl,

};

/// Stashed information about a defaulted function definition whose body has
/// not yet been lazily generated.
class DefaultedFunctionInfo final
: llvm::TrailingObjects<DefaultedFunctionInfo, DeclAccessPair> {
/// Stashed information about a defaulted/deleted function body.
class DefaultedOrDeletedFunctionInfo final
: llvm::TrailingObjects<DefaultedOrDeletedFunctionInfo, DeclAccessPair,
StringLiteral *> {
friend TrailingObjects;
unsigned NumLookups;
bool HasDeletedMessage;

size_t numTrailingObjects(OverloadToken<DeclAccessPair>) const {
return NumLookups;
}

public:
static DefaultedFunctionInfo *Create(ASTContext &Context,
ArrayRef<DeclAccessPair> Lookups);
static DefaultedOrDeletedFunctionInfo *
Create(ASTContext &Context, ArrayRef<DeclAccessPair> Lookups,
StringLiteral *DeletedMessage = nullptr);

/// Get the unqualified lookup results that should be used in this
/// defaulted function definition.
ArrayRef<DeclAccessPair> getUnqualifiedLookups() const {
return {getTrailingObjects<DeclAccessPair>(), NumLookups};
}

StringLiteral *getDeletedMessage() const {
return HasDeletedMessage ? *getTrailingObjects<StringLiteral *>()
: nullptr;
}

void setDeletedMessage(StringLiteral *Message);
};

private:
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Sirraide There's no reason we can't change this name to fix this typo right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That can be fixed as NFC commit. I missed it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Sirraide There's no reason we can't change this name to fix this typo right?

Ah, my bad, yeah, that should just be a NFC fix.


/// Whether this function is variadic.
bool isVariadic() const;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/DeclBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,12 @@ def warn_cxx98_compat_defaulted_deleted_function : Warning<
"%select{defaulted|deleted}0 function definitions are incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;

def ext_delete_with_message : ExtWarn<
"'= delete' with a message is a C++2c extension">, InGroup<CXX26>;
def warn_cxx23_delete_with_message : Warning<
"'= delete' with a message is incompatible with C++ standards before C++2c">,
DefaultIgnore, InGroup<CXXPre26Compat>;

// C++11 default member initialization
def ext_nonstatic_member_init : ExtWarn<
"default member initializer for non-static data member is a C++11 "
Expand Down
17 changes: 8 additions & 9 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down Expand Up @@ -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|"
Expand All @@ -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 "
Expand Down Expand Up @@ -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">;
Expand Down Expand Up @@ -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">;
Expand Down Expand Up @@ -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">;

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
17 changes: 14 additions & 3 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -3045,14 +3045,18 @@ class Sema final : public SemaBase {
void ActOnDocumentableDecls(ArrayRef<Decl *> Group);

enum class FnBodyKind {
/// C++ [dcl.fct.def.general]p1
/// C++26 [dcl.fct.def.general]p1
/// function-body:
/// ctor-initializer[opt] compound-statement
/// function-try-block
Other,
/// = default ;
Default,
/// deleted-function-body
///
/// deleted-function-body:
/// = delete ;
/// = delete ( unevaluated-string ) ;
Delete
};

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
69 changes: 52 additions & 17 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -3092,30 +3092,65 @@ bool FunctionDecl::isVariadic() const {
return false;
}

FunctionDecl::DefaultedFunctionInfo *
FunctionDecl::DefaultedFunctionInfo::Create(ASTContext &Context,
ArrayRef<DeclAccessPair> Lookups) {
DefaultedFunctionInfo *Info = new (Context.Allocate(
totalSizeToAlloc<DeclAccessPair>(Lookups.size()),
std::max(alignof(DefaultedFunctionInfo), alignof(DeclAccessPair))))
DefaultedFunctionInfo;
FunctionDecl::DefaultedOrDeletedFunctionInfo *
FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
ASTContext &Context, ArrayRef<DeclAccessPair> Lookups,
StringLiteral *DeletedMessage) {
static constexpr size_t Alignment =
std::max({alignof(DefaultedOrDeletedFunctionInfo),
alignof(DeclAccessPair), alignof(StringLiteral *)});
size_t Size = totalSizeToAlloc<DeclAccessPair, StringLiteral *>(
Lookups.size(), DeletedMessage != nullptr);

DefaultedOrDeletedFunctionInfo *Info =
new (Context.Allocate(Size, Alignment)) DefaultedOrDeletedFunctionInfo;
Info->NumLookups = Lookups.size();
Info->HasDeletedMessage = DeletedMessage != nullptr;

std::uninitialized_copy(Lookups.begin(), Lookups.end(),
Info->getTrailingObjects<DeclAccessPair>());
if (DeletedMessage)
*Info->getTrailingObjects<StringLiteral *>() = DeletedMessage;
return Info;
}

void FunctionDecl::setDefaultedFunctionInfo(DefaultedFunctionInfo *Info) {
assert(!FunctionDeclBits.HasDefaultedFunctionInfo && "already have this");
void FunctionDecl::setDefaultedOrDeletedInfo(
DefaultedOrDeletedFunctionInfo *Info) {
assert(!FunctionDeclBits.HasDefaultedOrDeletedInfo && "already have this");
assert(!Body && "can't replace function body with defaulted function info");

FunctionDeclBits.HasDefaultedFunctionInfo = true;
DefaultedInfo = Info;
FunctionDeclBits.HasDefaultedOrDeletedInfo = true;
DefaultedOrDeletedInfo = Info;
}

void FunctionDecl::setDeletedAsWritten(bool D, StringLiteral *Message) {
FunctionDeclBits.IsDeleted = D;

if (Message) {
assert(isDeletedAsWritten() && "Function must be deleted");
if (FunctionDeclBits.HasDefaultedOrDeletedInfo)
DefaultedOrDeletedInfo->setDeletedMessage(Message);
else
setDefaultedOrDeletedInfo(DefaultedOrDeletedFunctionInfo::Create(
getASTContext(), /*Lookups=*/{}, Message));
}
}

void FunctionDecl::DefaultedOrDeletedFunctionInfo::setDeletedMessage(
StringLiteral *Message) {
// We should never get here with the DefaultedOrDeletedInfo populated, but
// no space allocated for the deleted message, since that would require
// recreating this, but setDefaultedOrDeletedInfo() disallows overwriting
// an already existing DefaultedOrDeletedFunctionInfo.
assert(HasDeletedMessage &&
"No space to store a delete message in this DefaultedOrDeletedInfo");
*getTrailingObjects<StringLiteral *>() = Message;
}

FunctionDecl::DefaultedFunctionInfo *
FunctionDecl::getDefaultedFunctionInfo() const {
return FunctionDeclBits.HasDefaultedFunctionInfo ? DefaultedInfo : nullptr;
FunctionDecl::DefaultedOrDeletedFunctionInfo *
FunctionDecl::getDefalutedOrDeletedInfo() const {
return FunctionDeclBits.HasDefaultedOrDeletedInfo ? DefaultedOrDeletedInfo
: nullptr;
}

bool FunctionDecl::hasBody(const FunctionDecl *&Definition) const {
Expand Down Expand Up @@ -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());
Expand All @@ -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();
Expand Down
Loading