https://github.com/ahatanak updated 
https://github.com/llvm/llvm-project/pull/219953

>From 056d41605942f677106df91d2f88f38210759c8d Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Mon, 31 Aug 2026 17:29:08 +0530
Subject: [PATCH 1/2] [clang][Parse] Fix stale CurParsedObjCImpl after an
 @implementation ends early

ObjCImplParsingDataRAII only reset CurParsedObjCImpl in its destructor, but
an @implementation can be finished well before that: CheckNestedObjCContexts
ends it as soon as a nested @interface/@protocol/@implementation shows up,
and that container is then parsed inside the same frame. Anything defined in
there still looked like it was inside the already finished @implementation,
so ParseFunctionDefinition queued the body into a LateParsedObjCMethods that
nobody was going to drain again, and the destructor's
assert(LateParsedObjCMethods.empty()) fired.

finish() now restores the previous CurParsedObjCImpl the moment the
@implementation ends, instead of the destructor nulling it later. Restoring
rather than clearing also covers the mirror image: an @implementation nested
through a namespace inside another one used to wipe out the outer, still open
one when it ended, and the next method definition in the outer one tripped
the "Method out of @implementation" assert.

Fixes #209503
---
 clang/docs/ReleaseNotes.md         |  3 +++
 clang/include/clang/Parse/Parser.h |  4 +++-
 clang/lib/Parse/ParseObjc.cpp      |  5 ++++-
 clang/test/Parser/GH209503.mm      | 27 +++++++++++++++++++++++++++
 4 files changed, 37 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/Parser/GH209503.mm

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3cca316a91d4d..1799c5c9ac23a 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -709,6 +709,9 @@ features cannot lower the translation-unit ABI level;
 - Fixed an assertion when the `dim` argument to an OpenACC `gang` clause
   evaluated to a value not representable by a signed integer, such as an
   unsigned wrap around. (#GH221418)
+- Fixed an assertion failure when a method or function definition follows an
+  Objective-C `@implementation` that was ended by a nested `@interface`,
+  `@protocol` or `@implementation` before its `@end`. (#GH209503)
 
 ### OpenACC Specific Changes
 
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 6913c42884a36..9b2de3a10e988 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -5710,7 +5710,8 @@ class Parser : public CodeCompletionHandler {
     LateParsedObjCMethodContainer LateParsedObjCMethods;
 
     ObjCImplParsingDataRAII(Parser &parser, Decl *D)
-        : P(parser), Dcl(D), HasCFunction(false) {
+        : P(parser), Dcl(D), HasCFunction(false),
+          PrevParsedObjCImpl(parser.CurParsedObjCImpl) {
       P.CurParsedObjCImpl = this;
       Finished = false;
     }
@@ -5720,6 +5721,7 @@ class Parser : public CodeCompletionHandler {
     bool isFinished() const { return Finished; }
 
   private:
+    ObjCImplParsingDataRAII *PrevParsedObjCImpl;
     bool Finished;
   };
   ObjCImplParsingDataRAII *CurParsedObjCImpl;
diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp
index a70f6356ae013..7ca08cb2112e6 100644
--- a/clang/lib/Parse/ParseObjc.cpp
+++ b/clang/lib/Parse/ParseObjc.cpp
@@ -2035,7 +2035,6 @@ 
Parser::ObjCImplParsingDataRAII::~ObjCImplParsingDataRAII() {
           << SemaObjC::OCK_Implementation;
     }
   }
-  P.CurParsedObjCImpl = nullptr;
   assert(LateParsedObjCMethods.empty());
 }
 
@@ -2061,6 +2060,10 @@ void Parser::ObjCImplParsingDataRAII::finish(SourceRange 
AtEnd) {
     delete *I;
   LateParsedObjCMethods.clear();
 
+  // Parsing may go on in the enclosing frame before this object is destroyed
+  // (e.g. a nested @interface ended the @implementation early), so stop being
+  // the current @implementation now rather than in the destructor.
+  P.CurParsedObjCImpl = PrevParsedObjCImpl;
   Finished = true;
 }
 
diff --git a/clang/test/Parser/GH209503.mm b/clang/test/Parser/GH209503.mm
new file mode 100644
index 0000000000000..43d14b242dbcc
--- /dev/null
+++ b/clang/test/Parser/GH209503.mm
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s
+
+// Method and function bodies are parsed when their @implementation ends; make
+// sure they are queued into the right @implementation (if any) after a nested
+// container has ended one.
+
+@interface Z
+@end
+@interface A
+@end
+
+@implementation Z
+namespace N {
+@implementation A // expected-error {{Objective-C declarations may only appear 
in global scope}}
+@end
+}
+- (void)m {
+  undeclared(); // expected-error {{use of undeclared identifier 'undeclared'}}
+}
+@end
+
+@implementation NSArray // expected-warning {{cannot find interface 
declaration for 'NSArray'}} \
+                        // expected-note {{implementation started here}}
+@interface NSIndexSet // expected-error {{missing '@end'}} \
+                      // expected-note {{class started here}}
+// expected-warning@+1 {{function definition inside an Objective-C container 
is deprecated}}
+void f(void) {} // expected-error {{missing '@end'}}

>From 6f1deae95075d7b7420c30b972cda0addf90b813 Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Fri, 11 Sep 2026 08:05:23 +0530
Subject: [PATCH 2/2] Document `PrevParsedObjCImpl`

Explain that it is only ever non-null for invalid code, where an
@implementation starts while a previous one is still open.
---
 clang/include/clang/Parse/Parser.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 9b2de3a10e988..960b3c2485db0 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -5721,6 +5721,11 @@ class Parser : public CodeCompletionHandler {
     bool isFinished() const { return Finished; }
 
   private:
+    /// The \@implementation that was still open when this one started; made
+    /// current again once this one finishes. Only invalid code has one: an
+    /// \@implementation that starts while a previous \@implementation is
+    /// still open (e.g. through an intervening namespace). For valid code
+    /// this is always null.
     ObjCImplParsingDataRAII *PrevParsedObjCImpl;
     bool Finished;
   };

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to