diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 76a87877187c..137e9dd1d49e 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -559,6 +559,11 @@ class NamespaceDecl : public NamedDecl, /// The unnamed namespace that inhabits this namespace, if any. NamespaceDecl *AnonymousNamespace = nullptr; + /// The most recent Decl whose target scope is this namespace, but whose + /// lexical scope is another DeclContext. Used to traverse namespace members + /// (i.e., for reflection). + Decl *LastMultDCSemaDecl = nullptr; + protected: NamespaceDecl(Kind K, ASTContext &C, DeclContext *DC, bool Inline, SourceLocation StartLoc, SourceLocation IdLoc, @@ -645,6 +650,9 @@ public: NamespaceDecl *getCanonicalDecl() override { return getFirstDecl(); } const NamespaceDecl *getCanonicalDecl() const { return getFirstDecl(); } + Decl *getLastMultDCSemaDecl() { return LastMultDCSemaDecl; } + void setLastMultDCSemaDecl(Decl *D) { LastMultDCSemaDecl = D; } + SourceRange getSourceRange() const override LLVM_READONLY { return SourceRange(LocStart, RBraceLoc); } diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 63d7c0e1e7e7..b91c6494538e 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -254,6 +254,8 @@ private: struct MultipleDC { DeclContext *SemanticDC; DeclContext *LexicalDC; + + Decl *PrevMultDCSemaDecl; }; /// DeclCtx - Holds either a DeclContext* or a MultipleDC*. @@ -448,6 +450,11 @@ public: Decl *getNextDeclInContext() { return NextInContextAndBits.getPointer(); } const Decl *getNextDeclInContext() const {return NextInContextAndBits.getPointer();} + Decl *getPrevMultDCDeclInSemaContext(); + const Decl *getPrevMultDCDeclInSemaContext() const { + return const_cast(this)->getPrevMultDCDeclInSemaContext(); + } + DeclContext *getDeclContext() { if (isInSemaDC()) return getSemanticDC(); diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 27910f26fd08..6c5553d75863 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -403,6 +403,7 @@ void Decl::setDeclContextsImpl(DeclContext *SemaDC, DeclContext *LexicalDC, auto *MDC = new (Ctx) Decl::MultipleDC(); MDC->SemanticDC = SemaDC; MDC->LexicalDC = LexicalDC; + MDC->PrevMultDCSemaDecl = nullptr; DeclCtx = MDC; } } @@ -1237,6 +1238,11 @@ bool Decl::isFunctionPointerType() const { return Ty.getCanonicalType()->isFunctionPointerType(); } +Decl *Decl::getPrevMultDCDeclInSemaContext() { + assert(!isInSemaDC()); + return getMultipleDC()->PrevMultDCSemaDecl; +} + DeclContext *Decl::getNonTransparentDeclContext() { assert(getDeclContext()); return getDeclContext()->getNonTransparentContext(); @@ -1782,6 +1788,14 @@ void DeclContext::addHiddenDecl(Decl *D) { FirstDecl = LastDecl = D; } + if (auto *NS = dyn_cast(D->getDeclContext()); + NS && !D->isInSemaDC()) { + NS = NS->getCanonicalDecl(); + + D->getMultipleDC()->PrevMultDCSemaDecl = NS->getLastMultDCSemaDecl(); + NS->setLastMultDCSemaDecl(D); + } + // Notify a C++ record declaration that we've added a member, so it can // update its class-specific state. if (auto *Record = dyn_cast(this)) diff --git a/clang/lib/AST/ExprConstantMeta.cpp b/clang/lib/AST/ExprConstantMeta.cpp index a755348894ed..c9b6da3b8a14 100644 --- a/clang/lib/AST/ExprConstantMeta.cpp +++ b/clang/lib/AST/ExprConstantMeta.cpp @@ -1334,7 +1334,16 @@ static bool ensureDeclared(ASTContext &C, QualType QT, SourceLocation SpecLoc) { static bool isReflectableDecl(MetaActions &Meta, ASTContext &C, Decl *D) { assert(D && "null declaration"); - if (D != D->getCanonicalDecl()) + if (D != D->getCanonicalDecl()) { + Decl *First = nullptr; + for (Decl *I = D->getMostRecentDecl(); I; I = I->getPreviousDecl()) + if (I->getLexicalDeclContext() == D->getLexicalDeclContext()) + First = I; + if (D != First) + return false; + } + + if (D->isLocalExternDecl()) return false; if (isa(D)) @@ -1355,7 +1364,6 @@ static bool isReflectableDecl(MetaActions &Meta, ASTContext &C, Decl *D) { if (auto *FD = dyn_cast(D)) { for (auto *R = FD->getMostRecentDecl(); R; R = R->getPreviousDecl()) { if (!R->getDeclaredReturnType()->isUndeducedType() && - R->getDeclContext() == R->getLexicalDeclContext() && Meta.HasSatisfiedConstraints(R)) return true; } @@ -1386,26 +1394,35 @@ static Decl *findIterableMember(MetaActions &Meta, ASTContext &C, Decl *D, } do { - DeclContext *DC = D->getDeclContext(); + DeclContext *DC = D->getDeclContext(); // note: SemanticDC - // Get the next declaration in the DeclContext. - // - // Explicit specializations of templates are created with the DeclContext of - // the template from which they're instantiated, but they end up in the - // DeclContext within which they're declared. We therefore skip over any - // declarations whose DeclContext is different from the previous Decl; - // otherwise, we may inadvertently break the chain of redeclarations in - // difficult to predit ways. - do { - D = D->getNextDeclInContext(); - } while (D && D->getDeclContext() != DC); + if (D->getLexicalDeclContext() == DC) { + // Get the next declaration in the DeclContext. + // + // Explicit specializations of templates are created with the DeclContext + // of the template from which they're instantiated, but they end up in the + // DeclContext within which they're declared. We therefore skip over any + // declarations whose DeclContext is different from the previous Decl; + // otherwise, we may inadvertently break the chain of redeclarations in + // difficult to predit ways. + do { + D = D->getNextDeclInContext(); + } while (D && D->getDeclContext() != DC); - // In the case of namespaces, walk the redeclaration chain. - if (auto *NSDecl = dyn_cast(DC)) { - while (!D && NSDecl) { - NSDecl = NSDecl->getPreviousDecl(); - D = NSDecl ? *NSDecl->decls_begin() : nullptr; + // In the case of namespaces, walk the redeclaration chain. + if (auto *NSDecl = dyn_cast(DC)) { + while (!D && NSDecl) { + NSDecl = NSDecl->getPreviousDecl(); + D = NSDecl ? *NSDecl->decls_begin() : nullptr; + } + + if (!D) { + auto *Canonical = cast(DC->getPrimaryContext()); + D = Canonical->getLastMultDCSemaDecl(); + } } + } else { + D = D->getPrevMultDCDeclInSemaContext(); } // We need to recursively descend into LinkageSpecDecls to iterate over the diff --git a/libcxx/test/std/experimental/reflection/members-and-subobjects.pass.cpp b/libcxx/test/std/experimental/reflection/members-and-subobjects.pass.cpp index 32cf1a4c6378..5d1a08730921 100644 --- a/libcxx/test/std/experimental/reflection/members-and-subobjects.pass.cpp +++ b/libcxx/test/std/experimental/reflection/members-and-subobjects.pass.cpp @@ -435,6 +435,46 @@ static_assert(members_of(^^NS, } // namespace deduced_return_type + // ======================== + // out_of_line_declarations + // ======================== + +namespace out_of_line_declarations { +constexpr auto ctx = std::meta::access_context::current(); + +namespace NS1 { +struct A; + +} // namespace NS1 + +struct NS1::A { +static void f(class C *); +static auto g(); +}; +void NS1::A::f(NS1::C *) {} +void gq(NS1::C *) {} + +static_assert( + *std::ranges::find( + [](const auto &r) -> const auto & { return r; }( + members_of(^^NS1, ctx)), + ^^NS1::C) == + ^^NS1::C); + +namespace NS2 { +namespace Inner { +struct S { + friend void f(); +}; +} // namespace Inner +void Inner::f() {} + +} // namespace NS2 + +static_assert(1 == members_of(^^NS2, ctx).size()); +static_assert(2 == members_of(^^NS2::Inner, ctx).size()); +} // namespace out_of_line_declarations + int main() { class_members::usage_example(); }