https://github.com/akash-manna-sky created 
https://github.com/llvm/llvm-project/pull/219953

Fixes #209503

`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. The 
fatal `#include` in the report is a red herring, by the way — it only hides the 
diagnostics for the function that gets queued.

`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. The new test has both shapes.



>From ce31281fec6567d77d58bfdbeabaf81cdda6a574 Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Mon, 31 Aug 2026 17:29:08 +0530
Subject: [PATCH] [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 bdbabf2cd98d0..598b7cebd15d0 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -573,6 +573,9 @@ features cannot lower the translation-unit ABI level;
   threshold to the target's `size_t` width instead of using a fixed
   threshold of `1 << 60` regardless of the target.
 - Fixed a crash when generating fake uses for parameters of bodyless 
destructors with `-fextend-variable-liveness`.
+- 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 ae91153e34e3a..8b5a0ad732ece 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 d01b0abf917cb..40d4aca43047c 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'}}

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

Reply via email to