Skip to content

Commit

Permalink
Property return extern "C" things through 'members_of'.
Browse files Browse the repository at this point in the history
Closes issue llvm#82.
  • Loading branch information
katzdm committed Aug 6, 2024
1 parent f26b7a0 commit b62d82c
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 8 deletions.
42 changes: 34 additions & 8 deletions clang/lib/Sema/Metafunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1071,24 +1071,37 @@ static bool ensureDeclared(Sema &S, QualType QT, SourceLocation SpecLoc) {

static bool isReflectableDecl(ASTContext &C, const Decl *D) {
assert(D && "null declaration");
if (isa<AccessSpecDecl>(D) || isa<EmptyDecl>(D) || isa<FriendDecl>(D))
return false;

if (isa<NamespaceAliasDecl>(D))
return true;

if (!isa<VarDecl, FunctionDecl, TypeDecl, FieldDecl, TemplateDecl,
NamespaceDecl, NamespaceAliasDecl, TranslationUnitDecl>(D))
return false;

if (auto *Class = dyn_cast<CXXRecordDecl>(D))
if (Class->isInjectedClassName())
return false;
if (isa<StaticAssertDecl>(D))
return false;

return D->getCanonicalDecl() == D;
}

/// Filter non-reflectable members.
static Decl *findIterableMember(ASTContext &C, Decl *D, bool Inclusive) {
if (!D || (Inclusive && isReflectableDecl(C, D)))
if (!D)
return D;

if (Inclusive) {
if (isReflectableDecl(C, D))
return D;

// Handle the case where the first Decl is a LinkageSpecDecl.
if (auto *LSDecl = dyn_cast_or_null<LinkageSpecDecl>(D)) {
Decl *RecD = findIterableMember(C, *LSDecl->decls_begin(), true);
if (RecD) return RecD;
}
}

do {
DeclContext *DC = D->getDeclContext();

Expand All @@ -1100,17 +1113,30 @@ static Decl *findIterableMember(ASTContext &C, Decl *D, bool Inclusive) {
// declarations whose DeclContext is different from the previous Decl;
// otherwise, we may inadvertently break the chain of redeclarations in
// difficult to predit ways.
D = D->getNextDeclInContext();
while (D && D->getDeclContext() != DC)
D = D->getNextDeclInContext();
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;
}
}

// We need to recursively descend into LinkageSpecDecls to iterate over the
// members declared therein (e.g., `extern "C"` blocks).
if (auto *LSDecl = dyn_cast_or_null<LinkageSpecDecl>(D)) {
Decl *RecD = findIterableMember(C, *LSDecl->decls_begin(), true);
if (RecD) return RecD;
}

// Pop back out of a recursively entered LinkageSpecDecl.
if (!D && isa<LinkageSpecDecl>(DC))
return findIterableMember(C, cast<Decl>(DC), false);
} while (D && !isReflectableDecl(C, D));

return D;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,48 @@ static_assert((members_of(^myns) | std::views::filter(std::meta::is_template) |
std::vector{^myns::TAlias});
} // namespace_members

// ===========================
// language_linkage_specifiers
// ===========================

namespace language_linkage_specifiers {
namespace with_empty {
extern "C" { }
} // namespace with_empty

namespace leading_node {
extern "C" {
void fn1();
} // extern "C"

void fn2();
} // namespace leading_node

namespace multiple_blocks {
extern "C" {
void fn3();
} // extern "C"

void fn4();

extern "C" {
void fn5();
} // extern "C"
} // namespace multiple_blocks

namespace multiple_blocks {
void fn6();

extern "C" {
void fn7();
}
} // namespace multiple_blocks

static_assert(members_of(^with_empty).size() == 0);
static_assert(members_of(^leading_node).size() == 2);
static_assert(members_of(^multiple_blocks).size() == 5);
} // namespace language_linkage_specifiers

// ==========================
// inaccessible_class_members
// ==========================
Expand Down

0 comments on commit b62d82c

Please sign in to comment.