Skip to content

Commit 4d90c8c

Browse files
Thoemi09parcollet
andauthored
Allow friend functions to be wrapped (#21)
* Exclude friend functions in the matcher not the AST consumer * Use unqualified function call for inline friends * Exclude h5_write, h5_read and hdf5_format * Fix issue with friend functions defined inside class templates * Add fmt formatter for StringRef --------- Co-authored-by: Olivier Parcollet <[email protected]>
1 parent 343c37e commit 4d90c8c

5 files changed

Lines changed: 44 additions & 8 deletions

File tree

src/tools/clair-c2py/ast_consumer.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,17 @@ void ast_consumer::HandleTranslationUnit(clang::ASTContext &ctx) {
103103
return add_match_files(l);
104104
};
105105
// Final call of the chain, for classes, enums and functions
106-
// Function are special, as we have to exclude methods and friend declarations
106+
// Functions are special: exclude methods. Friend declarations (both inline
107+
// definitions and out-of-class declarations) are matched; deduplication
108+
// against out-of-class definitions is handled in the Fnt matcher callback.
107109
auto call_cls = [&](auto... x) {
108110
return cxxRecordDecl(std::move(x)...); //excludes);
109111
};
110112
auto call_enum = [&](auto... x) {
111113
return enumDecl(std::move(x)...); //excludes);
112114
};
113115
auto call_fun = [&](auto... x) {
114-
auto excludes = unless(anyOf(cxxMethodDecl(), hasAncestor(friendDecl())));
115-
return functionDecl(std::move(x)..., excludes);
116+
return functionDecl(std::move(x)..., unless(cxxMethodDecl()));
116117
};
117118

118119
// ------- Match the AST in two passes

src/tools/clair-c2py/codegen/fnt.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,14 @@ void codegen::write_dispatch(std::ostream &code, std::ostream &table, std::ostre
6969
auto parent_cls_name = (not cls_alias.empty()) ? cls_alias : (parent_class ? clu::get_fully_qualified_name(parent_class) : str_t{});
7070

7171
auto l = [&enforce_method, parent_class, &parent_cls_name](fnt_info_t const &f_info) {
72-
auto *f = f_info.ptr;
73-
auto *m = llvm::dyn_cast_or_null<clang::CXXMethodDecl>(f);
74-
auto args = fnt_params_with_default(f);
75-
auto fname = (m and parent_class ? parent_cls_name + "::" + f->getNameAsString() : f->getQualifiedNameAsString());
72+
auto *f = f_info.ptr;
73+
auto *m = llvm::dyn_cast_or_null<clang::CXXMethodDecl>(f);
74+
auto args = fnt_params_with_default(f);
75+
// Inline friend functions (defined inside a class body) are only found via ADL,
76+
// so they must be called unqualified. They are not CXXMethodDecls.
77+
bool is_inline_friend = (not m) and f->getFriendObjectKind() != clang::Decl::FOK_None;
78+
auto fname = (m and parent_class ? parent_cls_name + "::" + f->getNameAsString() :
79+
(is_inline_friend ? f->getNameAsString() : f->getQualifiedNameAsString()));
7680
auto fname_log = (m and parent_class ? clu::get_fully_qualified_name(parent_class) + "::" + f->getNameAsString() : f->getQualifiedNameAsString());
7781

7882
auto cfun_or_cmethod = std::string{enforce_method and (not m) ? "cmethod" : "cfun"};

src/tools/clair-c2py/matchers.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,18 +186,39 @@ template <> void matcher<mtch::Fnt>::run(const MatchResult &Result) {
186186
// Reject function template declaration
187187
if (f->getDescribedFunctionTemplate()) return;
188188

189+
// Reject functions with dependent (unresolved) types.
190+
// This filters out the pattern FunctionDecl of friend functions defined inside
191+
// class templates whose types still contain template parameters like `type-parameter-0-0`.
192+
if (f->getType()->isDependentType()) return;
193+
189194
// method should not be here
190195
EXPECTS(not llvm::isa<clang::CXXMethodDecl>(f));
191196

197+
// Deduplicate friend declarations vs out-of-class definitions.
198+
// If this is a friend declaration (not a definition) and the definition
199+
// exists in this TU, skip — the definition will be matched separately.
200+
// If no definition is visible (defined in another .cpp), keep the declaration.
201+
if (f->getFriendObjectKind() != clang::Decl::FOK_None and not f->isThisDeclarationADefinition())
202+
if (f->getDefinition()) return;
203+
204+
// Wrapping c2py functions makes no sense, and is probably a mistake in the configuration or includes. Panic...
192205
if (f->getQualifiedNameAsString().starts_with("c2py::")) {
193-
logs.error("FATAL ERROR: incorrect configuration or includes. It requests wrapping c2py functions which makes no sense.");
206+
logs.error("FATAL ERROR: incorrect configuration or includes. It requests wrapping c2py functions, which makes no sense.");
194207
std::abort();
195208
}
196209

197210
// apply c2py_ignore and the reject_name regex
198211
auto &M = wdata->module_info;
199212
if (should_reject(f, wdata->reject_names, &logs.rejected)) return;
200213

214+
// h5_write/h5_read/h5_read_construct are HDF5 serialization helpers, never meant to be wrapped
215+
// if we use the h5 method.
216+
if (wdata->concepts.HasHdf5)
217+
if (auto name = f->getName(); name == "h5_write" || name == "h5_read" || name == "h5_read_construct") {
218+
logs.rejected(fmt::format(R"RAW({0} [treated directly in h5 support])RAW", name));
219+
return;
220+
}
221+
201222
// Special treatment for operator
202223
if (f->getNameAsString().starts_with("operator")) {
203224
analyze_operator(f, *wdata);

src/tools/clair-c2py/scan_classes.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ static void analyze_one_method(clang::FunctionDecl const *f, cls_info_t &cls_inf
5555

5656
auto name = m->getNameAsString();
5757

58+
// hdf5_format is an HDF5 serialization helper, never meant to be wrapped
59+
if (wd.concepts.HasHdf5 and name == "hdf5_format") return;
60+
5861
// ---- constructors
5962
if (llvm::isa<clang::CXXConstructorDecl>(m)) {
6063
const bool is_base_class = (cls != cls_info.ptr);

src/utility/logger.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,16 @@
55
#include <utility>
66
#include <fmt/core.h>
77
#include <fmt/format.h>
8+
#include <llvm/ADT/StringRef.h>
89

910
#include "string_tools.hpp"
1011

12+
template <> struct fmt::formatter<llvm::StringRef> : fmt::formatter<std::string_view> {
13+
auto format(llvm::StringRef s, fmt::format_context &ctx) const {
14+
return fmt::formatter<std::string_view>::format({s.data(), s.size()}, ctx);
15+
}
16+
};
17+
1118
namespace util {
1219
class logger {
1320

0 commit comments

Comments
 (0)