[clang] [lldb] [Modules] Handle decl attributes on deserialization the same as during parsing. (PR #208348)
Volodymyr Sapsai via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 9 18:16:34 PDT 2026
https://github.com/vsapsai updated https://github.com/llvm/llvm-project/pull/208348
>From 6b6264b4a39d0427ad9aad3eba2f6f5c9926a270 Mon Sep 17 00:00:00 2001
From: Volodymyr Sapsai <vsapsai at apple.com>
Date: Wed, 8 Jul 2026 15:47:11 -0700
Subject: [PATCH 1/2] [Modules] Handle decl attributes on deserialization the
same as during parsing.
Replace manual handling of 2 attributes with `Sema::mergeDeclAttributes`,
which is called during parsing too. Also propagate attributes not from
a previous redeclaration but from a previous redeclaration outside of
the current module. This is done to avoid double propagation because attributes
from a previous decl in the same module are already handled when a module
is built.
Call `ASTDeclReader::attachLatestDecl` after each decl is added to
a redeclaration chain, not once per `ASTReader::loadPendingDeclChain` call.
This is done to maintain correct redeclaration chain for each
`ASTDeclReader::attachPreviousDecl` call because [newly added]
`mergeDeclAttributes` requires a correct redeclaration chain.
Separated `Parser::Initialize` and `Parser::ConsumeToken` so can move
the initialization earlier. This way `Parser::Initialize`, `Sema::Initialize`,
`ASTReader::InitializeSema` are executed before deserialization. This specific
case is verified by "Modules/decl-attr-merge-explicit-modules.c".
rdar://175997317
---
clang/include/clang/Parse/Parser.h | 3 +-
clang/lib/Interpreter/IncrementalParser.cpp | 1 +
clang/lib/Parse/ParseAST.cpp | 3 +-
clang/lib/Parse/ParseHLSLRootSignature.cpp | 1 +
clang/lib/Parse/Parser.cpp | 3 -
clang/lib/Serialization/ASTReaderDecl.cpp | 55 +++++--------
.../decl-attr-merge-explicit-modules.c | 79 +++++++++++++++++++
clang/test/Modules/decl-attr-merge2.c | 38 +++++++++
.../declare_variant_construct_codegen_1.c | 2 +-
9 files changed, 142 insertions(+), 43 deletions(-)
create mode 100644 clang/test/Modules/decl-attr-merge-explicit-modules.c
create mode 100644 clang/test/Modules/decl-attr-merge2.c
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index f0e06473bf615..c2b1d0b380185 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -304,8 +304,9 @@ class Parser : public CodeCompletionHandler {
typedef OpaquePtr<DeclGroupRef> DeclGroupPtrTy;
typedef OpaquePtr<TemplateName> TemplateTy;
- /// Initialize - Warm up the parser.
+ /// Prepare the parser and its components.
///
+ /// The lack of initialization can lead to missing functionality.
void Initialize();
/// Parse the first top-level declaration in a translation unit.
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index f6d2779d64b2b..3cce05b7923ec 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -44,6 +44,7 @@ IncrementalParser::IncrementalParser(CompilerInstance &Instance,
External->StartTranslationUnit(Consumer);
P->Initialize();
+ P->ConsumeToken();
}
IncrementalParser::~IncrementalParser() { P.reset(); }
diff --git a/clang/lib/Parse/ParseAST.cpp b/clang/lib/Parse/ParseAST.cpp
index c8ee625eb57ad..e4222acce97bc 100644
--- a/clang/lib/Parse/ParseAST.cpp
+++ b/clang/lib/Parse/ParseAST.cpp
@@ -131,6 +131,7 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
std::unique_ptr<Parser> ParseOP(
new Parser(S.getPreprocessor(), S, SkipFunctionBodies));
Parser &P = *ParseOP;
+ P.Initialize();
llvm::CrashRecoveryContextCleanupRegistrar<const void, ResetStackCleanup>
CleanupPrettyStack(llvm::SavePrettyStackState());
@@ -160,7 +161,7 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
}
return M;
});
- P.Initialize();
+ P.ConsumeToken();
Parser::DeclGroupPtrTy ADecl;
Sema::ModuleImportState ImportState;
EnterExpressionEvaluationContext PotentiallyEvaluated(
diff --git a/clang/lib/Parse/ParseHLSLRootSignature.cpp b/clang/lib/Parse/ParseHLSLRootSignature.cpp
index 80e81e5c403e1..d31520d916b96 100644
--- a/clang/lib/Parse/ParseHLSLRootSignature.cpp
+++ b/clang/lib/Parse/ParseHLSLRootSignature.cpp
@@ -1557,6 +1557,7 @@ void HandleRootSignatureTarget(Sema &S, StringRef EntryRootSig) {
bool HaveLexer = S.getPreprocessor().getCurrentLexer();
if (HaveLexer) {
P->Initialize();
+ P->ConsumeToken();
S.ActOnStartOfTranslationUnit();
// Skim through the file to parse to find the define
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 5e1fd4df1a3f0..8e8d6ddbefe6b 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -579,9 +579,6 @@ void Parser::Initialize() {
}
Actions.Initialize();
-
- // Prime the lexer look-ahead.
- ConsumeToken();
}
void Parser::DestroyTemplateIds() {
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 25598683d1d62..0cbd25396e7e2 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -286,7 +286,7 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
Decl *Previous, Decl *Canon);
static void attachPreviousDeclImpl(ASTReader &Reader, ...);
static void attachPreviousDecl(ASTReader &Reader, Decl *D, Decl *Previous,
- Decl *Canon);
+ Decl *PreviousNonLocal, Decl *Canon);
static void checkMultipleDefinitionInNamedModules(ASTReader &Reader, Decl *D,
Decl *Previous);
@@ -3646,28 +3646,6 @@ Decl *ASTReader::getMostRecentExistingDecl(Decl *D) {
return ASTDeclReader::getMostRecentDecl(D->getCanonicalDecl());
}
-namespace {
-void mergeInheritableAttributes(ASTReader &Reader, Decl *D, Decl *Previous) {
- InheritableAttr *NewAttr = nullptr;
- ASTContext &Context = Reader.getContext();
- const auto *IA = Previous->getAttr<MSInheritanceAttr>();
-
- if (IA && !D->hasAttr<MSInheritanceAttr>()) {
- NewAttr = cast<InheritableAttr>(IA->clone(Context));
- NewAttr->setInherited(true);
- D->addAttr(NewAttr);
- }
-
- if (!D->hasAttr<AvailabilityAttr>()) {
- for (const auto *AA : Previous->specific_attrs<AvailabilityAttr>()) {
- NewAttr = AA->clone(Context);
- NewAttr->setInherited(true);
- D->addAttr(NewAttr);
- }
- }
-}
-} // namespace
-
template<typename DeclT>
void ASTDeclReader::attachPreviousDeclImpl(ASTReader &Reader,
Redeclarable<DeclT> *D,
@@ -3879,7 +3857,8 @@ void ASTDeclReader::checkMultipleDefinitionInNamedModules(ASTReader &Reader,
}
void ASTDeclReader::attachPreviousDecl(ASTReader &Reader, Decl *D,
- Decl *Previous, Decl *Canon) {
+ Decl *Previous, Decl *PreviousNonLocal,
+ Decl *Canon) {
assert(D && Previous);
switch (D->getKind()) {
@@ -3908,11 +3887,12 @@ void ASTDeclReader::attachPreviousDecl(ASTReader &Reader, Decl *D,
inheritDefaultTemplateArguments(Reader.getContext(),
cast<TemplateDecl>(Previous), TD);
- // If any of the declaration in the chain contains an Inheritable attribute,
- // it needs to be added to all the declarations in the redeclarable chain.
- // FIXME: Only the logic of merging MSInheritableAttr is present, it should
- // be extended for all inheritable attributes.
- mergeInheritableAttributes(Reader, D, Previous);
+ if (PreviousNonLocal) {
+ if (Sema *S = Reader.getSema()) {
+ if (auto *ND = dyn_cast<NamedDecl>(D))
+ S->mergeDeclAttributes(ND, PreviousNonLocal);
+ }
+ }
}
template<typename DeclT>
@@ -4560,17 +4540,17 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
void ASTReader::loadPendingDeclChain(Decl *FirstLocal, uint64_t LocalOffset) {
// Attach FirstLocal to the end of the decl chain.
Decl *CanonDecl = FirstLocal->getCanonicalDecl();
+ Decl *NonLocalMostRecent = nullptr;
if (FirstLocal != CanonDecl) {
Decl *PrevMostRecent = ASTDeclReader::getMostRecentDecl(CanonDecl);
- ASTDeclReader::attachPreviousDecl(
- *this, FirstLocal, PrevMostRecent ? PrevMostRecent : CanonDecl,
- CanonDecl);
+ NonLocalMostRecent = PrevMostRecent ? PrevMostRecent : CanonDecl;
+ ASTDeclReader::attachPreviousDecl(*this, FirstLocal, NonLocalMostRecent,
+ NonLocalMostRecent, CanonDecl);
+ ASTDeclReader::attachLatestDecl(CanonDecl, FirstLocal);
}
- if (!LocalOffset) {
- ASTDeclReader::attachLatestDecl(CanonDecl, FirstLocal);
+ if (!LocalOffset)
return;
- }
// Load the list of other redeclarations from this module file.
ModuleFile *M = getOwningModuleFile(FirstLocal);
@@ -4604,10 +4584,11 @@ void ASTReader::loadPendingDeclChain(Decl *FirstLocal, uint64_t LocalOffset) {
for (unsigned I = 0, N = Record.size(); I != N; ++I) {
unsigned Idx = N - I - 1;
auto *D = ReadDecl(*M, Record, Idx);
- ASTDeclReader::attachPreviousDecl(*this, D, MostRecent, CanonDecl);
+ ASTDeclReader::attachPreviousDecl(*this, D, MostRecent, NonLocalMostRecent,
+ CanonDecl);
MostRecent = D;
+ ASTDeclReader::attachLatestDecl(CanonDecl, MostRecent);
}
- ASTDeclReader::attachLatestDecl(CanonDecl, MostRecent);
}
namespace {
diff --git a/clang/test/Modules/decl-attr-merge-explicit-modules.c b/clang/test/Modules/decl-attr-merge-explicit-modules.c
new file mode 100644
index 0000000000000..5b31171a3404f
--- /dev/null
+++ b/clang/test/Modules/decl-attr-merge-explicit-modules.c
@@ -0,0 +1,79 @@
+// Check merging attributes when modules are built explicitly.
+//
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -triple arm64-apple-macosx10.7.0 -I%t/headers \
+// RUN: -fmodule-name=first -xc %t/headers/first.modulemap -emit-module -o %t/first.pcm
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -triple arm64-apple-macosx10.7.0 -I%t/headers \
+// RUN: -fmodule-name=second -xc %t/headers/second.modulemap -emit-module -o %t/second.pcm
+
+// Without module names.
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -triple arm64-apple-macosx10.7.0 -I%t/headers \
+// RUN: -fmodule-file=%t/first.pcm -fmodule-file=%t/second.pcm -fsyntax-only %t/test.c -verify
+// With module names.
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -triple arm64-apple-macosx10.7.0 -I%t/headers \
+// RUN: -fmodule-file=first=%t/first.pcm -fmodule-file=second=%t/second.pcm -fsyntax-only %t/test.c -verify
+
+// Reverse order.
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -triple arm64-apple-macosx10.7.0 -I%t/headers \
+// RUN: -fmodule-file=%t/second.pcm -fmodule-file=%t/first.pcm -fsyntax-only %t/test-reverse.c -verify
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -triple arm64-apple-macosx10.7.0 -I%t/headers \
+// RUN: -fmodule-file=second=%t/second.pcm -fmodule-file=first=%t/first.pcm -fsyntax-only %t/test-reverse.c -verify
+
+// With a transitive module dependency.
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -triple arm64-apple-macosx10.7.0 -I%t/headers \
+// RUN: -fmodule-file=first=%t/first.pcm \
+// RUN: -fmodule-name=second_transitive -xc %t/headers/second-transitive.modulemap -emit-module -o %t/second-transitive.pcm
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -triple arm64-apple-macosx10.7.0 -I%t/headers \
+// RUN: -fmodule-file=%t/second-transitive.pcm -fsyntax-only %t/test-transitive.c -verify
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -triple arm64-apple-macosx10.7.0 -I%t/headers \
+// RUN: -fmodule-file=second_transitive=%t/second-transitive.pcm -fsyntax-only %t/test-transitive.c -verify
+
+//--- headers/first.h
+// Added "used" attribute to add corresponding `FunctionDecl` to `EagerlyDeserializedDecls`.
+void availabilityAttr(void) __attribute__((used)) __attribute__((availability(macos,unavailable)));
+//--- headers/first.modulemap
+module first {
+ header "first.h" export *
+}
+
+//--- headers/second.h
+void availabilityAttr(void) __attribute__((used)) __attribute__((availability(ios,introduced=4.0)));
+//--- headers/second.modulemap
+module second {
+ header "second.h" export *
+}
+
+//--- headers/second-transitive.h
+#include <first.h>
+void availabilityAttr(void) __attribute__((availability(ios,introduced=4.0)));
+//--- headers/second-transitive.modulemap
+module second_transitive {
+ header "second-transitive.h" export *
+}
+
+//--- test.c
+#include <first.h>
+#include <second.h>
+void test(void) {
+ availabilityAttr();
+ // expected-error at -1 {{'availabilityAttr' is unavailable: not available on macOS}}
+ // expected-note at first.h:* {{'availabilityAttr' has been explicitly marked unavailable here}}
+}
+
+//--- test-reverse.c
+#include <second.h>
+#include <first.h>
+void test(void) {
+ availabilityAttr();
+ // expected-error at -1 {{'availabilityAttr' is unavailable: not available on macOS}}
+ // expected-note at first.h:* {{'availabilityAttr' has been explicitly marked unavailable here}}
+}
+
+//--- test-transitive.c
+#include <second-transitive.h>
+void test(void) {
+ availabilityAttr();
+ // expected-error at -1 {{'availabilityAttr' is unavailable: not available on macOS}}
+ // expected-note at first.h:* {{'availabilityAttr' has been explicitly marked unavailable here}}
+}
diff --git a/clang/test/Modules/decl-attr-merge2.c b/clang/test/Modules/decl-attr-merge2.c
new file mode 100644
index 0000000000000..fc84b9df70171
--- /dev/null
+++ b/clang/test/Modules/decl-attr-merge2.c
@@ -0,0 +1,38 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps \
+// RUN: -fmodules-cache-path=%t/mcache -triple arm64-apple-macosx10.7.0 \
+// RUN: -I%t/headers -fsyntax-only %t/test.c -verify
+
+// Check more cases of attribute merging across multiple modules.
+
+//--- headers/module.modulemap
+module First {
+ header "first.h" export *
+}
+module Second {
+ header "second.h" export *
+}
+//--- headers/first.h
+void additiveAttr(void) __attribute__((availability(macos,unavailable)));
+void exclusiveAttr(void) __attribute__((hot));
+
+//--- headers/second.h
+void additiveAttr(void) __attribute__((availability(ios,introduced=4.0)));
+void exclusiveAttr(void) __attribute__((cold));
+
+//--- test.c
+#include <first.h>
+#include <second.h>
+
+void test(void) {
+ // Check the attribute from "second.h" doesn't hide the attribute from "first.h".
+ additiveAttr();
+ // expected-error at -1 {{'additiveAttr' is unavailable: not available on macOS}}
+ // expected-note at first.h:* {{'additiveAttr' has been explicitly marked unavailable here}}
+
+ // Check calling a function with `MutualExclusions` attributes.
+ exclusiveAttr();
+ // expected-error at second.h:* {{'cold' and 'hot' attributes are not compatible}}
+ // expected-note at first.h:* {{conflicting attribute is here}}
+}
diff --git a/clang/test/OpenMP/declare_variant_construct_codegen_1.c b/clang/test/OpenMP/declare_variant_construct_codegen_1.c
index 5e659f05773d1..fbe36d2a31840 100644
--- a/clang/test/OpenMP/declare_variant_construct_codegen_1.c
+++ b/clang/test/OpenMP/declare_variant_construct_codegen_1.c
@@ -28,8 +28,8 @@
void p_vxv(int *v1, int *v2, int *v3, int n);
void t_vxv(int *v1, int *v2, int *v3, int n);
-#pragma omp declare variant(t_vxv) match(construct={target})
#pragma omp declare variant(p_vxv) match(construct={parallel})
+#pragma omp declare variant(t_vxv) match(construct={target})
void vxv(int *v1, int *v2, int *v3, int n) {
for (int i = 0; i < n; i++) v3[i] = v1[i] * v2[i];
}
>From cc7c85673936af42694117faada99dc1bb3d2e6d Mon Sep 17 00:00:00 2001
From: Volodymyr Sapsai <vsapsai at apple.com>
Date: Thu, 9 Jul 2026 18:15:38 -0700
Subject: [PATCH 2/2] Update Parser initialization in LLDB, specifically in
`ClangModulesDeclVendor`.
---
.../Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp
index 3784acd511095..05bf869c46f3d 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp
@@ -789,9 +789,10 @@ ClangModulesDeclVendor::Create(Target &target) {
const bool skipFunctionBodies = false;
std::unique_ptr<clang::Parser> parser(new clang::Parser(
instance->getPreprocessor(), instance->getSema(), skipFunctionBodies));
+ parser->Initialize();
instance->getPreprocessor().EnterMainSourceFile();
- parser->Initialize();
+ parser->ConsumeToken();
clang::Parser::DeclGroupPtrTy parsed;
auto ImportState = clang::Sema::ModuleImportState::NotACXX20Module;
More information about the cfe-commits
mailing list