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
