Skip to content

Commit

Permalink
[Clang] CWG 1394: Incomplete types as parameters of deleted functions
Browse files Browse the repository at this point in the history
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 #52802

Differential Revision: https://reviews.llvm.org/D122981
  • Loading branch information
poyaoc97 committed Apr 12, 2022
1 parent 369c5fa commit 50b1faf
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 49 deletions.
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
19 changes: 17 additions & 2 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -2895,16 +2895,31 @@ class Sema final {
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(
FunctionDecl *FD, const FunctionDecl *EffectiveDefinition = nullptr,
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);
Expand Down
85 changes: 47 additions & 38 deletions clang/lib/Parse/Parser.cpp
Expand Up @@ -1299,17 +1299,55 @@ 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;
Decl *Res = Actions.ActOnStartOfFunctionDef(getCurScope(), 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;
}

Expand All @@ -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))
Expand All @@ -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();
Expand Down
18 changes: 10 additions & 8 deletions clang/lib/Sema/SemaDecl.cpp
Expand Up @@ -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();
Expand All @@ -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);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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();
Expand All @@ -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.
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/Sema/SemaDeclCXX.cpp
Expand Up @@ -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>();
Expand Down
@@ -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'}}
2 changes: 1 addition & 1 deletion clang/www/cxx_dr_status.html
Expand Up @@ -8178,7 +8178,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<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>
Expand Down

0 comments on commit 50b1faf

Please sign in to comment.