From 9fb200b08bc586f064bf26f97a636a0ea0d0dbf5 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 2 Oct 2026 19:43:33 +0100 Subject: [PATCH 01/34] Guard by standard --- rules/cstddef/src.cpp | 4 ++++ rules/iomanip/src.cpp | 2 ++ rules/new/src.cpp | 2 ++ rules/pair/src.cpp | 2 ++ rules/string/src.cpp | 2 ++ rules/unique_ptr/src.cpp | 4 ++++ 6 files changed, 16 insertions(+) diff --git a/rules/cstddef/src.cpp b/rules/cstddef/src.cpp index b53725844b..aa6fec4f3a 100644 --- a/rules/cstddef/src.cpp +++ b/rules/cstddef/src.cpp @@ -4,7 +4,9 @@ #include #include +#if __cplusplus >= 201703L using t1 = std::byte; +#endif typedef size_t t2; typedef size_t *t3; @@ -14,6 +16,7 @@ typedef ssize_t t6; typedef ssize_t *t7; typedef const ssize_t *t8; +#if __cplusplus >= 201703L std::byte f1(const std::byte &a0, unsigned a1) { return operator<<(a0, a1); } std::byte f2(const std::byte &a0, unsigned a1) { return operator>>(a0, a1); } @@ -21,3 +24,4 @@ std::byte f2(const std::byte &a0, unsigned a1) { return operator>>(a0, a1); } std::byte f3(std::byte &a0, unsigned a1) { return operator<<=(a0, a1); } std::byte f4(std::byte &a0, unsigned a1) { return operator>>=(a0, a1); } +#endif diff --git a/rules/iomanip/src.cpp b/rules/iomanip/src.cpp index 3140233256..2a1a628ecf 100644 --- a/rules/iomanip/src.cpp +++ b/rules/iomanip/src.cpp @@ -3,4 +3,6 @@ #include +#if __cplusplus >= 201402L auto f1(int n) { return std::setw(n); } +#endif diff --git a/rules/new/src.cpp b/rules/new/src.cpp index ea1a12ed09..fe9fd99ccb 100644 --- a/rules/new/src.cpp +++ b/rules/new/src.cpp @@ -11,4 +11,6 @@ void *f3(std::size_t a0) { return ::operator new[](a0); } void f4(void *a0) { return ::operator delete[](a0); } +#if __cplusplus >= 201703L template T1 *f5(T1 *a0) { return std::launder(a0); } +#endif diff --git a/rules/pair/src.cpp b/rules/pair/src.cpp index dbf2fbf50a..087cf18c4b 100644 --- a/rules/pair/src.cpp +++ b/rules/pair/src.cpp @@ -34,6 +34,7 @@ std::pair f7(T3 &&a0, T4 &&a1) { return std::pair(std::move(a0), std::move(a1)); } +#if __cplusplus >= 201402L template auto f9(T1 &&a0, T2 &a1) { return std::make_pair(std::move(a0), a1); } @@ -41,6 +42,7 @@ template auto f9(T1 &&a0, T2 &a1) { template auto f10(T1 &&a0, T2 &&a1) { return std::make_pair(std::move(a0), std::move(a1)); } +#endif template T1 &f11(std::pair &a0) { return a0.first; diff --git a/rules/string/src.cpp b/rules/string/src.cpp index 276962df85..ea6f3a3a26 100644 --- a/rules/string/src.cpp +++ b/rules/string/src.cpp @@ -23,7 +23,9 @@ std::string &f4(std::string &s, const char *p, std::size_t n) { const char *f5(const std::string &s) { return s.c_str(); } +#if __cplusplus >= 201703L char *f6(std::string &s) { return s.data(); } +#endif std::string f7(const char *s, std::size_t n) { return std::string(s, n); } diff --git a/rules/unique_ptr/src.cpp b/rules/unique_ptr/src.cpp index 8f7c5458a3..98cd44181a 100644 --- a/rules/unique_ptr/src.cpp +++ b/rules/unique_ptr/src.cpp @@ -9,9 +9,11 @@ template using Init = A; template using t1 = std::unique_ptr; template using t2 = std::unique_ptr; +#if __cplusplus >= 201402L template std::unique_ptr f1(std::size_t n) { return std::make_unique(n); } +#endif template T1 *f2(std::unique_ptr &o) { return o.get(); } @@ -33,10 +35,12 @@ template void f6(std::unique_ptr &o, T1 *p) { template T1 *f7(std::unique_ptr &o) { return o.get(); } +#if __cplusplus >= 201402L template std::unique_ptr f8(Init &&...args) { return std::make_unique(std::forward(args)...); } +#endif template void f9(std::unique_ptr &o) { return o.reset(nullptr); From 1c351031cef1d827e15a4b50681c67ab029b2c77 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 2 Oct 2026 19:45:52 +0100 Subject: [PATCH 02/34] Include rules at the end of the TU --- cpp2rust/CMakeLists.txt | 1 + cpp2rust/cpp2rust_lib.cpp | 40 +++++++++- cpp2rust/frontend_action.h | 34 +++++++++ cpp2rust/rules_prelude.cpp | 150 +++++++++++++++++++++++++++++++++++++ cpp2rust/rules_prelude.h | 23 ++++++ 5 files changed, 244 insertions(+), 4 deletions(-) create mode 100644 cpp2rust/rules_prelude.cpp create mode 100644 cpp2rust/rules_prelude.h diff --git a/cpp2rust/CMakeLists.txt b/cpp2rust/CMakeLists.txt index 1b7825e778..d7adc64b10 100644 --- a/cpp2rust/CMakeLists.txt +++ b/cpp2rust/CMakeLists.txt @@ -17,6 +17,7 @@ target_compile_definitions(cpp2rust_core PUBLIC "-DCLANG_C_COMPILER=\"${CMAKE_C_ target_compile_definitions(cpp2rust_core PUBLIC "-DCLANG_CXX_COMPILER=\"${CMAKE_CXX_COMPILER}\"") target_compile_definitions(cpp2rust_core PUBLIC "-DCLANG_RESOURCE_DIR=\"${LLVM_LIBRARY_DIR}/clang/${LLVM_VERSION_MAJOR}\"") target_compile_definitions(cpp2rust_core PUBLIC "-DCOMPAT_INCLUDE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/compat\"") +target_compile_definitions(cpp2rust_core PUBLIC "-DRULES_SOURCE_DIR=\"${PROJECT_SOURCE_DIR}/rules\"") target_compile_definitions(cpp2rust_core PUBLIC "-DRUST_STABLE_VERSION=\"${RUST_STABLE_VERSION}\"") if (APPLE) diff --git a/cpp2rust/cpp2rust_lib.cpp b/cpp2rust/cpp2rust_lib.cpp index 48754e5cf3..8271742d3d 100644 --- a/cpp2rust/cpp2rust_lib.cpp +++ b/cpp2rust/cpp2rust_lib.cpp @@ -8,11 +8,14 @@ #include #include +#include +#include #include "compat/platform_flags.h" #include "converter/converter.h" #include "converter/models/converter_refcount.h" #include "frontend_action.h" +#include "rules_prelude.h" namespace cpp2rust { std::string TranspileSrc(std::string_view cc_code, Model model, @@ -20,17 +23,23 @@ std::string TranspileSrc(std::string_view cc_code, Model model, const std::string &rules_dir, std::string_view filename) { auto tool_args = getPlatformClangBeginFlags(); + tool_args.push_back("-fno-spell-checking"); tool_args.push_back("-fparse-all-comments"); tool_args.insert(tool_args.end(), cxx_flags.begin(), cxx_flags.end()); auto end_flags = getPlatformClangEndFlags(); tool_args.insert(tool_args.end(), end_flags.begin(), end_flags.end()); + auto language = GetRulesLanguage(filename); + auto code = std::string(cc_code) + GetRulesPreludeInclude(language); + std::string rs_code; clang::tooling::runToolOnCodeWithArgs( std::make_unique(rs_code, model, /*first=*/true, rules_dir), - cc_code, tool_args, std::filesystem::path(filename).filename().string(), - filename.ends_with(".c") ? CLANG_C_COMPILER : CLANG_CXX_COMPILER); + code, tool_args, std::filesystem::path(filename).filename().string(), + filename.ends_with(".c") ? CLANG_C_COMPILER : CLANG_CXX_COMPILER, + std::make_shared(), + {{GetRulesPreludePath(language), BuildRulesPrelude(language)}}); Converter::EmitOpaqueRecords(rs_code); Converter::EmitVirtualMethods(rs_code); if (model == Model::kRefCount) { @@ -55,9 +64,10 @@ std::string TranspileDir(std::string_view build_dir, Model model, } clang::tooling::ClangTool Tool(*compile_dbase, files); + auto begin_flags = getPlatformClangBeginFlags(); + begin_flags.push_back("-fno-spell-checking"); Tool.appendArgumentsAdjuster(clang::tooling::getInsertArgumentAdjuster( - getPlatformClangBeginFlags(), - clang::tooling::ArgumentInsertPosition::BEGIN)); + begin_flags, clang::tooling::ArgumentInsertPosition::BEGIN)); Tool.appendArgumentsAdjuster(clang::tooling::getInsertArgumentAdjuster( getPlatformClangEndFlags(), clang::tooling::ArgumentInsertPosition::END)); // Redefine __FILE__ to use just the basename, so the generated code @@ -73,6 +83,28 @@ std::string TranspileDir(std::string_view build_dir, Model model, return result; }); + std::vector mapped; + mapped.reserve(2 * compile_dbase->getAllCompileCommands().size() + 4); + auto map = [&](std::string path, std::string content) { + mapped.push_back(std::move(path)); + mapped.push_back(std::move(content)); + Tool.mapVirtualFile(mapped[mapped.size() - 2], mapped.back()); + }; + for (auto language : {RulesLanguage::kC, RulesLanguage::kCxx}) { + map(GetRulesPreludePath(language), BuildRulesPrelude(language)); + } + for (const auto &compile_command : compile_dbase->getAllCompileCommands()) { + std::filesystem::path path(compile_command.Filename); + if (path.is_relative()) { + path = std::filesystem::path(compile_command.Directory) / path; + } + std::ifstream file(path); + std::string content{std::istreambuf_iterator(file), + std::istreambuf_iterator()}; + map(path.string(), + content + GetRulesPreludeInclude(GetRulesLanguage(path.string()))); + } + std::string rs_code; FrontendActionFactory factory(rs_code, model, rules_dir); Tool.run(&factory); diff --git a/cpp2rust/frontend_action.h b/cpp2rust/frontend_action.h index 5aae8a0250..d018de99d5 100644 --- a/cpp2rust/frontend_action.h +++ b/cpp2rust/frontend_action.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include @@ -13,8 +15,34 @@ #include "ast_consumer.h" #include "converter/factory.h" +#include "rules_prelude.h" namespace cpp2rust { +class SilenceRulesDiagnostics : public clang::PPCallbacks { +public: + explicit SilenceRulesDiagnostics(clang::CompilerInstance &CI) : CI_(CI) {} + + void LexedFileChanged(clang::FileID FID, LexedFileChangeReason Reason, + clang::SrcMgr::CharacteristicKind FileType, + clang::FileID PrevFID, + clang::SourceLocation Loc) override { + auto &src_mgr = CI_.getSourceManager(); + auto is_rules = [&](clang::FileID id) { + auto file = src_mgr.getFileEntryRefForID(id); + return file && IsRulesPrelude(file->getName()); + }; + if (Reason == LexedFileChangeReason::EnterFile && is_rules(FID)) { + CI_.getDiagnostics().setSuppressAllDiagnostics(true); + } + if (Reason == LexedFileChangeReason::ExitFile && is_rules(PrevFID)) { + CI_.getDiagnostics().setSuppressAllDiagnostics(false); + } + } + +private: + clang::CompilerInstance &CI_; +}; + class FrontendAction : public clang::ASTFrontendAction { public: explicit FrontendAction(std::string &rs_code, Model model, bool first, @@ -29,6 +57,12 @@ class FrontendAction : public clang::ASTFrontendAction { rules_dir_); } + bool BeginSourceFileAction(clang::CompilerInstance &CI) override { + CI.getPreprocessor().addPPCallbacks( + std::make_unique(CI)); + return true; + } + private: std::string &rs_code_; Model model_; diff --git a/cpp2rust/rules_prelude.cpp b/cpp2rust/rules_prelude.cpp new file mode 100644 index 0000000000..83bc5e59e3 --- /dev/null +++ b/cpp2rust/rules_prelude.cpp @@ -0,0 +1,150 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include "rules_prelude.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +namespace cpp2rust { + +namespace { + +std::string readFile(const fs::path &path) { + std::ifstream file(path); + return {std::istreambuf_iterator(file), + std::istreambuf_iterator()}; +} + +std::vector splitLines(const std::string &text) { + std::vector lines; + std::istringstream stream(text); + for (std::string line; std::getline(stream, line);) { + lines.push_back(std::move(line)); + } + return lines; +} + +std::string_view trimLeft(std::string_view text) { + while (!text.empty() && std::isspace((unsigned char)text.front())) { + text.remove_prefix(1); + } + return text; +} + +bool isInclude(std::string_view line) { + line = trimLeft(line); + if (!line.starts_with('#')) { + return false; + } + return trimLeft(line.substr(1)).starts_with("include"); +} + +std::string withoutIncludes(const std::string &text) { + std::string out; + for (const auto &line : splitLines(text)) { + if (!isInclude(line)) { + out += line; + } + out += '\n'; + } + return out; +} + +std::set getRuleNames(const std::string &text) { + std::set names; + auto is_word = [](char c) { + return std::isalnum((unsigned char)c) || c == '_'; + }; + for (size_t i = 0; i < text.size();) { + if (!is_word(text[i])) { + ++i; + continue; + } + size_t end = i; + while (end < text.size() && is_word(text[end])) { + ++end; + } + std::string_view word(text.data() + i, end - i); + if (word.size() > 1 && (word[0] == 'f' || word[0] == 't') && + std::all_of(word.begin() + 1, word.end(), + [](char c) { return std::isdigit((unsigned char)c); })) { + names.emplace(word); + } + i = end; + } + return names; +} + +void addCxxModule(std::string &out, const fs::path &module_dir) { + out += std::format("namespace cpp2rust_rules_{} {{\n", + module_dir.filename().string()); + out += withoutIncludes(readFile(module_dir / "src.cpp")); + out += "}\n"; +} + +void addCModule(std::string &out, const fs::path &module_dir) { + auto module = module_dir.filename().string(); + auto text = readFile(module_dir / "src.c"); + auto names = getRuleNames(text); + for (const auto &name : names) { + out += std::format("#define {} cpp2rust_rules_{}_{}\n", name, module, name); + } + out += withoutIncludes(text); + for (const auto &name : names) { + out += std::format("#undef {}\n", name); + } +} + +} // namespace + +RulesLanguage GetRulesLanguage(std::string_view filename) { + return filename.ends_with(".c") ? RulesLanguage::kC : RulesLanguage::kCxx; +} + +std::string GetRulesPreludePath(RulesLanguage language) { + return std::string(RULES_SOURCE_DIR) + (language == RulesLanguage::kC + ? "/cpp2rust_rules_prelude.c" + : "/cpp2rust_rules_prelude.cpp"); +} + +bool IsRulesPrelude(std::string_view path) { + return path == GetRulesPreludePath(RulesLanguage::kC) || + path == GetRulesPreludePath(RulesLanguage::kCxx); +} + +std::string GetRulesPreludeInclude(RulesLanguage language) { + return std::format("\n#include \"{}\"\n", GetRulesPreludePath(language)); +} + +std::string BuildRulesPrelude(RulesLanguage language) { + std::vector modules; + for (const auto &entry : fs::directory_iterator(RULES_SOURCE_DIR)) { + if (entry.is_directory()) { + modules.push_back(entry.path()); + } + } + std::sort(modules.begin(), modules.end()); + + std::string out = "#pragma GCC system_header\n"; + for (const auto &module_dir : modules) { + if (language == RulesLanguage::kCxx && fs::exists(module_dir / "src.cpp")) { + addCxxModule(out, module_dir); + } + if (language == RulesLanguage::kC && fs::exists(module_dir / "src.c")) { + addCModule(out, module_dir); + } + } + return out; +} + +} // namespace cpp2rust diff --git a/cpp2rust/rules_prelude.h b/cpp2rust/rules_prelude.h new file mode 100644 index 0000000000..e4c370166c --- /dev/null +++ b/cpp2rust/rules_prelude.h @@ -0,0 +1,23 @@ +#pragma once + +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include +#include + +namespace cpp2rust { + +enum class RulesLanguage { kC, kCxx }; + +RulesLanguage GetRulesLanguage(std::string_view filename); + +std::string GetRulesPreludePath(RulesLanguage language); + +std::string BuildRulesPrelude(RulesLanguage language); + +bool IsRulesPrelude(std::string_view path); + +std::string GetRulesPreludeInclude(RulesLanguage language); + +} // namespace cpp2rust From 32200402db456675a16a742f26ad8baefb79a687 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 2 Oct 2026 19:57:23 +0100 Subject: [PATCH 03/34] Load rules decls --- cpp2rust/ast_consumer.cpp | 2 + cpp2rust/converter/rules/registry.cpp | 26 +- cpp2rust/converter/rules/registry.h | 6 + cpp2rust/converter/rules/rule_decls.cpp | 337 ++++++++++++++++++++++++ cpp2rust/converter/rules/rule_decls.h | 36 +++ 5 files changed, 403 insertions(+), 4 deletions(-) create mode 100644 cpp2rust/converter/rules/rule_decls.cpp create mode 100644 cpp2rust/converter/rules/rule_decls.h diff --git a/cpp2rust/ast_consumer.cpp b/cpp2rust/ast_consumer.cpp index b1611d83e9..5b225ae017 100644 --- a/cpp2rust/ast_consumer.cpp +++ b/cpp2rust/ast_consumer.cpp @@ -4,10 +4,12 @@ #include "ast_consumer.h" #include "converter/converter.h" +#include "converter/rules/rule_decls.h" namespace cpp2rust { void ASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) { auto converter = CreateConverter(rs_code_, ctx, model_, rules_dir_); + RuleDecls::Collect(ctx); converter->SetSema(CI_.getSema()); if (first_) { converter->EmitFilePreamble(); diff --git a/cpp2rust/converter/rules/registry.cpp b/cpp2rust/converter/rules/registry.cpp index c0d048b42f..c987c17b90 100644 --- a/cpp2rust/converter/rules/registry.cpp +++ b/cpp2rust/converter/rules/registry.cpp @@ -23,6 +23,9 @@ bool translation_rules_loaded_ = false; ExprRuleMap exprs_; // key -> ExprRule TypeRuleMap types_; // key -> TypeRule +std::unordered_map exprs_by_name_; +std::unordered_map types_by_name_; + void AddTypeRule(std::string src, TranslationRule::TypeRule &&rule) { rule.src = std::move(src); auto key = Matcher::Key(rule); @@ -41,10 +44,12 @@ void addRulesFromDirectory(const std::filesystem::path &dir, Model model) { log() << "No rules found in " << path << '\n'; continue; } - for (auto &[_, rule] : expr_rules) { - exprs_.emplace(Matcher::Key(rule), std::move(rule)); + auto module = path.filename().string(); + for (auto &[name, rule] : expr_rules) { + auto it = exprs_.emplace(Matcher::Key(rule), std::move(rule)); + exprs_by_name_[module + '/' + name] = &it->second; } - for (auto &[_, rule] : type_rules) { + for (auto &[name, rule] : type_rules) { auto key = Matcher::Key(rule); auto [begin, end] = types_.equal_range(key); for (auto it = begin; it != end; ++it) { @@ -56,7 +61,8 @@ void addRulesFromDirectory(const std::filesystem::path &dir, Model model) { std::exit(EXIT_FAILURE); } } - types_.emplace(std::move(key), std::move(rule)); + auto it = types_.emplace(std::move(key), std::move(rule)); + types_by_name_[module + '/' + name] = &it->second; } } } @@ -70,6 +76,18 @@ GetParamInfo(clang::ASTContext &ctx, const clang::Expr *expr, unsigned index) { } // namespace +TranslationRule::ExprRule *FindExprRule(const std::string &module, + const std::string &name) { + auto it = exprs_by_name_.find(module + '/' + name); + return it == exprs_by_name_.end() ? nullptr : it->second; +} + +TranslationRule::TypeRule *FindTypeRule(const std::string &module, + const std::string &name) { + auto it = types_by_name_.find(module + '/' + name); + return it == types_by_name_.end() ? nullptr : it->second; +} + std::ranges::subrange ExprCandidates(const std::string &key) { auto [begin, end] = exprs_.equal_range(key); diff --git a/cpp2rust/converter/rules/registry.h b/cpp2rust/converter/rules/registry.h index aaa65d03b1..ce01beb506 100644 --- a/cpp2rust/converter/rules/registry.h +++ b/cpp2rust/converter/rules/registry.h @@ -28,6 +28,12 @@ ExprCandidates(const std::string &key); std::ranges::subrange TypeCandidates(const std::string &key); +TranslationRule::ExprRule *FindExprRule(const std::string &module, + const std::string &name); + +TranslationRule::TypeRule *FindTypeRule(const std::string &module, + const std::string &name); + Matcher::Match Search(clang::ASTContext &ctx, const clang::Expr *expr); Matcher::Match Search(clang::ASTContext &ctx, diff --git a/cpp2rust/converter/rules/rule_decls.cpp b/cpp2rust/converter/rules/rule_decls.cpp new file mode 100644 index 0000000000..9bf69f4ae3 --- /dev/null +++ b/cpp2rust/converter/rules/rule_decls.cpp @@ -0,0 +1,337 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include "converter/rules/rule_decls.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "converter/rules/registry.h" +#include "logging.h" +#include "rules_prelude.h" + +namespace cpp2rust::RuleDecls { + +namespace { + +constexpr std::string_view kPrefix = "cpp2rust_rules_"; + +ExprRuleDeclMap exprs_; +TypeRuleDeclMap types_; + +bool isRuleName(std::string_view name, char kind) { + return name.size() > 1 && name[0] == kind && + std::all_of(name.begin() + 1, name.end(), + [](char c) { return std::isdigit((unsigned char)c); }); +} + +struct RuleId { + std::string module; + std::string name; +}; + +std::optional getRuleId(const clang::NamedDecl *decl) { + if (!decl->getIdentifier()) { + return std::nullopt; + } + std::string_view name = decl->getName(); + if (const auto *ns = + llvm::dyn_cast(decl->getDeclContext())) { + std::string_view module = ns->getIdentifier() ? ns->getName() : ""; + if (!module.starts_with(kPrefix)) { + return std::nullopt; + } + return RuleId{std::string(module.substr(kPrefix.size())), + std::string(name)}; + } + if (!name.starts_with(kPrefix)) { + return std::nullopt; + } + name.remove_prefix(kPrefix.size()); + auto split = name.rfind('_'); + if (split == std::string_view::npos) { + return std::nullopt; + } + return RuleId{std::string(name.substr(0, split)), + std::string(name.substr(split + 1))}; +} + +const clang::Expr *getReturned(const clang::FunctionDecl *decl) { + const auto *body = + llvm::dyn_cast_or_null(decl->getBody()); + if (!body || body->size() != 1) { + return nullptr; + } + const auto *ret = llvm::dyn_cast(*body->body_begin()); + return ret ? ret->getRetValue() : nullptr; +} + +std::string getOperatorKey(clang::OverloadedOperatorKind op) { + if (op == clang::OO_None) { + return {}; + } + return std::string("operator") + clang::getOperatorSpelling(op); +} + +std::string getNameKey(clang::DeclarationName name) { + switch (name.getNameKind()) { + case clang::DeclarationName::Identifier: + if (const auto *identifier = name.getAsIdentifierInfo()) { + return identifier->getName().str(); + } + return {}; + case clang::DeclarationName::CXXOperatorName: + return getOperatorKey(name.getCXXOverloadedOperator()); + default: + return {}; + } +} + +std::string getTypeKey(clang::QualType type) { + type = type.getNonReferenceType(); + if (const auto *tst = type->getAs()) { + if (const auto *tmpl = tst->getTemplateName().getAsTemplateDecl()) { + return getNameKey(tmpl->getDeclName()); + } + } + if (const auto *tag = type->getAsTagDecl()) { + return getNameKey(tag->getDeclName()); + } + return {}; +} + +const clang::Expr *skipImplicit(const clang::Expr *expr) { + while (true) { + expr = expr->IgnoreParens(); + if (const auto *full = llvm::dyn_cast(expr)) { + expr = full->getSubExpr(); + continue; + } + if (const auto *temporary = + llvm::dyn_cast(expr)) { + expr = temporary->getSubExpr(); + continue; + } + if (const auto *bind = llvm::dyn_cast(expr)) { + expr = bind->getSubExpr(); + continue; + } + const auto *cast = llvm::dyn_cast(expr); + if (!cast) { + return expr; + } + expr = cast->getSubExpr(); + if (cast->getCastKind() != clang::CK_UserDefinedConversion) { + continue; + } + if (const auto *conversion = + llvm::dyn_cast(expr->IgnoreImplicit())) { + expr = conversion->getImplicitObjectArgument(); + } + } +} + +bool isDeclaredByRules(clang::ASTContext &ctx, const clang::Decl *decl) { + auto &src_mgr = ctx.getSourceManager(); + return IsRulesPrelude(src_mgr.getFilename( + src_mgr.getSpellingLoc(decl->getCanonicalDecl()->getLocation()))); +} + +bool refersToUndeclared(const clang::Expr *expr) { + expr = skipImplicit(expr); + if (const auto *call = llvm::dyn_cast(expr)) { + if (const auto *lookup = llvm::dyn_cast( + call->getCallee()->IgnoreParenImpCasts())) { + return lookup->getNumDecls() == 0; + } + if (const auto *callee = call->getDirectCallee()) { + return callee->isImplicit() && !callee->getBuiltinID(); + } + } + return false; +} + +bool refersToUndeclared(clang::ASTContext &ctx, clang::QualType type) { + while (type->isPointerType() || type->isReferenceType()) { + type = type->getPointeeType(); + } + const auto *tag = type->getAsTagDecl(); + return tag && isDeclaredByRules(ctx, tag); +} + +std::string getExprKey(clang::ASTContext &ctx, const clang::Expr *expr) { + expr = skipImplicit(expr); + + if (llvm::isa(expr) && + expr->getBeginLoc().isMacroID()) { + return clang::Lexer::getImmediateMacroName( + expr->getBeginLoc(), ctx.getSourceManager(), ctx.getLangOpts()) + .str(); + } + if (const auto *op = llvm::dyn_cast(expr)) { + return getOperatorKey(op->getOperator()); + } + if (const auto *rewritten = + llvm::dyn_cast(expr)) { + return getOperatorKey( + clang::BinaryOperator::getOverloadedOperator(rewritten->getOperator())); + } + if (const auto *call = llvm::dyn_cast(expr)) { + const auto *callee = call->getCallee()->IgnoreParenImpCasts(); + if (const auto *ref = llvm::dyn_cast(callee); + ref && llvm::isa(ref->getDecl())) { + return getOperatorKey(clang::OO_Call); + } + return getExprKey(ctx, callee); + } + if (const auto *overload = llvm::dyn_cast(expr)) { + return getNameKey(overload->getName()); + } + if (const auto *member = + llvm::dyn_cast(expr)) { + return getNameKey(member->getMember()); + } + if (const auto *ref = + llvm::dyn_cast(expr)) { + return getNameKey(ref->getDeclName()); + } + if (const auto *member = llvm::dyn_cast(expr)) { + return getNameKey(member->getMemberDecl()->getDeclName()); + } + if (const auto *ref = llvm::dyn_cast(expr)) { + return getNameKey(ref->getDecl()->getDeclName()); + } + if (const auto *unary = llvm::dyn_cast(expr)) { + if (unary->getSubExpr()->isTypeDependent()) { + return getOperatorKey( + clang::UnaryOperator::getOverloadedOperator(unary->getOpcode())); + } + return getExprKey(ctx, unary->getSubExpr()); + } + if (const auto *binary = llvm::dyn_cast(expr)) { + return getOperatorKey( + clang::BinaryOperator::getOverloadedOperator(binary->getOpcode())); + } + if (llvm::isa(expr)) { + return getOperatorKey(clang::OO_Subscript); + } + if (const auto *construct = + llvm::dyn_cast(expr)) { + return getTypeKey(construct->getTypeAsWritten()); + } + if (const auto *construct = llvm::dyn_cast(expr)) { + return getNameKey(construct->getConstructor()->getParent()->getDeclName()); + } + if (const auto *cast = llvm::dyn_cast(expr)) { + return getTypeKey(cast->getTypeAsWritten()); + } + return {}; +} + +void addExprRule(clang::ASTContext &ctx, const clang::NamedDecl *decl) { + auto id = getRuleId(decl); + if (!id || !isRuleName(id->name, 'f')) { + return; + } + const clang::FunctionDecl *function = nullptr; + if (const auto *tmpl = llvm::dyn_cast(decl)) { + function = tmpl->getTemplatedDecl(); + } else { + function = llvm::dyn_cast(decl); + } + if (!function || !function->isThisDeclarationADefinition()) { + return; + } + const auto *returned = getReturned(function); + if (decl->isInvalidDecl() || function->isInvalidDecl() || !returned || + returned->containsErrors() || refersToUndeclared(returned)) { + log() << "rule " << id->module << "::" << id->name << " does not apply\n"; + return; + } + auto *rule = RuleRegistry::FindExprRule(id->module, id->name); + if (!rule) { + log() << "rule " << id->module << "::" << id->name << " has no target\n"; + return; + } + auto key = getExprKey(ctx, returned); + log() << "rule " << id->module << "::" << id->name << " -> '" << key << "'\n"; + exprs_.emplace(std::move(key), ExprRuleDecl{function, rule}); +} + +void addTypeRule(clang::ASTContext &ctx, const clang::NamedDecl *decl) { + auto id = getRuleId(decl); + if (!id || !isRuleName(id->name, 't')) { + return; + } + const clang::TypedefNameDecl *alias = nullptr; + if (const auto *tmpl = llvm::dyn_cast(decl)) { + alias = tmpl->getTemplatedDecl(); + } else { + alias = llvm::dyn_cast(decl); + } + if (!alias) { + return; + } + if (decl->isInvalidDecl() || alias->isInvalidDecl() || + alias->getUnderlyingType()->containsErrors() || + refersToUndeclared(ctx, alias->getUnderlyingType())) { + log() << "rule " << id->module << "::" << id->name << " does not apply\n"; + return; + } + auto *rule = RuleRegistry::FindTypeRule(id->module, id->name); + if (!rule) { + log() << "rule " << id->module << "::" << id->name << " has no target\n"; + return; + } + auto key = getTypeKey(alias->getUnderlyingType()); + log() << "rule " << id->module << "::" << id->name << " -> '" << key << "'\n"; + types_.emplace(std::move(key), TypeRuleDecl{decl, rule}); +} + +void addRules(clang::ASTContext &ctx, const clang::DeclContext *context) { + for (const auto *child : context->decls()) { + if (const auto *ns = llvm::dyn_cast(child)) { + if (ns->getIdentifier() && ns->getName().starts_with(kPrefix)) { + addRules(ctx, ns); + } + continue; + } + if (const auto *named = llvm::dyn_cast(child)) { + addExprRule(ctx, named); + addTypeRule(ctx, named); + } + } +} + +} // namespace + +void Collect(clang::ASTContext &ctx) { + exprs_.clear(); + types_.clear(); + addRules(ctx, ctx.getTranslationUnitDecl()); + log() << "collected " << exprs_.size() << " expression rules and " + << types_.size() << " type rules\n"; +} + +std::ranges::subrange +ExprCandidates(const std::string &key) { + auto [begin, end] = exprs_.equal_range(key); + return {begin, end}; +} + +std::ranges::subrange +TypeCandidates(const std::string &key) { + auto [begin, end] = types_.equal_range(key); + return {begin, end}; +} + +} // namespace cpp2rust::RuleDecls diff --git a/cpp2rust/converter/rules/rule_decls.h b/cpp2rust/converter/rules/rule_decls.h new file mode 100644 index 0000000000..12f8eaa483 --- /dev/null +++ b/cpp2rust/converter/rules/rule_decls.h @@ -0,0 +1,36 @@ +#pragma once + +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include +#include + +#include +#include +#include + +#include "converter/translation_rule.h" + +namespace cpp2rust::RuleDecls { +struct ExprRuleDecl { + const clang::FunctionDecl *decl; + TranslationRule::ExprRule *rule; +}; + +struct TypeRuleDecl { + const clang::NamedDecl *decl; + TranslationRule::TypeRule *rule; +}; + +using ExprRuleDeclMap = std::unordered_multimap; +using TypeRuleDeclMap = std::unordered_multimap; + +void Collect(clang::ASTContext &ctx); + +std::ranges::subrange +ExprCandidates(const std::string &key); + +std::ranges::subrange +TypeCandidates(const std::string &key); +} // namespace cpp2rust::RuleDecls From dd8eb3482b66adad5c2907321485e197c8a4adac Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 2 Oct 2026 21:33:47 +0100 Subject: [PATCH 04/34] Update rules --- rules/algorithm/src.cpp | 17 ++-- rules/brotli/src.c | 11 +++ rules/builtin_types/src.c | 30 ++++++ rules/cstddef/src.c | 6 ++ rules/cstddef/src.cpp | 2 + rules/dirent/src.c | 7 ++ rules/fcntl/src.c | 8 ++ rules/ioctl/src.c | 6 ++ rules/map/src.cpp | 108 +++++++++++++++----- rules/pair/src.cpp | 2 +- rules/select/src.c | 6 ++ rules/stdio/src.c | 8 ++ rules/unique_ptr/src.cpp | 2 +- rules/vector/src.cpp | 202 ++++++++++++++++++++++++++------------ 14 files changed, 321 insertions(+), 94 deletions(-) create mode 100644 rules/brotli/src.c create mode 100644 rules/builtin_types/src.c create mode 100644 rules/cstddef/src.c create mode 100644 rules/dirent/src.c create mode 100644 rules/fcntl/src.c create mode 100644 rules/ioctl/src.c create mode 100644 rules/select/src.c create mode 100644 rules/stdio/src.c diff --git a/rules/algorithm/src.cpp b/rules/algorithm/src.cpp index 620934b32d..bad063e693 100644 --- a/rules/algorithm/src.cpp +++ b/rules/algorithm/src.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include template void f1(T1 first, T1 last) { @@ -30,15 +31,19 @@ template T1 *f8(T1 *first, T1 *last) { template void f9(T1 &a0, T1 &a1) { return std::swap(a0, a1); } -template -typename std::vector::iterator f10(typename std::vector::iterator a0, - typename std::vector::iterator a1) { +template < + typename It, typename T1 = typename std::iterator_traits::value_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +typename std::vector::iterator f10(It a0, It a1) { return std::unique(a0, a1); } -template -void f12(typename std::vector::iterator a0, - typename std::vector::iterator a1, const T2 &a2) { +template ::value_type, + typename = typename std::enable_if::iterator>::value>::type> +void f12(It a0, It a1, const T2 &a2) { return std::fill(a0, a1, a2); } diff --git a/rules/brotli/src.c b/rules/brotli/src.c new file mode 100644 index 0000000000..2ec73213db --- /dev/null +++ b/rules/brotli/src.c @@ -0,0 +1,11 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include "brotli/decode.h" +#include "brotli/encode.h" + +typedef BrotliDecoderResult t1; +typedef BrotliEncoderMode t2; +typedef BrotliDecoderStateStruct *t3; +typedef const BrotliDecoderStateStruct *t4; +typedef BrotliDecoderErrorCode t5; diff --git a/rules/builtin_types/src.c b/rules/builtin_types/src.c new file mode 100644 index 0000000000..680e1de594 --- /dev/null +++ b/rules/builtin_types/src.c @@ -0,0 +1,30 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#define BUILTIN_TYPE(T, N0, N1, N2, N3, N4) \ + typedef T N0; \ + typedef T *N1; \ + typedef const T *N2; \ + typedef volatile T *N3; \ + typedef const volatile T *N4; + +BUILTIN_TYPE(_Bool, t10, t11, t12, t13, t14) +BUILTIN_TYPE(char, t20, t21, t22, t23, t24) +BUILTIN_TYPE(signed char, t30, t31, t32, t33, t34) +BUILTIN_TYPE(unsigned char, t40, t41, t42, t43, t44) +BUILTIN_TYPE(short, t60, t61, t62, t63, t64) +BUILTIN_TYPE(unsigned short, t70, t71, t72, t73, t74) +BUILTIN_TYPE(int, t90, t91, t92, t93, t94) +BUILTIN_TYPE(unsigned int, t100, t101, t102, t103, t104) +BUILTIN_TYPE(float, t130, t131, t132, t133, t134) +BUILTIN_TYPE(long, t140, t141, t142, t143, t144) +BUILTIN_TYPE(unsigned long, t150, t151, t152, t153, t154) +BUILTIN_TYPE(long long, t160, t161, t162, t163, t164) +BUILTIN_TYPE(unsigned long long, t170, t171, t172, t173, t174) +BUILTIN_TYPE(double, t180, t181, t182, t183, t184) +BUILTIN_TYPE(long double, t190, t191, t192, t193, t194) +BUILTIN_TYPE(__int128, t200, t201, t202, t203, t204) +BUILTIN_TYPE(unsigned __int128, t210, t211, t212, t213, t214) +BUILTIN_TYPE(void, t220, t221, t222, t223, t224) + +#undef BUILTIN_TYPE diff --git a/rules/cstddef/src.c b/rules/cstddef/src.c new file mode 100644 index 0000000000..700633384a --- /dev/null +++ b/rules/cstddef/src.c @@ -0,0 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +typedef __typeof__(sizeof(0)) t5; diff --git a/rules/cstddef/src.cpp b/rules/cstddef/src.cpp index aa6fec4f3a..848f12befb 100644 --- a/rules/cstddef/src.cpp +++ b/rules/cstddef/src.cpp @@ -8,6 +8,8 @@ using t1 = std::byte; #endif +using std::size_t; + typedef size_t t2; typedef size_t *t3; typedef const size_t *t4; diff --git a/rules/dirent/src.c b/rules/dirent/src.c new file mode 100644 index 0000000000..7a78c99c15 --- /dev/null +++ b/rules/dirent/src.c @@ -0,0 +1,7 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +typedef DIR *t1; +typedef struct dirent t2; diff --git a/rules/fcntl/src.c b/rules/fcntl/src.c new file mode 100644 index 0000000000..4a8eaa66c7 --- /dev/null +++ b/rules/fcntl/src.c @@ -0,0 +1,8 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +int (*f1)(int, int, ...) = fcntl; + +int (*f2)(const char *, int, ...) = open; diff --git a/rules/ioctl/src.c b/rules/ioctl/src.c new file mode 100644 index 0000000000..fe322aec32 --- /dev/null +++ b/rules/ioctl/src.c @@ -0,0 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +int (*f1)(int, unsigned long, ...) = ioctl; diff --git a/rules/map/src.cpp b/rules/map/src.cpp index 121a140511..1a81480c01 100644 --- a/rules/map/src.cpp +++ b/rules/map/src.cpp @@ -1,16 +1,30 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. +#include #include +#include #include template using t1 = std::map; -template -using t2 = typename std::map::const_iterator; - -template -using t3 = typename std::map::iterator; +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if::const_iterator>::value>::type> +void t2(It *); + +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +void t3(It *); template T2 &f1(std::map &o, const T1 &key) { return o.operator[](key); @@ -53,9 +67,14 @@ typename std::map::iterator f10(std::map &o, const T1 &key) { return o.find(key); } -template -bool f11(typename std::map::iterator a, - typename std::map::iterator b) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +bool f11(It a, It b) { return operator!=(a, b); } @@ -64,9 +83,14 @@ typename std::map::iterator f12(std::map &o) { return o.begin(); } -template -bool f13(typename std::map::const_iterator a, - typename std::map::const_iterator b) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if::const_iterator>::value>::type> +bool f13(It a, It b) { return operator==(a, b); } @@ -80,9 +104,14 @@ const T2 &f15(const std::map &o, const T1 &key) { return o.at(key); } -template -bool f16(typename std::map::iterator a, - typename std::map::iterator b) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +bool f16(It a, It b) { return operator==(a, b); } @@ -92,29 +121,58 @@ typename std::map::const_iterator f17(const std::map &o, return o.find(key); } -template -typename std::map::const_iterator -f19(const typename std::map::iterator &it) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +typename std::map::const_iterator f19(const It &it) { return typename std::map::const_iterator(it); } -template -const T1 &f20(typename std::map::const_iterator it) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if::const_iterator>::value>::type> +const T1 &f20(It it) { return it->first; } -template -const T2 &f21(typename std::map::const_iterator it) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if::const_iterator>::value>::type> +const T2 &f21(It it) { return it->second; } -template -const T1 &f22(typename std::map::iterator it) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +const T1 &f22(It it) { return it->first; } -template -T2 &f23(typename std::map::iterator it) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +T2 &f23(It it) { return it->second; } diff --git a/rules/pair/src.cpp b/rules/pair/src.cpp index 087cf18c4b..c72845700c 100644 --- a/rules/pair/src.cpp +++ b/rules/pair/src.cpp @@ -31,7 +31,7 @@ std::pair f6(T3 &a0, T4 &a1) { template std::pair f7(T3 &&a0, T4 &&a1) { - return std::pair(std::move(a0), std::move(a1)); + return std::pair(std::forward(a0), std::forward(a1)); } #if __cplusplus >= 201402L diff --git a/rules/select/src.c b/rules/select/src.c new file mode 100644 index 0000000000..0c711faf92 --- /dev/null +++ b/rules/select/src.c @@ -0,0 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +typedef fd_set t1; diff --git a/rules/stdio/src.c b/rules/stdio/src.c new file mode 100644 index 0000000000..df294843dd --- /dev/null +++ b/rules/stdio/src.c @@ -0,0 +1,8 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +typedef FILE *t1; + +int (*f21)(char *, size_t, const char *, ...) = snprintf; diff --git a/rules/unique_ptr/src.cpp b/rules/unique_ptr/src.cpp index 98cd44181a..c25e56274f 100644 --- a/rules/unique_ptr/src.cpp +++ b/rules/unique_ptr/src.cpp @@ -10,7 +10,7 @@ template using t1 = std::unique_ptr; template using t2 = std::unique_ptr; #if __cplusplus >= 201402L -template std::unique_ptr f1(std::size_t n) { +template std::unique_ptr f1(std::size_t n) { return std::make_unique(n); } #endif diff --git a/rules/vector/src.cpp b/rules/vector/src.cpp index 0121a7e316..5439b60cc1 100644 --- a/rules/vector/src.cpp +++ b/rules/vector/src.cpp @@ -3,23 +3,42 @@ #include #include +#include +#include #include template using Init = A; template using t1 = std::vector; -template using t2 = typename std::vector::iterator; +template < + typename It, typename T1 = typename std::iterator_traits::value_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +void t2(It *); template using t3 = std::vector>; -template using t4 = typename std::vector::const_iterator; +template ::value_type, + typename = typename std::enable_if::const_iterator>::value>::type> +void t4(It *); template > using t5 = std::vector; #if defined(__linux__) -template > -using t6 = typename std::vector::iterator; -template > -using t7 = typename std::vector::const_iterator; +template