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

Reply via email to