forked from OSchip/llvm-project
[Clang] CWG 1394: Incomplete types as parameters of deleted functions
According to CWG 1394 and C++20 [dcl.fct.def.general]p2, Clang should not diagnose incomplete types if function body is "= delete;". For example: ``` struct Incomplete; Incomplete f(Incomplete) = delete; // well-formed ``` Also close https://github.com/llvm/llvm-project/issues/52802 Differential Revision: https://reviews.llvm.org/D122981
This commit is contained in:
parent
369c5fa17b
commit
50b1faf5c1
|
@ -113,6 +113,10 @@ Bug Fixes
|
|||
This fixes Issue `Issue 54462 <https://github.com/llvm/llvm-project/issues/54462>`_.
|
||||
- Statement expressions are now disabled in default arguments in general.
|
||||
This fixes Issue `Issue 53488 <https://github.com/llvm/llvm-project/issues/53488>`_.
|
||||
- According to `CWG 1394 <https://wg21.link/cwg1394>`_ and
|
||||
`C++20 [dcl.fct.def.general]p2 <https://timsong-cpp.github.io/cppwp/n4868/dcl.fct.def#general-2.sentence-3>`_,
|
||||
Clang should not diagnose incomplete types in function definitions if the function body is "= delete;".
|
||||
This fixes Issue `Issue 52802 <https://github.com/llvm/llvm-project/issues/52802>`_.
|
||||
|
||||
Improvements to Clang's diagnostics
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -2895,6 +2895,18 @@ public:
|
|||
void ActOnDocumentableDecl(Decl *D);
|
||||
void ActOnDocumentableDecls(ArrayRef<Decl *> Group);
|
||||
|
||||
enum class FnBodyKind {
|
||||
/// C++ [dcl.fct.def.general]p1
|
||||
/// function-body:
|
||||
/// ctor-initializer[opt] compound-statement
|
||||
/// function-try-block
|
||||
Other,
|
||||
/// = default ;
|
||||
Default,
|
||||
/// = delete ;
|
||||
Delete
|
||||
};
|
||||
|
||||
void ActOnFinishKNRParamDeclarations(Scope *S, Declarator &D,
|
||||
SourceLocation LocAfterDecls);
|
||||
void CheckForFunctionRedefinition(
|
||||
|
@ -2902,9 +2914,12 @@ public:
|
|||
SkipBodyInfo *SkipBody = nullptr);
|
||||
Decl *ActOnStartOfFunctionDef(Scope *S, Declarator &D,
|
||||
MultiTemplateParamsArg TemplateParamLists,
|
||||
SkipBodyInfo *SkipBody = nullptr);
|
||||
SkipBodyInfo *SkipBody = nullptr,
|
||||
FnBodyKind BodyKind = FnBodyKind::Other);
|
||||
Decl *ActOnStartOfFunctionDef(Scope *S, Decl *D,
|
||||
SkipBodyInfo *SkipBody = nullptr);
|
||||
SkipBodyInfo *SkipBody = nullptr,
|
||||
FnBodyKind BodyKind = FnBodyKind::Other);
|
||||
void SetFunctionBodyKind(Decl *D, SourceLocation Loc, FnBodyKind BodyKind);
|
||||
void ActOnStartTrailingRequiresClause(Scope *S, Declarator &D);
|
||||
ExprResult ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr);
|
||||
ExprResult ActOnRequiresClause(ExprResult ConstraintExpr);
|
||||
|
|
|
@ -1299,6 +1299,41 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
|
|||
ParseScope BodyScope(this, Scope::FnScope | Scope::DeclScope |
|
||||
Scope::CompoundStmtScope);
|
||||
|
||||
// Parse function body eagerly if it is either '= delete;' or '= default;' as
|
||||
// ActOnStartOfFunctionDef needs to know whether the function is deleted.
|
||||
Sema::FnBodyKind BodyKind = Sema::FnBodyKind::Other;
|
||||
SourceLocation KWLoc;
|
||||
if (TryConsumeToken(tok::equal)) {
|
||||
assert(getLangOpts().CPlusPlus && "Only C++ function definitions have '='");
|
||||
|
||||
if (TryConsumeToken(tok::kw_delete, KWLoc)) {
|
||||
Diag(KWLoc, getLangOpts().CPlusPlus11
|
||||
? diag::warn_cxx98_compat_defaulted_deleted_function
|
||||
: diag::ext_defaulted_deleted_function)
|
||||
<< 1 /* deleted */;
|
||||
BodyKind = Sema::FnBodyKind::Delete;
|
||||
} else if (TryConsumeToken(tok::kw_default, KWLoc)) {
|
||||
Diag(KWLoc, getLangOpts().CPlusPlus11
|
||||
? diag::warn_cxx98_compat_defaulted_deleted_function
|
||||
: diag::ext_defaulted_deleted_function)
|
||||
<< 0 /* defaulted */;
|
||||
BodyKind = Sema::FnBodyKind::Default;
|
||||
} else {
|
||||
llvm_unreachable("function definition after = not 'delete' or 'default'");
|
||||
}
|
||||
|
||||
if (Tok.is(tok::comma)) {
|
||||
Diag(KWLoc, diag::err_default_delete_in_multiple_declaration)
|
||||
<< (BodyKind == Sema::FnBodyKind::Delete);
|
||||
SkipUntil(tok::semi);
|
||||
} else if (ExpectAndConsume(tok::semi, diag::err_expected_after,
|
||||
BodyKind == Sema::FnBodyKind::Delete
|
||||
? "delete"
|
||||
: "default")) {
|
||||
SkipUntil(tok::semi);
|
||||
}
|
||||
}
|
||||
|
||||
// Tell the actions module that we have entered a function definition with the
|
||||
// specified Declarator for the function.
|
||||
Sema::SkipBodyInfo SkipBody;
|
||||
|
@ -1306,10 +1341,13 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
|
|||
TemplateInfo.TemplateParams
|
||||
? *TemplateInfo.TemplateParams
|
||||
: MultiTemplateParamsArg(),
|
||||
&SkipBody);
|
||||
&SkipBody, BodyKind);
|
||||
|
||||
if (SkipBody.ShouldSkip) {
|
||||
SkipFunctionBody();
|
||||
// Do NOT enter SkipFunctionBody if we already consumed the tokens.
|
||||
if (BodyKind == Sema::FnBodyKind::Other)
|
||||
SkipFunctionBody();
|
||||
|
||||
return Res;
|
||||
}
|
||||
|
||||
|
@ -1320,6 +1358,13 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
|
|||
// safe because we're always the sole owner.
|
||||
D.getMutableDeclSpec().abort();
|
||||
|
||||
if (BodyKind != Sema::FnBodyKind::Other) {
|
||||
Actions.SetFunctionBodyKind(Res, KWLoc, BodyKind);
|
||||
Stmt *GeneratedBody = Res ? Res->getBody() : nullptr;
|
||||
Actions.ActOnFinishFunctionBody(Res, GeneratedBody, false);
|
||||
return Res;
|
||||
}
|
||||
|
||||
// With abbreviated function templates - we need to explicitly add depth to
|
||||
// account for the implicit template parameter list induced by the template.
|
||||
if (auto *Template = dyn_cast_or_null<FunctionTemplateDecl>(Res))
|
||||
|
@ -1329,42 +1374,6 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
|
|||
// parameter list was specified.
|
||||
CurTemplateDepthTracker.addDepth(1);
|
||||
|
||||
if (TryConsumeToken(tok::equal)) {
|
||||
assert(getLangOpts().CPlusPlus && "Only C++ function definitions have '='");
|
||||
|
||||
bool Delete = false;
|
||||
SourceLocation KWLoc;
|
||||
if (TryConsumeToken(tok::kw_delete, KWLoc)) {
|
||||
Diag(KWLoc, getLangOpts().CPlusPlus11
|
||||
? diag::warn_cxx98_compat_defaulted_deleted_function
|
||||
: diag::ext_defaulted_deleted_function)
|
||||
<< 1 /* deleted */;
|
||||
Actions.SetDeclDeleted(Res, KWLoc);
|
||||
Delete = true;
|
||||
} else if (TryConsumeToken(tok::kw_default, KWLoc)) {
|
||||
Diag(KWLoc, getLangOpts().CPlusPlus11
|
||||
? diag::warn_cxx98_compat_defaulted_deleted_function
|
||||
: diag::ext_defaulted_deleted_function)
|
||||
<< 0 /* defaulted */;
|
||||
Actions.SetDeclDefaulted(Res, KWLoc);
|
||||
} else {
|
||||
llvm_unreachable("function definition after = not 'delete' or 'default'");
|
||||
}
|
||||
|
||||
if (Tok.is(tok::comma)) {
|
||||
Diag(KWLoc, diag::err_default_delete_in_multiple_declaration)
|
||||
<< Delete;
|
||||
SkipUntil(tok::semi);
|
||||
} else if (ExpectAndConsume(tok::semi, diag::err_expected_after,
|
||||
Delete ? "delete" : "default")) {
|
||||
SkipUntil(tok::semi);
|
||||
}
|
||||
|
||||
Stmt *GeneratedBody = Res ? Res->getBody() : nullptr;
|
||||
Actions.ActOnFinishFunctionBody(Res, GeneratedBody, false);
|
||||
return Res;
|
||||
}
|
||||
|
||||
if (SkipFunctionBodies && (!Res || Actions.canSkipFunctionBody(Res)) &&
|
||||
trySkippingFunctionBody()) {
|
||||
BodyScope.Exit();
|
||||
|
|
|
@ -14271,7 +14271,7 @@ void Sema::ActOnFinishKNRParamDeclarations(Scope *S, Declarator &D,
|
|||
Decl *
|
||||
Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Declarator &D,
|
||||
MultiTemplateParamsArg TemplateParameterLists,
|
||||
SkipBodyInfo *SkipBody) {
|
||||
SkipBodyInfo *SkipBody, FnBodyKind BodyKind) {
|
||||
assert(getCurFunctionDecl() == nullptr && "Function parsing confused");
|
||||
assert(D.isFunctionDeclarator() && "Not a function declarator!");
|
||||
Scope *ParentScope = FnBodyScope->getParent();
|
||||
|
@ -14290,7 +14290,7 @@ Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Declarator &D,
|
|||
|
||||
D.setFunctionDefinitionKind(FunctionDefinitionKind::Definition);
|
||||
Decl *DP = HandleDeclarator(ParentScope, D, TemplateParameterLists);
|
||||
Decl *Dcl = ActOnStartOfFunctionDef(FnBodyScope, DP, SkipBody);
|
||||
Decl *Dcl = ActOnStartOfFunctionDef(FnBodyScope, DP, SkipBody, BodyKind);
|
||||
|
||||
if (!Bases.empty())
|
||||
ActOnFinishedFunctionDefinitionInOpenMPDeclareVariantScope(Dcl, Bases);
|
||||
|
@ -14466,7 +14466,8 @@ static void RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator,
|
|||
}
|
||||
|
||||
Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
|
||||
SkipBodyInfo *SkipBody) {
|
||||
SkipBodyInfo *SkipBody,
|
||||
FnBodyKind BodyKind) {
|
||||
if (!D) {
|
||||
// Parsing the function declaration failed in some way. Push on a fake scope
|
||||
// anyway so we can try to parse the function body.
|
||||
|
@ -14555,11 +14556,11 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
|
|||
}
|
||||
}
|
||||
|
||||
// The return type of a function definition must be complete
|
||||
// (C99 6.9.1p3, C++ [dcl.fct]p6).
|
||||
// The return type of a function definition must be complete (C99 6.9.1p3),
|
||||
// unless the function is deleted (C++ specifc, C++ [dcl.fct.def.general]p2)
|
||||
QualType ResultType = FD->getReturnType();
|
||||
if (!ResultType->isDependentType() && !ResultType->isVoidType() &&
|
||||
!FD->isInvalidDecl() &&
|
||||
!FD->isInvalidDecl() && BodyKind != FnBodyKind::Delete &&
|
||||
RequireCompleteType(FD->getLocation(), ResultType,
|
||||
diag::err_func_def_incomplete_result))
|
||||
FD->setInvalidDecl();
|
||||
|
@ -14568,8 +14569,9 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
|
|||
PushDeclContext(FnBodyScope, FD);
|
||||
|
||||
// Check the validity of our function parameters
|
||||
CheckParmsForFunctionDef(FD->parameters(),
|
||||
/*CheckParameterNames=*/true);
|
||||
if (BodyKind != FnBodyKind::Delete)
|
||||
CheckParmsForFunctionDef(FD->parameters(),
|
||||
/*CheckParameterNames=*/true);
|
||||
|
||||
// Add non-parameter declarations already in the function to the current
|
||||
// scope.
|
||||
|
|
|
@ -17379,6 +17379,21 @@ void Sema::DiagnoseReturnInConstructorExceptionHandler(CXXTryStmt *TryBlock) {
|
|||
}
|
||||
}
|
||||
|
||||
void Sema::SetFunctionBodyKind(Decl *D, SourceLocation Loc,
|
||||
FnBodyKind BodyKind) {
|
||||
switch (BodyKind) {
|
||||
case FnBodyKind::Delete:
|
||||
SetDeclDeleted(D, Loc);
|
||||
break;
|
||||
case FnBodyKind::Default:
|
||||
SetDeclDefaulted(D, Loc);
|
||||
break;
|
||||
case FnBodyKind::Other:
|
||||
llvm_unreachable(
|
||||
"Parsed function body should be '= delete;' or '= default;'");
|
||||
}
|
||||
}
|
||||
|
||||
bool Sema::CheckOverridingFunctionAttributes(const CXXMethodDecl *New,
|
||||
const CXXMethodDecl *Old) {
|
||||
const auto *NewFT = New->getType()->castAs<FunctionProtoType>();
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
|
||||
struct Incomplete; // expected-note 2{{forward declaration of 'Incomplete'}}
|
||||
Incomplete f(Incomplete) = delete; // well-formed
|
||||
Incomplete g(Incomplete) {} // expected-error{{incomplete result type 'Incomplete' in function definition}}\
|
||||
// expected-error{{variable has incomplete type 'Incomplete'}}
|
|
@ -8178,7 +8178,7 @@ and <I>POD class</I></td>
|
|||
<td><a href="https://wg21.link/cwg1394">1394</a></td>
|
||||
<td>CD3</td>
|
||||
<td>Incomplete types as parameters of deleted functions</td>
|
||||
<td class="none" align="center">Unknown</td>
|
||||
<td class="unreleased" align="center">Clang 15</td>
|
||||
</tr>
|
||||
<tr id="1395">
|
||||
<td><a href="https://wg21.link/cwg1395">1395</a></td>
|
||||
|
|
Loading…
Reference in New Issue