https://github.com/SoulTch created https://github.com/llvm/llvm-project/pull/218168
Hi, this is my first contribution to the LLVM project, so please let me know if there is anything I should follow when contributing. Simply fixes https://github.com/llvm/llvm-project/issues/194298 - If a function has a ":" symbol in its declaration, the following "{" symbol is interpreted as an initializer, not as the function body. - Hence, the function is interpreted as a function declaration without a body. - Meanwhile, "co_await" marks the function as a coroutine, so it tries to find the function body and crashes while accessing it. - This diff marks the function as invalid if there is an initializer but no actual function body. I utilized AI while trying to understand why this change makes sense as much as possible. Assisted-by: Claude Opus 5 (Claude Code) >From 89b6fdf4644635b0975a8cfcd6f2ee903b306d21 Mon Sep 17 00:00:00 2001 From: Jeongjin Lee <[email protected]> Date: Sat, 22 Aug 2026 17:32:21 -0400 Subject: [PATCH] [Clang] Mark the declaration invalid when a function definition has no body Three parser paths abandon a function definition and finish it with a null body when a ctor-initializer is not followed by '{': Parser.cpp ParseFunctionDefinition ParseCXXInlineMethods.cpp ParseLexedMethodDef ParseTemplate.cpp ParseLateTemplatedFuncDef None of them marked the declaration invalid, but CheckCompletedCoroutineBody relies on the opposite: if (!Body) { assert(FD->isInvalidDecl() && "a null body is only allowed for invalid declarations"); return; } so a declaration that reaches one of those paths and also uses a coroutine keyword crashes an assertions build: class C {}; C f() : bar { co_await ({}) } The braced-init-list is consumed as part of the mem-initializer, leaving no '{' for the function body, while the co_await inside it marks the function as a coroutine. This cannot happen in a well-formed program: only constructors have mem-initializers, and a constructor cannot be a coroutine. Mark the declaration invalid where the body is dropped, which is what the assertion requires. Note that setInvalidDecl() does not propagate from a FunctionTemplateDecl to its templated decl, so the FunctionDecl is marked directly. Two alternatives were rejected: - Marking the declaration invalid in ActOnMemInitializers fixes the crash, but is both narrower and broader than the invariant: it misses the other ways of reaching these paths, and it suppresses unrelated diagnostics when a body is present, e.g. "no viable conversion from 'C' to 'int'" for C f() : bar { } { } void g() { int x = f(); } - Marking it in ActOnFinishFunctionBody whenever the body is null covers all three paths at once, but a null body is also legitimate there for deleted and defaulted functions, and marking those invalid loses every "call to deleted function" diagnostic. The test covers the first two paths. The ParseLateTemplatedFuncDef path needs -fdelayed-template-parsing and an instantiation to be reached, which then hits the separate pre-existing assertion in InstantiateFunctionDefinition tracked by llvm/llvm-project#50100, so it cannot be checked independently here. Fixes #194298 Assisted-by: Claude Opus 5 (Claude Code) --- clang/lib/Parse/ParseCXXInlineMethods.cpp | 1 + clang/lib/Parse/ParseTemplate.cpp | 4 +++- clang/lib/Parse/Parser.cpp | 2 ++ clang/test/SemaCXX/coroutines.cpp | 8 ++++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index 3f101feb26a6d..51591011ca548 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -633,6 +633,7 @@ void Parser::ParseLexedMethodDef(LexedMethod &LM) { // Error recovery. if (!Tok.is(tok::l_brace)) { FnScope.Exit(); + LM.D->getAsFunction()->setInvalidDecl(); Actions.ActOnFinishFunctionBody(LM.D, nullptr); return; } diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 735a9bd1f9f1c..e69d953215e89 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -1517,8 +1517,10 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) { "current template being instantiated!"); ParseFunctionStatementBody(LPT.D, FnScope); Actions.UnmarkAsLateParsedTemplate(FunD); - } else + } else { + FunD->setInvalidDecl(); Actions.ActOnFinishFunctionBody(LPT.D, nullptr); + } } } diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index bad81ea92cd2d..5c39ba2ad71a1 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1407,6 +1407,8 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, // Recover from error. if (!Tok.is(tok::l_brace)) { BodyScope.Exit(); + if (Res) + Res->getAsFunction()->setInvalidDecl(); Actions.ActOnFinishFunctionBody(Res, nullptr); return Res; } diff --git a/clang/test/SemaCXX/coroutines.cpp b/clang/test/SemaCXX/coroutines.cpp index 4cef2f2b7ea0f..19b2ce1678e1f 100644 --- a/clang/test/SemaCXX/coroutines.cpp +++ b/clang/test/SemaCXX/coroutines.cpp @@ -1566,3 +1566,11 @@ void g() { } } + +namespace GH194298 { +// https://github.com/llvm/llvm-project/issues/194298 +coro<promise_void> f1() : bar { co_await suspend_always{} }; // expected-error {{only constructors take base initializers}} +coro<promise> f2() : bar { co_yield 0 }; // expected-error {{only constructors take base initializers}} +coro<promise_void> f3() : bar { co_return }; // expected-error {{expected expression}} \ + // expected-error {{only constructors take base initializers}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
