Account for out-of-line declarations in members_of.

This commit is contained in:
Dan Katz
2025-06-10 10:33:58 -04:00
parent 59f41178cb
commit 5b709111a9
5 changed files with 105 additions and 19 deletions

View File

@@ -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);
}

View File

@@ -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<Decl*>(this)->getPrevMultDCDeclInSemaContext();
}
DeclContext *getDeclContext() {
if (isInSemaDC())
return getSemanticDC();

View File

@@ -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<NamespaceDecl>(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<CXXRecordDecl>(this))

View File

@@ -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<NamespaceAliasDecl>(D))
@@ -1355,7 +1364,6 @@ static bool isReflectableDecl(MetaActions &Meta, ASTContext &C, Decl *D) {
if (auto *FD = dyn_cast<FunctionDecl>(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<NamespaceDecl>(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<NamespaceDecl>(DC)) {
while (!D && NSDecl) {
NSDecl = NSDecl->getPreviousDecl();
D = NSDecl ? *NSDecl->decls_begin() : nullptr;
}
if (!D) {
auto *Canonical = cast<NamespaceDecl>(DC->getPrimaryContext());
D = Canonical->getLastMultDCSemaDecl();
}
}
} else {
D = D->getPrevMultDCDeclInSemaContext();
}
// We need to recursively descend into LinkageSpecDecls to iterate over the

View File

@@ -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();
}