diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -113,6 +113,10 @@ This fixes Issue `Issue 54462 `_. - Statement expressions are now disabled in default arguments in general. This fixes Issue `Issue 53488 `_. +- According to `CWG 1394 `_ and + `C++20 [dcl.fct.def.general]p2 `_, + Clang should not diagnose incomplete types in function definitions if the function body is "= delete;". + This fixes Issue `Issue 52802 `_. Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2895,6 +2895,18 @@ void ActOnDocumentableDecl(Decl *D); void ActOnDocumentableDecls(ArrayRef 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 @@ 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); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1299,6 +1299,41 @@ 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 @@ 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 @@ // 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(Res)) @@ -1329,42 +1374,6 @@ // 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(); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14271,7 +14271,7 @@ 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 @@ 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 @@ } 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 @@ } } - // 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 @@ 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. diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17379,6 +17379,21 @@ } } +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(); diff --git a/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.general/p2.cpp b/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.general/p2.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.general/p2.cpp @@ -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'}} diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -8178,7 +8178,7 @@ 1394 CD3 Incomplete types as parameters of deleted functions - Unknown + Clang 15 1395