diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index d5c6ed73bb..deff1a2957 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -673,8 +673,7 @@ bool Converter::RecordDerivesDefault(const clang::RecordDecl *decl) { bool Converter::IsPassThroughRule(clang::Expr *expr) const { const auto *rule = Mapper::GetExprRule(GetCalleeOrExpr(expr)); return rule && rule->body.size() == 1 && - std::holds_alternative( - rule->body[0]); + std::holds_alternative(rule->body[0]); } bool Converter::RecordDerivesCopy(const clang::RecordDecl *decl) const { @@ -2204,7 +2203,8 @@ static std::string getTypedLiteral(const char *num, std::string_view type) { std::string Converter::getIntegerLiteral(clang::IntegerLiteral *expr, bool incl_type, const clang::QualType *type) { - auto num_as_string = GetNumAsString(expr->getValue()); + auto num_as_string = + GetNumAsString(expr->getValue(), expr->getType()->isSignedIntegerType()); if (num_as_string[0] != '-' && !incl_type) { if (type && (*type)->isFloatingType() && num_as_string.find('.') == llvm::StringRef::npos) { @@ -4886,8 +4886,7 @@ std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg, Buffer buf(*this); PushExplicitAutoref autoref( *this, ph_ctx.is_index_base - ? std::optional(ph_ctx.access == - TranslationRule::Access::kBorrowMut) + ? std::optional(ph_ctx.access == IrTgt::Access::kBorrowMut) : std::nullopt); PushExprKind push(*this, ExprKind::RValue); ConvertDeref(arg); @@ -4902,7 +4901,7 @@ std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg, return ConvertLValue(arg); } - if (ph_ctx.access == TranslationRule::Access::kTake) { + if (ph_ctx.access == IrTgt::Access::kTake) { if (clang::isa(arg)) { return ConvertRValue(arg); } @@ -4926,7 +4925,7 @@ std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg, return std::format("std::mem::take(&mut {})", std::move(lvalue)); } - if (ph_ctx.access == TranslationRule::Access::kMove) { + if (ph_ctx.access == IrTgt::Access::kMove) { return ConvertFreshRValue(arg, ph_ctx.implicit_convert_to); } @@ -4934,8 +4933,8 @@ std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg, } std::string Converter::ConvertMappedMethodCall( - clang::Expr *expr, const TranslationRule::MethodCallFragment &mc, - clang::Expr **args, unsigned num_args, TempMaterializationCtx *ctx) { + clang::Expr *expr, const IrTgt::MethodCallFragment &mc, clang::Expr **args, + unsigned num_args, TempMaterializationCtx *ctx) { return ConvertIRFragment(mc.receiver, expr, args, num_args, ctx) + ConvertIRFragment(mc.body, expr, args, num_args, ctx); } @@ -4954,11 +4953,11 @@ std::string Converter::GetMappedAsString(clang::Expr *expr, clang::Expr **args, return result; } -std::string Converter::ConvertIRFragment( - const std::vector &fragments, - clang::Expr *expr, clang::Expr **args, unsigned num_args, - TempMaterializationCtx *ctx) { - using namespace TranslationRule; +std::string +Converter::ConvertIRFragment(const std::vector &fragments, + clang::Expr *expr, clang::Expr **args, + unsigned num_args, TempMaterializationCtx *ctx) { + using namespace IrTgt; auto all_args = BuildUnifiedArgs(expr, args, num_args); @@ -4989,9 +4988,9 @@ std::string Converter::ConvertIRFragment( .is_index_base = ph->is_index_base, }; result += ConvertPlaceholder(expr, arg, ph_ctx); - } else if (std::get_if(&frag)) { + } else if (std::get_if(&frag)) { result += ConvertVariadicTail(expr, all_args); - } else if (std::get_if(&frag)) { + } else if (std::get_if(&frag)) { result += ConvertInitFragment(expr, all_args); } else if (auto *mc = std::get_if>(&frag)) { @@ -5025,12 +5024,13 @@ std::string Converter::ConvertInitFragment(clang::Expr *expr, const std::vector &all_args) { const auto *tgt_ir = Mapper::GetExprRule(GetCalleeOrExpr(expr)); - assert(tgt_ir && tgt_ir->init_type.valid()); + const auto &init_type = Mapper::GetInitType(GetCalleeOrExpr(expr)); + assert(tgt_ir && init_type.valid()); auto *callee = clang::cast(expr)->getDirectCallee(); assert(callee); auto type = GetSema() - .getTemplateInstantiationArgs(callee)(tgt_ir->init_type.depth, - tgt_ir->init_type.index) + .getTemplateInstantiationArgs(callee)(init_type.depth, + init_type.index) .getAsType(); Buffer buf(*this); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 16825b8abe..605c5fa7b4 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -20,7 +20,7 @@ #include "converter/converter_lib.h" #include "converter/factory.h" #include "converter/lex.h" -#include "converter/translation_rule.h" +#include "converter/rules/ir_tgt.h" #include "logging.h" namespace cpp2rust { @@ -247,7 +247,7 @@ class Converter : public clang::RecursiveASTVisitor { std::optional implicit_convert_to; TempMaterializationCtx *materialize_ctx; int materialize_idx; // <0 = no idx, >=0 idx valid - TranslationRule::Access access; + IrTgt::Access access; bool is_receiver; bool is_cpp_ptr; bool maps_to_rust_ptr; @@ -272,9 +272,7 @@ class Converter : public clang::RecursiveASTVisitor { declared_in_rule_as_rust_ptr; } - bool needs_lvalue() const { - return access == TranslationRule::Access::kBorrowMut; - } + bool needs_lvalue() const { return access == IrTgt::Access::kBorrowMut; } void dump() const; }; @@ -695,7 +693,7 @@ class Converter : public clang::RecursiveASTVisitor { TempMaterializationCtx *ctx = nullptr); std::string - ConvertIRFragment(const std::vector &fragments, + ConvertIRFragment(const std::vector &fragments, clang::Expr *expr, clang::Expr **args, unsigned num_args, TempMaterializationCtx *ctx); @@ -716,7 +714,7 @@ class Converter : public clang::RecursiveASTVisitor { clang::CXXConstructExpr *ctor); virtual std::string ConvertMappedMethodCall( - clang::Expr *expr, const TranslationRule::MethodCallFragment &mc, + clang::Expr *expr, const IrTgt::MethodCallFragment &mc, clang::Expr **args, unsigned num_args, TempMaterializationCtx *ctx); virtual std::string AccessLValueObject(clang::MemberExpr *member); diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 4c01a1556e..72400e0aa4 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -1049,6 +1049,13 @@ std::string GetFunctionBaseName(const clang::FunctionDecl *decl) { return decl->getNameAsString(); } +bool HasFunctionParameterPack(const clang::FunctionDecl *decl) { + if (auto *primary = decl->getPrimaryTemplate()) { + decl = primary->getTemplatedDecl(); + } + return decl->getNumParams() && decl->parameters().back()->isParameterPack(); +} + void ToIdentifier(std::string &name) { ReplaceAll(name, "[", "arr"); ReplaceAll(name, "]", "arr"); diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 6e0a8cc98f..47fcc06a6b 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -175,12 +176,25 @@ template llvm::SmallString<16> GetNumAsString(const T &num) { return small_string; } +inline llvm::SmallString<16> GetNumAsString(const llvm::APInt &num, + bool is_signed = false) { + llvm::SmallString<16> small_string; + num.toString(small_string, 10, is_signed); + return small_string; +} + +inline llvm::SmallString<16> GetNumAsString(const llvm::APSInt &num) { + return GetNumAsString(num, num.isSigned()); +} + clang::QualType GetReturnTypeOfFunction(const clang::CallExpr *expr); const char *GetOverloadedOperator(const clang::FunctionDecl *decl); std::string GetFunctionBaseName(const clang::FunctionDecl *decl); +bool HasFunctionParameterPack(const clang::FunctionDecl *decl); + void ToIdentifier(std::string &name); std::string GetConversionName(const clang::CXXConversionDecl *decl, diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 0464976ce2..551d8cdf9f 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -6,20 +6,20 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include #include "converter/converter_lib.h" -#include "converter/translation_rule.h" +#include "converter/rules/ir_src.h" +#include "converter/rules/ir_src_builder.h" +#include "converter/rules/ir_tgt.h" namespace cpp2rust::Mapper { @@ -29,10 +29,18 @@ clang::ASTContext *ctx_ = nullptr; Model model_ = Model::kUnsafe; bool translation_rules_loaded_ = false; -std::unordered_multimap - exprs_; // src -> ExprRule -std::unordered_multimap - types_; // src -> TypeRule +struct ExprRule { + IrSrc::ExprRule src; + IrTgt::ExprRule tgt; +}; + +struct TypeRule { + IrSrc::TypeRule src; + IrTgt::TypeRule tgt; +}; + +std::unordered_multimap exprs_; +std::unordered_multimap types_; clang::PrintingPolicy getPrintPolicy() { assert(ctx_); @@ -46,310 +54,62 @@ clang::PrintingPolicy getPrintPolicy() { return policy; } -std::string GetExprMapKey(const std::string &str) { - // Extract the function name from something like - // const T1 & std::foo::fn_name(args) - auto n = str.find_first_of('('); - if (n == std::string::npos) { - n = str.size(); - } +using Node = IrSrc::Node; +using NodePtr = IrSrc::NodePtr; +using Kind = Node::Kind; - // Walk backwards from '(' tracking <> depth: - // - skip characters inside template arguments (depth > 0) - // - stop at the first space outside all angle brackets - std::string result; - int depth = 0; - for (int i = (int)n - 1; i >= 0; --i) { - char c = str[i]; - if (c == '>') - ++depth; - else if (c == '<') - --depth; - else if (c == ' ' && depth == 0) - break; - else if (depth == 0) - result += c; - } - std::reverse(result.begin(), result.end()); - return result; -} +struct Binding { + clang::QualType type; + std::optional value; +}; +using Bindings = std::vector>; -constexpr const char kPackMarker[] = "&&..."; +std::unordered_map> + expr_cache_; +std::unordered_map> type_cache_; -std::string GetTypeMapKey(const std::string &str) { - auto n = str.find_first_of("<["); - if (n == std::string::npos || str[n] == '<') { - return str.substr(0, n); - } - // something like int[][] or T1[] -> [] - return str.substr(n + 1); +void clearCaches() { + expr_cache_.clear(); + type_cache_.clear(); } -void AddTypeRule(std::string src, TranslationRule::TypeRule &&rule) { - auto key = GetTypeMapKey(src); - rule.src = std::move(src); - types_.emplace(std::move(key), std::move(rule)); -} - -// Attempts to unify an instantiated C++ type or function signature with a -// corresponding template pattern. If the two match structurally, it returns -// a mapping from template parameter names (e.g., "T1") to their concrete -// instantiated types (e.g., "int"). If no match is possible, returns nullopt. -// -// Example: -// template_str = "std::vector::vector()" -// instantiated = "std::vector::vector()" -// result = { "int" } -std::optional>> -matchTemplate(const std::string &template_str, - const std::string &instantiated) { - auto matchLiteralAt = [&](const std::string &input_str, size_t pos, - std::string_view literal, size_t &end_pos) -> bool { - size_t i = pos; - size_t j = 0; - - while (true) { - while (i < input_str.size() && std::isspace(input_str[i])) { - i++; - } - - while (j < literal.size() && std::isspace(literal[j])) { - j++; - } - - if (j == literal.size()) { - end_pos = i; - return true; - } - - if (i >= input_str.size()) { - return false; - } - - if (input_str[i] != literal[j]) { - return false; - } - - i++; - j++; - } - }; - - auto findNextLiteralSameDepth = [&](const std::string &s, size_t start, - std::string_view lit) -> size_t { - int ang = 0; - int par = 0; - int sq = 0; - - for (size_t i = 0; i < s.size() && i < start; i++) { - switch (s[i]) { - case '<': { - ang++; - break; - } - case '>': { - ang--; - break; - } - case '(': { - par++; - break; - } - case ')': { - par--; - break; - } - case '[': { - sq++; - break; - } - case ']': { - sq--; - break; - } - default: - break; - } - assert(ang >= 0 && par >= 0 && sq >= 0 && "Unbalanced ang, par or sq"); - } - - int base_ang = ang; - int base_par = par; - int base_sq = sq; - - for (size_t i = start; i <= s.size(); i++) { - if (ang == base_ang && par == base_par && sq == base_sq) { - size_t end_i = 0; - if (matchLiteralAt(s, i, lit, end_i)) { - return i; - } - } - - if (i == s.size()) { - break; - } - - char c = s[i]; - switch (c) { - case '<': { - ang++; - break; - } - case '>': { - ang--; - break; - } - case '(': { - par++; - break; - } - case ')': { - par--; - break; - } - case '[': { - sq++; - break; - } - case ']': { - sq--; - break; - } - default: - break; - } - - if (ang < 0 || par < 0 || sq < 0) { - return std::string::npos; - } - } - - return std::string::npos; - }; - - std::vector> captured; - - size_t ti = 0; - size_t si = 0; - - while (ti < template_str.size()) { - if (template_str[ti] == 'T' && ti + 1 < template_str.size() && - std::isdigit(template_str[ti + 1])) { - size_t tj = ti + 2; - while (tj < template_str.size() && std::isdigit(template_str[tj])) { - tj++; - } - - size_t type_idx = std::stoi(&template_str[ti + 1]) - 1; - assert(type_idx < TranslationRule::kMaxGenerics && - "template placeholder exceeds kMaxGenerics"); - ti = tj; - - std::string_view nextLit; - size_t scan = ti; - while (scan < template_str.size()) { - if (template_str[scan] == 'T' && scan + 1 < template_str.size() && - std::isdigit(template_str[scan + 1])) { - break; - } - scan++; - } - nextLit = std::string_view(template_str).substr(ti, scan - ti); - - captured.resize(std::max(captured.size(), type_idx + 1)); - auto &repl = captured[type_idx]; - if (repl.has_value()) { - size_t end_pos = 0; - if (!matchLiteralAt(instantiated, si, *repl, end_pos)) { - return std::nullopt; - } - si = end_pos; - } else { - if (!nextLit.empty()) { - size_t k = findNextLiteralSameDepth(instantiated, si, nextLit); - if (k == std::string::npos) { - return std::nullopt; - } - - size_t a = si; - size_t b = k; - - while (a < b && std::isspace(instantiated[a])) { - a++; - } - while (b > a && std::isspace(instantiated[b - 1])) { - b--; - } - - repl = instantiated.substr(a, b - a); - si = k; - } else { - size_t a = si; - size_t b = instantiated.size(); - - while (a < b && std::isspace(instantiated[a])) { - a++; - } - while (b > a && std::isspace(instantiated[b - 1])) { - b--; - } - - repl = instantiated.substr(a, b - a); - si = instantiated.size(); - } - } - - if (!nextLit.empty()) { - size_t end_pos = 0; - if (!matchLiteralAt(instantiated, si, nextLit, end_pos)) { - return std::nullopt; - } - si = end_pos; - ti += nextLit.size(); - } - } else { - size_t tj = ti; - while (tj < template_str.size()) { - if (template_str[tj] == 'T' && tj + 1 < template_str.size() && - std::isdigit(template_str[tj + 1])) { - break; - } - ++tj; - } - - auto lit = std::string_view(template_str).substr(ti, tj - ti); - size_t end_pos = 0; - if (!matchLiteralAt(instantiated, si, lit, end_pos)) { - return std::nullopt; - } - si = end_pos; - ti = tj; +bool Match(const Node &rule, const Node &use, + std::vector &bindings) { + if (const auto *param = llvm::dyn_cast(&rule)) { + assert(param->param < bindings.size()); + auto &slot = bindings[param->param]; + // First time we see the binding, always succeed. + if (!slot) { + slot = &use; + return true; } + // Second time we see the binding, check that it equals the first usage. + return *slot == use; } - - while (si < instantiated.size() && std::isspace(instantiated[si])) { - si++; + if (!rule.shallowEquals(use)) { + return false; } + return IrSrc::zipChildren(rule, use, [&](const NodePtr &r, const NodePtr &u) { + return Match(*r, *u, bindings); + }); +} - if (si != instantiated.size()) { - return std::nullopt; +void AddTypeRule(clang::QualType type, IrTgt::TypeRule &&rule) { + auto src = IrSrc::Builder(*ctx_).FromType(type); + auto key = src->indexKey(); + auto [begin, end] = types_.equal_range(key); + for (auto it = begin; it != end; ++it) { + if (*it->second.src.ir == *src) { + // Skip if the rule already exists + return; + } } - - return captured; + types_.emplace(std::move(key), TypeRule{{std::move(src)}, std::move(rule)}); + type_cache_.clear(); } -// Substitutes concrete types into a target template string using the provided -// type mapping. Each template parameter in `tgt_template` is replaced with its -// corresponding instantiated type from `types`. -// -// Example: -// types = { {"i32"} } -// tgt_template = "Vec" -// result = "Vec" -std::string instantiateTgt(const std::vector> &types, +std::string instantiateTgt(const Bindings &bindings, const std::string &tgt_template) { - assert(types.size() <= TranslationRule::kMaxGenerics && - "template placeholder exceeds kMaxGenerics"); std::string instantiated_template = tgt_template; std::string::size_type pos = 0; while ((pos = instantiated_template.find('T', pos)) != std::string::npos) { @@ -360,7 +120,21 @@ std::string instantiateTgt(const std::vector> &types, ++pos; continue; } - const auto &repl = types.at(instantiated_template[pos + 1] - '1').value(); + unsigned n = instantiated_template[pos + 1] - '0'; + assert(n < bindings.size() && bindings[n] && + "target uses a template parameter the use site does not bind"); + const auto &bound = *bindings[n]; + std::string repl; + if (bound.value) { + repl = *bound.value; + } else { + assert(!bound.type.isNull() && "template parameter bound to a non-type"); + repl = Map(bound.type); + if (repl.empty()) { + llvm::errs() << "cpp_type: " << ToString(bound.type) << '\n'; + assert(0 && "Type is not present in types_"); + } + } instantiated_template.replace(pos, 2, repl); pos += repl.length(); } @@ -368,64 +142,143 @@ std::string instantiateTgt(const std::vector> &types, } template -std::pair>> -search(std::unordered_multimap &map, const std::string &txt, - const std::string &key) { +std::pair search(std::unordered_multimap &map, + const Node &use, const std::string &key) { auto [it, end] = map.equal_range(key); T *rule = nullptr; - std::vector> subs; - + std::vector best; + unsigned specificity = 0; for (; it != end; ++it) { - auto &this_rule = it->second; - auto this_subs = matchTemplate(this_rule.src, txt); - if (!this_subs) { + const auto &src = it->second.src; + const auto &ir = *src.ir; + std::vector these(src.num_params + 1); + if (!Match(ir, use, these)) { + continue; + } + if (!rule || ir.specificity() > specificity) { + rule = &it->second; + best = std::move(these); + specificity = ir.specificity(); + } + } + Bindings bindings; + for (const auto *bound : best) { + if (!bound) { + bindings.emplace_back(); continue; } - // tie breaker: prefer more specific rules (usually the longer ones) - if (!rule || this_rule.src.size() > rule->src.size()) { - rule = &this_rule; - subs = *std::move(this_subs); + std::optional value; + if (bound->kind == Kind::kValue) { + value = bound->name; } + bindings.push_back(Binding{bound->type, std::move(value)}); } - return {rule, std::move(subs)}; + return {rule, std::move(bindings)}; } -TranslationRule::ExprRule *search(const clang::Expr *expr) { - if (RefersToUserDefinedDecl(expr)) { - return nullptr; +std::pair search(const clang::Expr *expr) { + if (auto it = expr_cache_.find(expr); it != expr_cache_.end()) { + return it->second; } - auto qualified_name = ToString(expr); - auto [rule, subs] = - search(exprs_, qualified_name, GetExprMapKey(qualified_name)); - log() << "search expr " << qualified_name << ", result:\n"; - if (rule) { - rule->dump(); - } else { - log() << "None\n"; + std::pair res; + if (!RefersToUserDefinedDecl(expr)) { + if (auto use = IrSrc::Builder(*ctx_).FromExpr(expr)) { + res = search(exprs_, *use, use->indexKey()); + log() << "search expr " << use->str() << ", result:\n"; + if (res.first) { + res.first->src.dump(); + res.first->tgt.dump(); + } else { + log() << "None\n"; + } + } } - return rule; + expr_cache_.emplace(expr, res); + return res; } -std::pair>> -search(clang::QualType qual_type) { - auto sugared = ToString(qual_type, ScalarSugar::kPreserve); - if (auto res = search(types_, sugared, GetTypeMapKey(sugared)); res.first) { - log() << "search type " << sugared - << ", result: " << res.first->type_info.type << '\n'; - return res; +NodePtr typeIR(clang::QualType qual_type, bool sugared) { + IrSrc::Builder builder(*ctx_); + if (sugared) { + builder.keep_builtin_typedef = true; + builder.keep_pointee_sugar = [](clang::QualType pointee) { + auto canonical = pointee.getCanonicalType().getDesugaredType(*ctx_); + bool builtin_alias = canonical->isBuiltinType() && + (pointee->getAs() || + pointee->getAs()); + return builtin_alias || Map(pointee) != Map(canonical); + }; + } + auto node = builder.FromType(qual_type); + while (auto *qual = llvm::dyn_cast(node.get())) { + node = std::move(qual->operand); } - auto type = ToString(qual_type); - if (type == sugared) { - log() << "search type " << type << ", result: None\n"; - return {}; + return node; +} + +std::pair search(clang::QualType qual_type) { + auto *cache_key = qual_type.getAsOpaquePtr(); + if (auto it = type_cache_.find(cache_key); it != type_cache_.end()) { + return it->second; } - auto res = search(types_, type, GetTypeMapKey(type)); - log() << "search type " << type - << ", result: " << (res.first ? res.first->type_info.type : "None") - << '\n'; + std::pair res; + for (bool sugared : {true, false}) { + auto use = typeIR(qual_type, sugared); + auto key = use->indexKey(); + auto [rule, bindings] = search(types_, *use, key); + if (!rule && !key.empty()) { + std::tie(rule, bindings) = search(types_, *use, ""); + } + if (rule) { + log() << "search type " << use->str() + << ", result: " << rule->tgt.type_info.type << '\n'; + res = {&rule->tgt, std::move(bindings)}; + break; + } + } + if (!res.first) { + log() << "search type " << ToString(qual_type) << ", result: None\n"; + } + type_cache_.emplace(cache_key, res); return res; } +void validate(const std::string &name, const ExprRule &rule) { + const auto &[src, tgt] = rule; + if (tgt.usesInit() && !src.init_type.valid()) { + llvm::errs() << name << '\n'; + tgt.dump(); + llvm::report_fatal_error( + "Expr rule uses init but its src pack is not declared as Init"); + } + + for (size_t i = 0, e = tgt.generics.size(); i < e; ++i) { + if (!src.ir->hasParam(i + 1)) { + llvm::errs() << name << '\n'; + tgt.dump(); + llvm::errs() << "generic T" << (i + 1) + << " declared but missing from src: " << src.ir->str() + << '\n'; + llvm::report_fatal_error("Absent generic from src"); + } + } +} + +template +Src takeSrc(std::unordered_map &src, const std::string &name, + const std::filesystem::path &dir) { + auto it = src.find(name); + if (it == src.end()) { + llvm::errs() << "ERROR: " << dir.string() << ": rule " << name + << " has no entry in ir_src.json\n"; + std::exit(EXIT_FAILURE); + } + auto rule = std::move(it->second); + src.erase(it); + return rule; +} + void addRulesFromDirectory(const std::filesystem::path &dir, Model model) { namespace fs = std::filesystem; for (const auto &entry : fs::directory_iterator(dir)) { @@ -433,121 +286,67 @@ void addRulesFromDirectory(const std::filesystem::path &dir, Model model) { assert(fs::exists(path / "ir_src.json") && (fs::exists(path / "ir_unsafe.json") || fs::exists(path / "ir_refcount.json"))); - auto [expr_rules, type_rules] = TranslationRule::Load(path, model); - if (expr_rules.empty() && type_rules.empty()) { + auto src = IrSrc::Load(path); + auto tgt = IrTgt::Load(path, model); + if (tgt.exprs.empty() && tgt.types.empty()) { log() << "No rules found in " << path << '\n'; continue; } - for (auto &[_, rule] : expr_rules) { - exprs_.emplace(GetExprMapKey(rule.src), std::move(rule)); + for (auto &[name, rule] : tgt.exprs) { + ExprRule paired{takeSrc(src.exprs, name, path), std::move(rule)}; + validate(name, paired); + auto key = paired.src.ir->indexKey(); + exprs_.emplace(std::move(key), std::move(paired)); } - for (auto &[_, rule] : type_rules) { - auto key = GetTypeMapKey(rule.src); + for (auto &[name, rule] : tgt.types) { + TypeRule paired{takeSrc(src.types, name, path), std::move(rule)}; + auto key = paired.src.ir->indexKey(); auto [begin, end] = types_.equal_range(key); for (auto it = begin; it != end; ++it) { - if (it->second.src == rule.src) { + if (*it->second.src.ir == *paired.src.ir) { llvm::errs() << "ERROR: duplicate type rule for C++ type '" - << rule.src << "': maps to both '" - << it->second.type_info.type << "' and '" - << rule.type_info.type << "'\n"; + << paired.src.ir->str() << "': maps to both '" + << it->second.tgt.type_info.type << "' and '" + << paired.tgt.type_info.type << "'\n"; std::exit(EXIT_FAILURE); } } - types_.emplace(std::move(key), std::move(rule)); - } - } -} - -clang::QualType normalizeQualType(clang::QualType qual_type) { - assert(ctx_); - - bool isLRef = qual_type->isLValueReferenceType(); - bool isRRef = qual_type->isRValueReferenceType(); - qual_type = qual_type.getNonReferenceType(); - - clang::Qualifiers qualifiers = qual_type.getQualifiers(); - - while (true) { - if (const auto *attributed = - llvm::dyn_cast(qual_type)) { - qual_type = attributed->getModifiedType(); - continue; - } - if (const auto *dcltype = llvm::dyn_cast(qual_type)) { - qual_type = dcltype->getUnderlyingType(); - continue; + types_.emplace(std::move(key), std::move(paired)); } - break; - } - - if (llvm::isa(qual_type)) { - qual_type = qual_type.getCanonicalType(); - } - - qual_type = qual_type.withFastQualifiers(qualifiers.getFastQualifiers()); - if (qualifiers.hasNonFastQualifiers()) { - qual_type = ctx_->getQualifiedType(qual_type, qualifiers); - } - - if (isLRef) { - qual_type = ctx_->getLValueReferenceType(qual_type); - } - - if (isRRef) { - qual_type = ctx_->getRValueReferenceType(qual_type); - } - - return qual_type.getCanonicalType().getUnqualifiedType().getDesugaredType( - *ctx_); -} - -std::string mapTypeStringRecursive(const std::string &cpp_type) { - auto [rule, subs] = search(types_, cpp_type, GetTypeMapKey(cpp_type)); - if (!rule) { - llvm::errs() << "cpp_type: " << cpp_type << '\n'; - assert(0 && "Type is not present in types_"); - } - for (auto &ty : subs) { - if (ty) { - ty = mapTypeStringRecursive(*ty); + if (!src.exprs.empty() || !src.types.empty()) { + llvm::errs() << "ERROR: " << path.string() + << ": ir_src.json has rules without a target rule\n"; + std::exit(EXIT_FAILURE); } } - return instantiateTgt(subs, rule->type_info.type); -} - -std::string normalizeTranslationRule(std::string rule) { - // Detach pointer from double reference. Useful for matching translation - // rules. - ReplaceAll(rule, "*&&", "* &&"); - - static const std::array, 1> - normalization_rules{{ - // Ignore constant template parameters, i.e. replace them with _. - {std::regex(R"(\b\d+\b)"), "_"}, - }}; - - for (const auto &r : normalization_rules) { - rule = std::regex_replace(rule, r.first, r.second); - } - - return rule; } } // namespace PushASTContext::PushASTContext(clang::ASTContext &ctx) : prev_(ctx_) { ctx_ = &ctx; + clearCaches(); +} +PushASTContext::~PushASTContext() { + ctx_ = prev_; + clearCaches(); } -PushASTContext::~PushASTContext() { ctx_ = prev_; } bool Contains(clang::QualType qual_type) { return search(qual_type).first != nullptr; } -bool Contains(const clang::Expr *expr) { return search(expr) != nullptr; } +bool Contains(const clang::Expr *expr) { return search(expr).first != nullptr; } + +const IrTgt::ExprRule *GetExprRule(const clang::Expr *expr) { + auto rule = search(expr).first; + return rule ? &rule->tgt : nullptr; +} -const TranslationRule::ExprRule *GetExprRule(const clang::Expr *expr) { - return search(expr); +const IrSrc::InitTypeLocation &GetInitType(const clang::Expr *expr) { + auto rule = search(expr).first; + assert(rule && "expression must have a translation rule"); + return rule->src.init_type; } bool IsLibcPassthrough(const clang::Expr *expr) { @@ -566,7 +365,7 @@ bool IsLibcPassthrough(const clang::Expr *expr) { std::string MapFunctionName(const clang::FunctionDecl *decl) { assert(decl); if (!IsUserDefinedDecl(decl) && - exprs_.contains(GetExprMapKey(ToString(decl)))) { + exprs_.contains(IrSrc::Builder(*ctx_).FromDecl(decl)->indexKey())) { return std::format("libcc2rs::{}_{}", decl->getNameAsString(), model_ == Model::kRefCount ? "refcount" : "unsafe"); } @@ -574,41 +373,26 @@ std::string MapFunctionName(const clang::FunctionDecl *decl) { } std::string InstantiateTemplate(const clang::Expr *expr, unsigned n) { - auto expr_str = ToString(expr); - auto [rule, subs] = search(exprs_, expr_str, GetExprMapKey(expr_str)); + auto [rule, bindings] = search(expr); auto text = std::format("T{}", n); if (!rule) { return text; } - auto &ty = subs.at(n - 1); - if (ty) { - ty = mapTypeStringRecursive(*ty); - } - return instantiateTgt(subs, text); + return instantiateTgt(bindings, text); } std::string Map(clang::QualType qual_type) { - auto [rule, subs] = search(qual_type); + auto [rule, bindings] = search(qual_type); if (rule) { - for (auto &ty : subs) { - if (ty) { - ty = mapTypeStringRecursive(*ty); - } - } - return instantiateTgt(subs, rule->type_info.type); + return instantiateTgt(bindings, rule->type_info.type); } return {}; } std::string MapInitializer(clang::QualType qual_type) { - auto [rule, subs] = search(qual_type); + auto [rule, bindings] = search(qual_type); if (rule && !rule->initializer.empty()) { - for (auto &ty : subs) { - if (ty) { - ty = mapTypeStringRecursive(*ty); - } - } - return instantiateTgt(subs, rule->initializer); + return instantiateTgt(bindings, rule->initializer); } return {}; } @@ -634,26 +418,20 @@ void SetDerives(clang::QualType qual_type, std::vector derives) { } } bool ReturnsPointer(const clang::Expr *expr) { - auto rule = search(expr); - return rule && rule->return_type.is_pointer(); + auto rule = search(expr).first; + return rule && rule->tgt.return_type.is_pointer(); } -const TranslationRule::TypeInfo &GetParamInfo(const clang::Expr *expr, - unsigned index) { - auto rule = search(expr); +const IrTgt::TypeInfo &GetParamInfo(const clang::Expr *expr, unsigned index) { + auto rule = search(expr).first; assert(rule && "expression must have a translation rule"); - return rule->params.at(index); + return rule->tgt.params.at(index); } std::string GetParamType(const clang::Expr *expr, unsigned index) { - auto expr_str = ToString(expr); - auto [rule, subs] = search(exprs_, expr_str, GetExprMapKey(expr_str)); - for (auto &ty : subs) { - if (ty) { - ty = mapTypeStringRecursive(*ty); - } - } - return instantiateTgt(subs, rule->params.at(index).type); + auto [rule, bindings] = search(expr); + assert(rule && "expression must have a translation rule"); + return instantiateTgt(bindings, rule->tgt.params.at(index).type); } bool ParamIsPointer(const clang::Expr *expr, unsigned index) { @@ -682,10 +460,11 @@ clang::QualType GetTypeForDecl(const clang::NamedDecl *decl) { } void AddRuleForUserDefinedType(clang::NamedDecl *decl) { - auto cpp_name = ToString(GetTypeForDecl(decl)); - auto rs_name = ToRustName(cpp_name); + auto type = ctx_->getCanonicalTagType(clang::cast(decl)); + auto ptr = ctx_->getPointerType(type); + auto rs_name = ToRustName(ToString(GetTypeForDecl(decl))); - AddTypeRule(cpp_name, TranslationRule::TypeRule::Plain(rs_name)); + AddTypeRule(type, IrTgt::TypeRule::Plain(rs_name)); if (auto record_decl = llvm::dyn_cast(decl)) { // Forward declaration @@ -697,23 +476,21 @@ void AddRuleForUserDefinedType(clang::NamedDecl *decl) { if (cxx_decl->isAbstract()) { switch (model_) { case Model::kUnsafe: - AddTypeRule(cpp_name + " *", TranslationRule::TypeRule::UnsafePtr( - "*mut dyn " + rs_name)); + AddTypeRule(ptr, IrTgt::TypeRule::UnsafePtr("*mut dyn " + rs_name)); break; case Model::kRefCount: - AddTypeRule(cpp_name + " *", TranslationRule::TypeRule::RefcountPtr( - "PtrDyn')); + AddTypeRule( + ptr, IrTgt::TypeRule::RefcountPtr("PtrDyn')); break; } } else { switch (model_) { case Model::kUnsafe: - AddTypeRule(cpp_name + " *", - TranslationRule::TypeRule::UnsafePtr("*mut " + rs_name)); + AddTypeRule(ptr, IrTgt::TypeRule::UnsafePtr("*mut " + rs_name)); break; case Model::kRefCount: - AddTypeRule(cpp_name + " *", TranslationRule::TypeRule::RefcountPtr( - "Ptr<" + rs_name + '>')); + AddTypeRule(ptr, + IrTgt::TypeRule::RefcountPtr("Ptr<" + rs_name + '>')); break; } } @@ -740,37 +517,9 @@ std::string ToRustName(std::string name) { return name; } -std::string ToString(clang::QualType qual_type, ScalarSugar sugar) { +std::string ToString(clang::QualType qual_type) { assert(ctx_); - if (sugar == ScalarSugar::kPreserve) { - clang::QualType t = qual_type; - if (const auto *decltype_type = - clang::dyn_cast(t.getTypePtr())) { - t = decltype_type->getUnderlyingType(); - } - if (const auto *typedef_type = t->getAs()) { - if (t.getCanonicalType()->isBuiltinType()) { - return typedef_type->getDecl()->getNameAsString(); - } - } else if (const auto *predef = t->getAs()) { - return predef->getIdentifier()->getName().str(); - } else if (const auto *ptr = t->getAs()) { - auto pointee = ptr->getPointeeType(); - auto canonical = pointee.getCanonicalType().getDesugaredType(*ctx_); - bool builtin_alias = canonical->isBuiltinType() && - (pointee->getAs() || - pointee->getAs()); - if (!builtin_alias && Map(pointee) == Map(canonical)) { - pointee = canonical; - } - std::string out; - llvm::raw_string_ostream os(out); - ctx_->getPointerType(pointee).print(os, getPrintPolicy()); - return normalizeTranslationRule(std::move(out)); - } - } - if (auto cxx_record_decl = qual_type->getAsCXXRecordDecl()) { if (cxx_record_decl->isLambda()) { return ToString(cxx_record_decl->getLambdaCallOperator()); @@ -795,15 +544,8 @@ std::string ToString(clang::QualType qual_type, ScalarSugar sugar) { std::string type; llvm::raw_string_ostream os(type); - normalizeQualType(qual_type).print(os, getPrintPolicy()); - return normalizeTranslationRule(std::move(type)); -} - -bool HasFunctionParameterPack(const clang::FunctionDecl *decl) { - if (auto *primary = decl->getPrimaryTemplate()) { - decl = primary->getTemplatedDecl(); - } - return decl->getNumParams() && decl->parameters().back()->isParameterPack(); + qual_type.getCanonicalType().getUnqualifiedType().print(os, getPrintPolicy()); + return type; } std::string ToString(const clang::NamedDecl *decl) { @@ -840,34 +582,12 @@ std::string ToString(const clang::NamedDecl *decl) { if (!func_decl) { decl->printQualifiedName(os, getPrintPolicy()); - return normalizeTranslationRule(std::move(out)); + return out; } os << ToString(func_decl->getReturnType()) << ' '; - if (const auto op = func_decl->getOverloadedOperator(); - op >= clang::OverloadedOperatorKind::OO_LessLess && - op <= clang::OverloadedOperatorKind::OO_GreaterGreaterEqual) { - // ensure matchTemplate does not consider these operator names when matching - func_decl->getQualifier().print(os, getPrintPolicy()); - os << "operator "; - switch (op) { - case clang::OverloadedOperatorKind::OO_LessLess: - os << "shl"; - break; - case clang::OverloadedOperatorKind::OO_GreaterGreater: - os << "shr"; - break; - case clang::OverloadedOperatorKind::OO_LessLessEqual: - os << "shleq"; - break; - case clang::OverloadedOperatorKind::OO_GreaterGreaterEqual: - os << "shreq"; - break; - default: - assert(0 && "Unexpected overloaded operator kind"); - } - } else if (const auto *method_decl = - llvm::dyn_cast(func_decl)) { + if (const auto *method_decl = + llvm::dyn_cast(func_decl)) { if (method_decl->getParent()->isLambda() && method_decl->getOverloadedOperator() == clang::OO_Call) { func_decl->printName(os, getPrintPolicy()); @@ -878,27 +598,13 @@ std::string ToString(const clang::NamedDecl *decl) { func_decl->printQualifiedName(os, getPrintPolicy()); } - bool has_pack = HasFunctionParameterPack(func_decl); - unsigned num_params = func_decl->getNumParams(); - if (has_pack) { - const auto *primary = func_decl->getPrimaryTemplate(); - num_params = - (primary ? primary->getTemplatedDecl() : func_decl)->getNumParams() - 1; - } - os << '('; - for (unsigned i = 0; i < num_params; ++i) { + for (unsigned i = 0, e = func_decl->getNumParams(); i < e; ++i) { if (i) { os << ", "; } os << ToString(func_decl->getParamDecl(i)->getType()); } - if (has_pack) { - if (num_params) { - os << ", "; - } - os << kPackMarker; - } if (func_decl->isVariadic()) { if (func_decl->getNumParams()) { os << ", "; @@ -927,7 +633,7 @@ std::string ToString(const clang::NamedDecl *decl) { } } - return normalizeTranslationRule(std::move(out)); + return out; } std::string ToString(const clang::Expr *expr) { @@ -937,53 +643,15 @@ std::string ToString(const clang::Expr *expr) { expr = expr->IgnoreParenImpCasts(); - if (llvm::isa(expr) && - expr->getBeginLoc().isMacroID()) { - auto &sm = ctx_->getSourceManager(); - auto name = clang::Lexer::getImmediateMacroName(expr->getBeginLoc(), sm, - ctx_->getLangOpts()); - if (!name.empty()) { - return name.str(); - } - } - if (const auto *CE = llvm::dyn_cast(expr)) { if (const auto *decl = CE->getDirectCallee()) { return ToString(decl); } } - if (const auto *ctor = llvm::dyn_cast(expr)) { - if (const auto *ctor_decl = ctor->getConstructor()) { - return ToString(ctor_decl); - } - assert(0 && "expr is a CXXConstructExpr but could not get constructor"); - } - if (const auto *ME = llvm::dyn_cast(expr)) { if (const auto *member_decl = llvm::dyn_cast(ME->getMemberDecl())) { - if (const auto *method_decl = - llvm::dyn_cast(member_decl)) { - return ToString(method_decl); - } - if (ME->isArrow()) { - auto *base = ME->getBase()->IgnoreParenImpCasts(); - if (auto *op = llvm::dyn_cast(base)) { - if (op->getOperator() == clang::OO_Arrow) { - return ToString(op->getArg(0)->getType()) + "->" + - ToString(member_decl); - } - } - } else if (auto for_range = GetParentForRange(*ctx_, ME)) { - if (ToString(for_range->getRangeInit()->getType()) - .starts_with("std::map<")) { - auto iter_type = GetForRangeIteratorType(for_range); - if (!iter_type.isNull()) { - return ToString(iter_type) + "->" + ToString(member_decl); - } - } - } return ToString(member_decl); } assert(0 && "expr is a MemberExpr but could not get named decl"); @@ -1001,14 +669,6 @@ std::string ToString(const clang::Expr *expr) { return ""; } - if (const auto *uop = llvm::dyn_cast(expr)) { - auto sub = ToString(uop->getSubExpr()); - std::string_view opcode = - clang::UnaryOperator::getOpcodeStr(uop->getOpcode()); - return uop->isPostfix() ? std::format("{}{}", sub, opcode) - : std::format("{}{}", opcode, sub); - } - return "Unhandled case in ToString"; } @@ -1016,6 +676,7 @@ void LoadTranslationRules(Model model, clang::ASTContext &ctx, const std::string &rules_dir) { ctx_ = &ctx; model_ = model; + clearCaches(); if (translation_rules_loaded_) { return; diff --git a/cpp2rust/converter/mapper.h b/cpp2rust/converter/mapper.h index dbd6380f86..6be03215b4 100644 --- a/cpp2rust/converter/mapper.h +++ b/cpp2rust/converter/mapper.h @@ -10,7 +10,8 @@ #include #include "converter/factory.h" -#include "converter/translation_rule.h" +#include "converter/rules/ir_src.h" +#include "converter/rules/ir_tgt.h" namespace cpp2rust::Mapper { class PushASTContext { @@ -29,7 +30,8 @@ bool Contains(const clang::Expr *expr); std::string Map(clang::QualType qual_type); std::string MapInitializer(clang::QualType qual_type); -const TranslationRule::ExprRule *GetExprRule(const clang::Expr *expr); +const IrTgt::ExprRule *GetExprRule(const clang::Expr *expr); +const IrSrc::InitTypeLocation &GetInitType(const clang::Expr *expr); bool IsLibcPassthrough(const clang::Expr *expr); std::string MapFunctionName(const clang::FunctionDecl *decl); std::string InstantiateTemplate(const clang::Expr *expr, unsigned n); @@ -41,16 +43,8 @@ bool MapsToRefcountPointer(clang::QualType qual_type); const std::vector *MappedDerives(clang::QualType qual_type); void SetDerives(clang::QualType qual_type, std::vector derives); -enum class ScalarSugar { - kDesugar, - kPreserve, -}; - -bool HasFunctionParameterPack(const clang::FunctionDecl *decl); - clang::QualType GetTypeForDecl(const clang::NamedDecl *decl); -std::string ToString(clang::QualType qual_type, - ScalarSugar sugar = ScalarSugar::kDesugar); +std::string ToString(clang::QualType qual_type); std::string ToString(const clang::Expr *expr); std::string ToString(const clang::NamedDecl *decl); std::string ToRustName(std::string name); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 6ce2ca6055..e9fb51fc84 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -2761,11 +2761,11 @@ bool ConverterRefCount::IsReferenceType(const clang::Expr *expr) const { } std::string ConverterRefCount::ConvertMappedMethodCall( - clang::Expr *expr, const TranslationRule::MethodCallFragment &mc, - clang::Expr **args, unsigned num_args, TempMaterializationCtx *ctx) { + clang::Expr *expr, const IrTgt::MethodCallFragment &mc, clang::Expr **args, + unsigned num_args, TempMaterializationCtx *ctx) { auto receiver_ph = mc.getReceiverPlaceholder(); - if (!receiver_ph || receiver_ph->access == TranslationRule::Access::kBorrow || - receiver_ph->access == TranslationRule::Access::kMove) { + if (!receiver_ph || receiver_ph->access == IrTgt::Access::kBorrow || + receiver_ph->access == IrTgt::Access::kMove) { return Converter::ConvertMappedMethodCall(expr, mc, args, num_args, ctx); } diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index 0b410404ea..0b92d299e3 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -239,11 +239,10 @@ class ConverterRefCount final : public Converter { bool IsReferenceType(const clang::Expr *expr) const override; - std::string - ConvertMappedMethodCall(clang::Expr *expr, - const TranslationRule::MethodCallFragment &mc, - clang::Expr **args, unsigned num_args, - TempMaterializationCtx *ctx) override; + std::string ConvertMappedMethodCall(clang::Expr *expr, + const IrTgt::MethodCallFragment &mc, + clang::Expr **args, unsigned num_args, + TempMaterializationCtx *ctx) override; private: void SetUFCSReceiver(clang::Expr *base, bool is_arrow, diff --git a/cpp2rust/converter/rules/ir.cpp b/cpp2rust/converter/rules/ir.cpp new file mode 100644 index 0000000000..7d12d3873a --- /dev/null +++ b/cpp2rust/converter/rules/ir.cpp @@ -0,0 +1,39 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include "converter/rules/ir.h" + +#include +#include + +#include + +namespace cpp2rust::Ir { + +std::optional ReadJSON(const std::filesystem::path &path, + bool required) { + auto buf = llvm::MemoryBuffer::getFile(path.string()); + if (!buf) { + if (required) { + llvm::errs() << "ERROR: missing " << path << '\n'; + std::exit(EXIT_FAILURE); + } + return std::nullopt; + } + + auto parsed = llvm::json::parse((*buf)->getBuffer()); + if (!parsed) { + llvm::errs() << "ERROR: failed to parse " << path << ": " + << llvm::toString(parsed.takeError()) << '\n'; + std::exit(EXIT_FAILURE); + } + + auto *root = parsed->getAsObject(); + if (!root) { + llvm::errs() << "ERROR: " << path << " is not a JSON object\n"; + std::exit(EXIT_FAILURE); + } + return std::move(*root); +} + +} // namespace cpp2rust::Ir diff --git a/cpp2rust/converter/rules/ir.h b/cpp2rust/converter/rules/ir.h new file mode 100644 index 0000000000..3084efd5d8 --- /dev/null +++ b/cpp2rust/converter/rules/ir.h @@ -0,0 +1,45 @@ +#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 + +namespace cpp2rust::Ir { + +static inline constexpr unsigned kMaxGenerics = 9; + +template struct Rules { + std::unordered_map exprs; + std::unordered_map types; +}; + +std::optional ReadJSON(const std::filesystem::path &path, + bool required); + +template +void LoadJSON(Rules &rules, const std::filesystem::path &path, + bool required, ParseExpr parse_expr, ParseType parse_type) { + auto root = ReadJSON(path, required); + if (!root) { + return; + } + for (auto &[key, value] : *root) { + const auto *obj = value.getAsObject(); + assert(obj && "rule entry must be an object"); + auto name = key.str(); + if (name[0] == 'f') { + rules.exprs[std::move(name)] = parse_expr(*obj); + } else if (name[0] == 't') { + rules.types[std::move(name)] = parse_type(*obj); + } + } +} + +} // namespace cpp2rust::Ir diff --git a/cpp2rust/converter/rules/ir_src.cpp b/cpp2rust/converter/rules/ir_src.cpp new file mode 100644 index 0000000000..ec6d84faf7 --- /dev/null +++ b/cpp2rust/converter/rules/ir_src.cpp @@ -0,0 +1,330 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include "converter/rules/ir_src.h" + +#include +#include +#include + +#include "logging.h" + +namespace cpp2rust::IrSrc { + +namespace { + +using Kind = Node::Kind; +using llvm::cast; +using llvm::dyn_cast; + +constexpr std::pair kKindNames[] = { + {Kind::kParam, "param"}, {Kind::kValue, "value"}, + {Kind::kBuiltin, "builtin"}, {Kind::kRecord, "record"}, + {Kind::kEnum, "enum"}, {Kind::kTypedef, "typedef"}, + {Kind::kConst, "const"}, {Kind::kVolatile, "volatile"}, + {Kind::kPointer, "ptr"}, {Kind::kLRef, "lref"}, + {Kind::kRRef, "rref"}, {Kind::kArray, "array"}, + {Kind::kFunctionType, "fntype"}, {Kind::kOpaque, "opaque"}, + {Kind::kFunction, "function"}, {Kind::kDecl, "decl"}, + {Kind::kMacro, "macro"}, {Kind::kUnary, "unary"}, + {Kind::kArrow, "arrow"}, {Kind::kPackParams, "pack_params"}, +}; + +const char *kindName(Kind kind) { + for (const auto &[k, name] : kKindNames) { + if (k == kind) { + return name; + } + } + assert(0 && "unknown IR node kind"); + return ""; +} + +Kind kindFromName(llvm::StringRef name) { + for (const auto &[k, n] : kKindNames) { + if (name == n) { + return k; + } + } + llvm::errs() << "ERROR: unknown IR node kind '" << name << "'\n"; + std::exit(EXIT_FAILURE); +} + +NodePtr ParseNodeJSON(const llvm::json::Value &value) { + const auto *obj = value.getAsObject(); + assert(obj && "IR node must be an object"); + auto kind = kindFromName(*obj->getString("kind")); + auto name = obj->getString("name").value_or("").str(); + NodePtr node; + switch (kind) { + case Kind::kParam: + node = std::make_unique(*obj->getInteger("param")); + break; + case Kind::kConst: + case Kind::kVolatile: + node = std::make_unique(kind, nullptr); + break; + case Kind::kPointer: + case Kind::kLRef: + case Kind::kRRef: + node = std::make_unique(kind, nullptr); + break; + case Kind::kArray: + node = std::make_unique(nullptr); + break; + case Kind::kRecord: + node = std::make_unique(std::move(name)); + break; + case Kind::kDecl: + node = std::make_unique(std::move(name)); + break; + case Kind::kFunction: + case Kind::kFunctionType: { + auto fn = std::make_unique(kind, std::move(name)); + fn->variadic = obj->getBoolean("variadic").value_or(false); + fn->is_const = obj->getBoolean("is_const").value_or(false); + fn->is_volatile = obj->getBoolean("is_volatile").value_or(false); + fn->ref = obj->getString("ref").value_or("").str(); + node = std::move(fn); + break; + } + case Kind::kUnary: + node = std::make_unique(std::move(name), nullptr); + break; + case Kind::kArrow: + node = std::make_unique(nullptr, nullptr); + break; + default: + node = std::make_unique(kind, std::move(name)); + break; + } + forEachField(*node, *node, [&](const char *key, auto &field, auto &) { + if constexpr (kIsNodeList) { + if (const auto *list = obj->getArray(key)) { + for (const auto &child : *list) { + field.push_back(ParseNodeJSON(child)); + } + } + } else if (const auto *child = obj->get(key)) { + field = ParseNodeJSON(*child); + } + return true; + }); + return node; +} + +unsigned NumParams(const Node &ir) { + unsigned n = 0; + ir.forEachParam([&](unsigned param) { n = std::max(n, param); }); + assert(n <= Ir::kMaxGenerics && "template placeholder exceeds kMaxGenerics"); + return n; +} + +ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { + ExprRule rule; + rule.ir = ParseNodeJSON(*obj.get("ir")); + rule.num_params = NumParams(*rule.ir); + if (const auto *init_type = obj.getObject("init_type")) { + rule.init_type = InitTypeLocation{ + (unsigned)*init_type->getInteger("depth"), + (unsigned)*init_type->getInteger("index"), + }; + } + return rule; +} + +TypeRule ParseTypeRuleJSON(const llvm::json::Object &obj) { + TypeRule rule; + rule.ir = ParseNodeJSON(*obj.get("ir")); + rule.num_params = NumParams(*rule.ir); + return rule; +} + +} // namespace + +bool Node::operator==(const Node &other) const { + return shallowEquals(other) && + zipChildren(*this, other, [](const NodePtr &a, const NodePtr &b) { + return *a == *b; + }); +} + +bool Node::shallowEquals(const Node &other) const { + if (kind != other.kind || name != other.name) { + return false; + } + if (const auto *param = dyn_cast(this)) { + return param->param == cast(other).param; + } + if (const auto *fn = dyn_cast(this)) { + const auto &other_fn = cast(other); + return fn->variadic == other_fn.variadic && + fn->is_const == other_fn.is_const && + fn->is_volatile == other_fn.is_volatile && fn->ref == other_fn.ref; + } + return true; +} + +std::string Node::indexKey() const { + switch (kind) { + case Kind::kFunction: + case Kind::kDecl: + case Kind::kRecord: + case Kind::kEnum: + case Kind::kTypedef: + case Kind::kBuiltin: + return name; + case Kind::kMacro: + return "macro:" + name; + case Kind::kUnary: + return "unary" + name + ":" + cast(this)->operand->indexKey(); + case Kind::kArrow: + return cast(this)->member->indexKey(); + case Kind::kConst: + case Kind::kVolatile: + return cast(this)->operand->indexKey(); + case Kind::kPointer: + return "*" + cast(this)->pointee->indexKey(); + case Kind::kLRef: + return "&" + cast(this)->pointee->indexKey(); + case Kind::kRRef: + return "&&" + cast(this)->pointee->indexKey(); + case Kind::kArray: + return "[]" + cast(this)->element->indexKey(); + default: + return ""; + } +} + +unsigned Node::specificity() const { + unsigned n = kind == Kind::kParam ? 0 : 1; + zipChildren(*this, *this, [&](const NodePtr &child, const NodePtr &) { + n += child->specificity(); + return true; + }); + return n; +} + +bool Node::hasParam(unsigned n) const { + if (const auto *param = dyn_cast(this)) { + return param->param == n; + } + return !zipChildren(*this, *this, [&](const NodePtr &child, const NodePtr &) { + return !child->hasParam(n); + }); +} + +void Node::forEachParam(const std::function &fn) const { + if (const auto *param = dyn_cast(this)) { + fn(param->param); + } + zipChildren(*this, *this, [&](const NodePtr &child, const NodePtr &) { + child->forEachParam(fn); + return true; + }); +} + +std::string Node::str() const { + if (const auto *param = dyn_cast(this)) { + return "T" + std::to_string(param->param); + } + if (const auto *qual = dyn_cast(this)) { + return std::string(kindName(kind)) + " " + qual->operand->str(); + } + std::string out; + if (const auto *fn = dyn_cast(this)) { + if (fn->is_const) { + out += "const "; + } + if (fn->is_volatile) { + out += "volatile "; + } + } + out += kindName(kind); + if (!name.empty()) { + out += " " + name; + } + std::vector fields; + forEachField(*this, *this, + [&](const char *key, const auto &field, const auto &) { + if constexpr (kIsNodeList) { + if (!field.empty()) { + std::string list; + for (const auto &child : field) { + list += (list.empty() ? "" : ", ") + child->str(); + } + fields.push_back(std::string(key) + ": [" + list + "]"); + } + } else if (field) { + fields.push_back(std::string(key) + ": " + field->str()); + } + return true; + }); + if (!fields.empty()) { + out += "("; + for (size_t i = 0; i < fields.size(); ++i) { + out += (i ? ", " : "") + fields[i]; + } + out += ")"; + } + return out; +} + +llvm::json::Value ToJSON(const Node &node) { + llvm::json::Object obj{{"kind", kindName(node.kind)}}; + if (!node.name.empty()) { + obj["name"] = node.name; + } + if (const auto *param = dyn_cast(&node)) { + obj["param"] = param->param; + } + if (const auto *fn = dyn_cast(&node)) { + if (fn->is_const) { + obj["is_const"] = true; + } + if (fn->is_volatile) { + obj["is_volatile"] = true; + } + if (fn->variadic) { + obj["variadic"] = true; + } + if (!fn->ref.empty()) { + obj["ref"] = fn->ref; + } + } + forEachField(node, node, + [&](const char *key, const auto &field, const auto &) { + if constexpr (kIsNodeList) { + if (!field.empty()) { + llvm::json::Array list; + for (const auto &child : field) { + list.push_back(ToJSON(*child)); + } + obj[key] = std::move(list); + } + } else if (field) { + obj[key] = ToJSON(*field); + } + return true; + }); + return obj; +} + +void ExprRule::dump() const { + log() << "Matching: " << ir->str() << '\n'; + if (init_type.valid()) { + log() << " init type: depth " << init_type.depth << ", index " + << init_type.index << '\n'; + } +} + +void TypeRule::dump() const { log() << "name: " << ir->str() << '\n'; } + +Rules Load(const std::filesystem::path &dir) { + Rules rules; + Ir::LoadJSON(rules, dir / "ir_src.json", true, ParseExprRuleJSON, + ParseTypeRuleJSON); + return rules; +} + +} // namespace cpp2rust::IrSrc diff --git a/cpp2rust/converter/rules/ir_src.h b/cpp2rust/converter/rules/ir_src.h new file mode 100644 index 0000000000..3924514c23 --- /dev/null +++ b/cpp2rust/converter/rules/ir_src.h @@ -0,0 +1,254 @@ +#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 +#include +#include +#include +#include +#include +#include + +#include "converter/rules/ir.h" + +namespace cpp2rust::IrSrc { + +struct Node; +using NodePtr = std::unique_ptr; + +struct Node { + enum class Kind { + kParam, + kValue, + kBuiltin, + kRecord, + kEnum, + kTypedef, + kConst, + kVolatile, + kPointer, + kLRef, + kRRef, + kArray, + kFunctionType, + kOpaque, + kFunction, + kDecl, + kMacro, + kUnary, + kArrow, + kPackParams, + }; + + const Kind kind; + std::string name; + clang::QualType type; + + explicit Node(Kind kind, std::string name = {}) + : kind(kind), name(std::move(name)) {} + Node(const Node &) = delete; + Node &operator=(const Node &) = delete; + virtual ~Node() = default; + + bool operator==(const Node &other) const; + bool shallowEquals(const Node &other) const; + std::string indexKey() const; + unsigned specificity() const; + void forEachParam(const std::function &fn) const; + bool hasParam(unsigned n) const; + std::string str() const; +}; + +struct ParamNode : Node { + unsigned param; + + explicit ParamNode(unsigned param) : Node(Kind::kParam), param(param) {} + static bool classof(const Node *node) { return node->kind == Kind::kParam; } +}; + +struct QualNode : Node { + NodePtr operand; + + QualNode(Kind kind, NodePtr operand) + : Node(kind), operand(std::move(operand)) {} + static bool classof(const Node *node) { + return node->kind == Kind::kConst || node->kind == Kind::kVolatile; + } +}; + +struct PointerNode : Node { + NodePtr pointee; + + PointerNode(Kind kind, NodePtr pointee) + : Node(kind), pointee(std::move(pointee)) {} + static bool classof(const Node *node) { + return node->kind == Kind::kPointer || node->kind == Kind::kLRef || + node->kind == Kind::kRRef; + } +}; + +struct ArrayNode : Node { + NodePtr element; + NodePtr size; + + explicit ArrayNode(NodePtr element) + : Node(Kind::kArray), element(std::move(element)) {} + static bool classof(const Node *node) { return node->kind == Kind::kArray; } +}; + +struct RecordNode : Node { + NodePtr class_; + std::vector args; + + explicit RecordNode(std::string name) + : Node(Kind::kRecord, std::move(name)) {} + static bool classof(const Node *node) { return node->kind == Kind::kRecord; } +}; + +struct DeclNode : Node { + NodePtr class_; + + explicit DeclNode(std::string name) : Node(Kind::kDecl, std::move(name)) {} + static bool classof(const Node *node) { return node->kind == Kind::kDecl; } +}; + +struct FunctionNode : Node { + NodePtr class_; + NodePtr return_type; + std::vector params; + bool variadic = false; + bool is_const = false; + bool is_volatile = false; + std::string ref; + + explicit FunctionNode(Kind kind, std::string name = {}) + : Node(kind, std::move(name)) {} + static bool classof(const Node *node) { + return node->kind == Kind::kFunction || node->kind == Kind::kFunctionType; + } +}; + +struct UnaryNode : Node { + NodePtr operand; + + UnaryNode(std::string op, NodePtr operand) + : Node(Kind::kUnary, std::move(op)), operand(std::move(operand)) {} + static bool classof(const Node *node) { return node->kind == Kind::kUnary; } +}; + +struct ArrowNode : Node { + NodePtr object; + NodePtr member; + + ArrowNode(NodePtr object, NodePtr member) + : Node(Kind::kArrow), object(std::move(object)), + member(std::move(member)) {} + static bool classof(const Node *node) { return node->kind == Kind::kArrow; } +}; + +template +bool forEachField(A &a, B &b, Fn fn) { + using Kind = Node::Kind; + using llvm::cast; + assert(a.kind == b.kind); + switch (a.kind) { + case Kind::kConst: + case Kind::kVolatile: + return fn("operand", cast(a).operand, cast(b).operand); + case Kind::kPointer: + case Kind::kLRef: + case Kind::kRRef: + return fn("pointee", cast(a).pointee, + cast(b).pointee); + case Kind::kArray: + return fn("element", cast(a).element, + cast(b).element) && + fn("size", cast(a).size, cast(b).size); + case Kind::kRecord: + return fn("class", cast(a).class_, + cast(b).class_) && + fn("args", cast(a).args, cast(b).args); + case Kind::kDecl: + return fn("class", cast(a).class_, cast(b).class_); + case Kind::kFunction: + case Kind::kFunctionType: + return fn("class", cast(a).class_, + cast(b).class_) && + fn("return_type", cast(a).return_type, + cast(b).return_type) && + fn("params", cast(a).params, + cast(b).params); + case Kind::kUnary: + return fn("operand", cast(a).operand, + cast(b).operand); + case Kind::kArrow: + return fn("object", cast(a).object, cast(b).object) && + fn("member", cast(a).member, cast(b).member); + default: + return true; + } +} + +template +inline constexpr bool kIsNodeList = + std::is_same_v, std::vector>; + +template +bool zipChildren(A &a, B &b, Fn fn) { + return forEachField(a, b, [&](const char *, auto &x, auto &y) { + if constexpr (kIsNodeList) { + if (x.size() != y.size()) { + return false; + } + for (std::size_t i = 0; i < x.size(); ++i) { + if (!fn(x[i], y[i])) { + return false; + } + } + return true; + } else { + if (!x || !y) { + return !x && !y; + } + return fn(x, y); + } + }); +} + +llvm::json::Value ToJSON(const Node &node); + +struct InitTypeLocation { + unsigned depth = -1u; + unsigned index = -1u; + + bool valid() const { return depth != -1u; } +}; + +struct ExprRule { + NodePtr ir; + unsigned num_params = 0; + InitTypeLocation init_type; + + void dump() const; +}; + +struct TypeRule { + NodePtr ir; + unsigned num_params = 0; + + void dump() const; +}; + +using Rules = Ir::Rules; + +Rules Load(const std::filesystem::path &dir); + +} // namespace cpp2rust::IrSrc diff --git a/cpp2rust/converter/rules/ir_src_builder.cpp b/cpp2rust/converter/rules/ir_src_builder.cpp new file mode 100644 index 0000000000..26f9489a67 --- /dev/null +++ b/cpp2rust/converter/rules/ir_src_builder.cpp @@ -0,0 +1,355 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include "converter/rules/ir_src_builder.h" + +#include +#include +#include +#include +#include + +#include + +#include "converter/converter_lib.h" + +namespace cpp2rust::IrSrc { + +namespace { + +using Kind = Node::Kind; + +NodePtr qualify(NodePtr node, clang::QualType type) { + if (type.isVolatileQualified()) { + auto qualified = node->type.withVolatile(); + node = std::make_unique(Kind::kVolatile, std::move(node)); + node->type = qualified; + } + if (type.isConstQualified()) { + auto qualified = node->type.withConst(); + node = std::make_unique(Kind::kConst, std::move(node)); + node->type = qualified; + } + return node; +} + +std::string nameOf(const clang::NamedDecl *decl) { + if (const auto *ctor = llvm::dyn_cast(decl)) { + return ctor->getParent()->getName().str(); + } + if (const auto *dtor = llvm::dyn_cast(decl)) { + return "~" + dtor->getParent()->getName().str(); + } + auto name = decl->getDeclName(); + if (name.getNameKind() == clang::DeclarationName::CXXConversionFunctionName) { + return "operator conversion"; + } + if (const auto *tag = llvm::dyn_cast(decl); + tag && !tag->getIdentifier()) { + if (const auto *tdef = tag->getTypedefNameForAnonDecl()) { + return tdef->getName().str(); + } + return "(anonymous)"; + } + return name.getAsString(); +} + +std::string QualifiedName(const clang::NamedDecl *decl) { + std::vector parts{nameOf(decl)}; + for (const auto *dc = decl->getDeclContext(); dc; dc = dc->getParent()) { + if (const auto *ns = llvm::dyn_cast(dc)) { + if (!ns->isInline() && !ns->isAnonymousNamespace()) { + parts.push_back(ns->getName().str()); + } + } else if (const auto *named = llvm::dyn_cast(dc); + named && (llvm::isa(named) || + llvm::isa(named))) { + parts.push_back(nameOf(named)); + } + } + std::string out; + for (auto it = parts.rbegin(); it != parts.rend(); ++it) { + if (!out.empty()) { + out += "::"; + } + out += *it; + } + return out; +} + +std::string tagName(const clang::TagDecl *tag) { + if (!tag->getIdentifier() && !tag->getTypedefNameForAnonDecl()) { + return GetNamedDeclAsString(tag); + } + if (tag->getIdentifier() && tag->getDeclContext()->isFunctionOrMethod()) { + return GetNamedDeclAsString(tag); + } + if (auto renamed = DisambiguateAnonymousTag(tag); !renamed.empty()) { + return renamed; + } + return QualifiedName(tag); +} + +} // namespace + +NodePtr Builder::FromType(clang::QualType type) { return fromType(type, true); } + +NodePtr Builder::fromType(clang::QualType type, bool top) { + if (keep_builtin_typedef && top) { + if (const auto *decltype_type = + llvm::dyn_cast(type.getTypePtr())) { + type = decltype_type->getUnderlyingType(); + } + auto canonical = type.getCanonicalType(); + const clang::NamedDecl *name = nullptr; + if (const auto *typedef_type = type->getAs(); + typedef_type && canonical->isBuiltinType()) { + name = typedef_type->getDecl(); + } + if (name) { + auto node = std::make_unique(Kind::kTypedef, name->getName().str()); + node->type = canonical.getUnqualifiedType(); + return qualify(std::move(node), canonical); + } + if (const auto *predef = type->getAs()) { + auto node = std::make_unique( + Kind::kTypedef, predef->getIdentifier()->getName().str()); + node->type = canonical.getUnqualifiedType(); + return qualify(std::move(node), canonical); + } + if (const auto *ptr = type->getAs(); + ptr && keep_pointee_sugar && + keep_pointee_sugar(ptr->getPointeeType())) { + auto node = std::make_unique( + Kind::kPointer, fromType(ptr->getPointeeType(), true)); + node->type = canonical.getUnqualifiedType(); + return qualify(std::move(node), canonical); + } + } + return fromCanonical(type.getCanonicalType()); +} + +NodePtr Builder::fromCanonical(clang::QualType canonical) { + NodePtr node; + const auto *type = canonical.getTypePtr(); + bool quals_on_element = false; + if (const auto *builtin = llvm::dyn_cast(type)) { + clang::PrintingPolicy policy(ctx_.getLangOpts()); + policy.Bool = true; + node = + std::make_unique(Kind::kBuiltin, builtin->getName(policy).str()); + } else if (const auto *ptr = llvm::dyn_cast(type)) { + node = std::make_unique( + Kind::kPointer, fromType(ptr->getPointeeType(), false)); + } else if (const auto *ref = + llvm::dyn_cast(type)) { + node = std::make_unique( + Kind::kLRef, fromType(ref->getPointeeType(), false)); + } else if (const auto *ref = + llvm::dyn_cast(type)) { + node = std::make_unique( + Kind::kRRef, fromType(ref->getPointeeType(), false)); + } else if (const auto *array = ctx_.getAsConstantArrayType(canonical)) { + auto array_node = + std::make_unique(fromType(array->getElementType(), false)); + array_node->size = std::make_unique( + Kind::kValue, llvm::toString(array->getSize(), 10, false)); + node = std::move(array_node); + quals_on_element = true; + } else if (const auto *array = ctx_.getAsIncompleteArrayType(canonical)) { + node = + std::make_unique(fromType(array->getElementType(), false)); + quals_on_element = true; + } else if (const auto *record = type->getAsRecordDecl()) { + node = fromRecord(record); + } else if (const auto *enum_type = llvm::dyn_cast(type)) { + node = std::make_unique(Kind::kEnum, tagName(enum_type->getDecl())); + } else if (const auto *proto = + llvm::dyn_cast(type)) { + auto fn = std::make_unique(Kind::kFunctionType); + fn->variadic = proto->isVariadic(); + fn->return_type = fromType(proto->getReturnType(), false); + for (auto param : proto->getParamTypes()) { + fn->params.push_back(fromType(param, false)); + } + node = std::move(fn); + } else { + node = std::make_unique(Kind::kOpaque, + canonical.getUnqualifiedType().getAsString()); + } + if (quals_on_element) { + node->type = canonical; + return node; + } + node->type = canonical.getUnqualifiedType(); + return qualify(std::move(node), canonical); +} + +NodePtr Builder::fromRecord(const clang::RecordDecl *decl) { + if (stand_in) { + if (auto param = stand_in(decl)) { + return std::make_unique(*param); + } + } + if (const auto *cxx = llvm::dyn_cast(decl); + cxx && cxx->isLambda()) { + return std::make_unique(Kind::kOpaque, "lambda"); + } + auto node = std::make_unique(tagName(decl)); + node->class_ = classOf(decl); + if (const auto *spec = + llvm::dyn_cast(decl)) { + for (const auto &arg : spec->getTemplateArgs().asArray()) { + node->args.push_back(fromTemplateArg(arg)); + } + } + return node; +} + +NodePtr Builder::fromTemplateArg(const clang::TemplateArgument &arg) { + switch (arg.getKind()) { + case clang::TemplateArgument::Type: + return fromType(arg.getAsType(), false); + case clang::TemplateArgument::Integral: { + auto node = std::make_unique(Kind::kValue, + llvm::toString(arg.getAsIntegral(), 10)); + node->type = arg.getIntegralType(); + return node; + } + default: { + std::string spelling; + llvm::raw_string_ostream os(spelling); + arg.print(clang::PrintingPolicy(ctx_.getLangOpts()), os, + /*IncludeType=*/true); + return std::make_unique(Kind::kOpaque, spelling); + } + } +} + +NodePtr Builder::classOf(const clang::Decl *decl) { + if (const auto *record = + llvm::dyn_cast(decl->getDeclContext())) { + auto node = fromRecord(record); + node->type = ctx_.getCanonicalTagType(record); + return node; + } + return nullptr; +} + +NodePtr Builder::FromDecl(const clang::NamedDecl *decl) { + if (const auto *tmpl = llvm::dyn_cast(decl)) { + decl = tmpl->getTemplatedDecl(); + } + const auto *func = llvm::dyn_cast(decl); + if (!func) { + auto node = std::make_unique(QualifiedName(decl)); + node->class_ = classOf(decl); + return node; + } + + auto node = + std::make_unique(Kind::kFunction, QualifiedName(func)); + node->variadic = func->isVariadic(); + node->class_ = classOf(func); + node->return_type = fromType(func->getReturnType(), false); + bool has_pack = HasFunctionParameterPack(func); + unsigned num_params = func->getNumParams(); + if (has_pack) { + const auto *primary = func->getPrimaryTemplate(); + num_params = + (primary ? primary->getTemplatedDecl() : func)->getNumParams() - 1; + } + for (unsigned i = 0; i < num_params; ++i) { + node->params.push_back(fromType(func->getParamDecl(i)->getType(), false)); + } + if (has_pack) { + node->params.push_back(std::make_unique(Kind::kPackParams)); + } + if (const auto *method = llvm::dyn_cast(func)) { + node->is_const = method->isConst(); + node->is_volatile = method->isVolatile(); + switch (method->getRefQualifier()) { + case clang::RQ_LValue: + node->ref = "&"; + break; + case clang::RQ_RValue: + node->ref = "&&"; + break; + default: + break; + } + } + return node; +} + +NodePtr Builder::FromExpr(const clang::Expr *expr) { + expr = expr->IgnoreParenImpCasts(); + + if (llvm::isa(expr) && + expr->getBeginLoc().isMacroID()) { + auto name = clang::Lexer::getImmediateMacroName( + expr->getBeginLoc(), ctx_.getSourceManager(), ctx_.getLangOpts()); + if (!name.empty()) { + return std::make_unique(Kind::kMacro, name.str()); + } + } + + if (const auto *call = llvm::dyn_cast(expr)) { + if (const auto *callee = call->getDirectCallee()) { + return FromDecl(callee); + } + } + + if (const auto *ctor = llvm::dyn_cast(expr)) { + assert(ctor->getConstructor() && + "expr is a CXXConstructExpr but could not get constructor"); + return FromDecl(ctor->getConstructor()); + } + + if (const auto *member = llvm::dyn_cast(expr)) { + const auto *decl = member->getMemberDecl(); + if (llvm::isa(decl)) { + return FromDecl(decl); + } + auto arrow = [&](clang::QualType object) { + return std::make_unique(FromType(object), FromDecl(decl)); + }; + if (member->isArrow()) { + const auto *base = member->getBase()->IgnoreParenImpCasts(); + if (const auto *op = llvm::dyn_cast(base); + op && op->getOperator() == clang::OO_Arrow) { + return arrow(op->getArg(0)->IgnoreImpCasts()->getType()); + } + } else if (auto for_range = GetParentForRange(ctx_, member)) { + const auto *range = + for_range->getRangeInit()->getType()->getAsCXXRecordDecl(); + if (range && llvm::isa(range) && + QualifiedName(range) == "std::map") { + auto iter_type = GetForRangeIteratorType(for_range); + if (!iter_type.isNull()) { + return arrow(iter_type); + } + } + } + return FromDecl(decl); + } + + if (const auto *ref = llvm::dyn_cast(expr)) { + return FromDecl(ref->getDecl()); + } + + if (const auto *uop = llvm::dyn_cast(expr)) { + auto sub = FromExpr(uop->getSubExpr()); + if (!sub) { + return nullptr; + } + return std::make_unique( + (uop->isPostfix() ? "post" : "") + + clang::UnaryOperator::getOpcodeStr(uop->getOpcode()).str(), + std::move(sub)); + } + + return nullptr; +} + +} // namespace cpp2rust::IrSrc diff --git a/cpp2rust/converter/rules/ir_src_builder.h b/cpp2rust/converter/rules/ir_src_builder.h new file mode 100644 index 0000000000..228f005d2f --- /dev/null +++ b/cpp2rust/converter/rules/ir_src_builder.h @@ -0,0 +1,40 @@ +#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 + +#include "converter/rules/ir_src.h" + +namespace cpp2rust::IrSrc { + +class Builder { +public: + explicit Builder(clang::ASTContext &ctx) : ctx_(ctx) {} + + std::function(const clang::Decl *)> stand_in; + bool keep_builtin_typedef = false; + std::function keep_pointee_sugar; + + NodePtr FromType(clang::QualType type); + NodePtr FromDecl(const clang::NamedDecl *decl); + NodePtr FromExpr(const clang::Expr *expr); + +private: + clang::ASTContext &ctx_; + + NodePtr fromType(clang::QualType type, bool top); + NodePtr fromCanonical(clang::QualType canonical); + NodePtr fromTemplateArg(const clang::TemplateArgument &arg); + NodePtr fromRecord(const clang::RecordDecl *decl); + NodePtr classOf(const clang::Decl *decl); +}; + +} // namespace cpp2rust::IrSrc diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/rules/ir_tgt.cpp similarity index 60% rename from cpp2rust/converter/translation_rule.cpp rename to cpp2rust/converter/rules/ir_tgt.cpp index 34c7696c96..3f7a52a71c 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/rules/ir_tgt.cpp @@ -1,10 +1,9 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. -#include "converter/translation_rule.h" +#include "converter/rules/ir_tgt.h" #include -#include #include #include @@ -12,7 +11,7 @@ #include "logging.h" -namespace cpp2rust::TranslationRule { +namespace cpp2rust::IrTgt { namespace { @@ -152,91 +151,6 @@ TypeRule ParseTypeRuleJSON(const llvm::json::Object &obj) { return rule; } -void LoadTgtFromIR(ExprRules &exprs, TypeRules &types, - const std::filesystem::path &json_path) { - auto buf = llvm::MemoryBuffer::getFile(json_path.string()); - if (!buf) - return; - - auto parsed = llvm::json::parse((*buf)->getBuffer()); - if (!parsed) { - llvm::errs() << "Failed to parse IR JSON: " << json_path << ": " - << llvm::toString(parsed.takeError()) << '\n'; - assert(0); - return; - } - - auto *root = parsed->getAsObject(); - if (!root) - return; - - for (auto &[entry_name, entry_val] : *root) { - auto *obj = entry_val.getAsObject(); - if (!obj) - continue; - - auto name = entry_name.str(); - if (name[0] == 'f') { - exprs[std::move(name)] = ParseExprRuleJSON(*obj); - } else if (name[0] == 't') { - types[std::move(name)] = ParseTypeRuleJSON(*obj); - } - } -} - -void LoadIrSrc(ExprRules &exprs, TypeRules &types, - const std::filesystem::path &json_path) { - auto buf = llvm::MemoryBuffer::getFile(json_path.string()); - if (!buf) { - llvm::errs() << "Missing " << json_path << ", run cpp-rule-preprocessor\n"; - assert(0); - return; - } - - auto parsed = llvm::json::parse((*buf)->getBuffer()); - if (!parsed) { - llvm::errs() << "Failed to parse IR src JSON: " << json_path << ": " - << llvm::toString(parsed.takeError()) << '\n'; - assert(0); - return; - } - - auto *root = parsed->getAsObject(); - if (!root) { - return; - } - - for (auto &[entry_name, entry_val] : *root) { - auto name = entry_name.str(); - auto val = entry_val.getAsString(); - if (name[0] == 'f') { - auto it = exprs.find(name); - if (it == exprs.end()) { - llvm::errs() << name << '\n'; - assert(0 && "ir_src.json expr entry has no matching IR target rule"); - } - if (auto *obj = entry_val.getAsObject()) { - it->second.src = obj->getString("key")->str(); - auto *init_type = obj->getObject("init_type"); - assert(init_type && "ir_src.json expr entry object without init_type"); - it->second.init_type = InitTypeLocation{ - (unsigned)*init_type->getInteger("depth"), - (unsigned)*init_type->getInteger("index"), - }; - continue; - } - it->second.src = val->str(); - } else if (name[0] == 't') { - auto it = types.find(name); - if (it == types.end()) { - llvm::errs() << name << '\n'; - assert(0 && "ir_src.json type entry has no matching IR target rule"); - } - it->second.src = val->str(); - } - } -} - void BodyFragmentDump(const BodyFragment &frag) { if (auto *t = std::get_if(&frag)) { t->dump(); @@ -311,11 +225,6 @@ void MethodCallFragment::dump() const { } void ExprRule::dump() const { - log() << "Matching: " << src << '\n'; - if (init_type.valid()) { - log() << " init type: depth " << init_type.depth << ", index " - << init_type.index << '\n'; - } unsigned i = 0; for (auto &info : params) { log() << " param a" << i++ << ": "; @@ -340,46 +249,7 @@ void ExprRule::dump() const { } } -void ExprRule::validate(const std::string &name) const { - if (src.empty()) { - llvm::errs() << name << '\n'; - dump(); - llvm::report_fatal_error("Expr rule loaded from IR but has no src"); - } - - if (HasInitFragment(body) && !init_type.valid()) { - llvm::errs() << name << '\n'; - dump(); - llvm::report_fatal_error( - "Expr rule uses init but its src pack is not declared as Init"); - } - - if (generics.empty()) - return; - - bool has_generic[kMaxGenerics] = {false}; - for (size_t i = 0, e = src.size(); i < e; ++i) { - auto pos = src.find('T', i); - if (pos == std::string::npos) - break; - auto ch = pos + 1 < e ? src[pos + 1] : '\0'; - if (ch >= '1' && ch <= '9') { - has_generic[ch - '1'] = true; - i = pos + 1; - } - } - - for (size_t i = 0, e = generics.size(); i < e; ++i) { - if (!has_generic[i]) { - llvm::errs() << name << '\n'; - dump(); - llvm::errs() << "generic T" << (i + 1) - << " declared but missing from src: " << src << '\n'; - llvm::report_fatal_error("Absent generic from src"); - } - } -} +bool ExprRule::usesInit() const { return HasInitFragment(body); } void GenericFragment::dump() const { log() << " generic: " << n << '\n'; } @@ -394,7 +264,7 @@ void TypeInfo::dump() const { } void TypeRule::dump() const { - log() << "name: " << src << "\n Rust type: "; + log() << " Rust type: "; type_info.dump(); log() << '\n'; if (!initializer.empty()) { @@ -402,32 +272,15 @@ void TypeRule::dump() const { } } -std::pair Load(const std::filesystem::path &dir, - Model model) { - ExprRules exprs; - TypeRules types; - LoadTgtFromIR(exprs, types, dir / "ir_unsafe.json"); - +Rules Load(const std::filesystem::path &dir, Model model) { + Rules rules; + Ir::LoadJSON(rules, dir / "ir_unsafe.json", false, ParseExprRuleJSON, + ParseTypeRuleJSON); if (model == Model::kRefCount) { - auto refcount_ir_path = dir / "ir_refcount.json"; - if (std::filesystem::exists(refcount_ir_path)) { - LoadTgtFromIR(exprs, types, refcount_ir_path); - } - } - - LoadIrSrc(exprs, types, dir / "ir_src.json"); - - for (auto &[name, rule] : exprs) { - rule.validate(name); - } - for (auto &[name, rule] : types) { - if (rule.src.empty()) { - llvm::errs() << name << '\n'; - rule.dump(); - assert(0 && "Type rule loaded from IR but has no src"); - } + Ir::LoadJSON(rules, dir / "ir_refcount.json", false, ParseExprRuleJSON, + ParseTypeRuleJSON); } - return {std::move(exprs), std::move(types)}; + return rules; } -} // namespace cpp2rust::TranslationRule +} // namespace cpp2rust::IrTgt diff --git a/cpp2rust/converter/translation_rule.h b/cpp2rust/converter/rules/ir_tgt.h similarity index 70% rename from cpp2rust/converter/translation_rule.h rename to cpp2rust/converter/rules/ir_tgt.h index 8d6846ef8e..4f9aeded17 100644 --- a/cpp2rust/converter/translation_rule.h +++ b/cpp2rust/converter/rules/ir_tgt.h @@ -7,15 +7,13 @@ #include #include -#include #include #include #include "converter/factory.h" +#include "converter/rules/ir.h" -namespace cpp2rust::TranslationRule { - -static inline constexpr unsigned kMaxGenerics = 9; +namespace cpp2rust::IrTgt { struct TextFragment { std::string text; @@ -72,16 +70,7 @@ struct TypeInfo { void dump() const; }; -struct InitTypeLocation { - unsigned depth = -1u; - unsigned index = -1u; - - bool valid() const { return depth != -1u; } -}; - struct ExprRule { - std::string src; - InitTypeLocation init_type; std::vector params; TypeInfo return_type; std::vector> generics; // "T1" -> ["Ord", "Clone"] @@ -89,31 +78,29 @@ struct ExprRule { bool multi_statement = false; bool is_extern = false; + bool usesInit() const; void dump() const; - void validate(const std::string &name) const; }; struct TypeRule { - std::string src; std::string initializer; // Rust initializer expression TypeInfo type_info; void dump() const; static TypeRule Plain(std::string type) { - return {{}, {}, {{}, std::move(type), false, false}}; + return {{}, {{}, std::move(type), false, false}}; } static TypeRule RefcountPtr(std::string type) { - return {{}, {}, {{}, std::move(type), true, false}}; + return {{}, {{}, std::move(type), true, false}}; } static TypeRule UnsafePtr(std::string type) { - return {{}, {}, {{}, std::move(type), false, true}}; + return {{}, {{}, std::move(type), false, true}}; } }; -using ExprRules = std::unordered_map; -using TypeRules = std::unordered_map; +using Rules = Ir::Rules; + +Rules Load(const std::filesystem::path &dir, Model model); -std::pair Load(const std::filesystem::path &dir, - Model model); -} // namespace cpp2rust::TranslationRule +} // namespace cpp2rust::IrTgt diff --git a/cpp2rust/cpp_rule_preprocessor.cpp b/cpp2rust/cpp_rule_preprocessor.cpp index 43a38ab6aa..e27fe390b6 100644 --- a/cpp2rust/cpp_rule_preprocessor.cpp +++ b/cpp2rust/cpp_rule_preprocessor.cpp @@ -29,11 +29,13 @@ #include #include +#include #include #include "compat/platform_flags.h" #include "converter/converter_lib.h" #include "converter/mapper.h" +#include "converter/rules/ir_src_builder.h" namespace fs = std::filesystem; @@ -118,128 +120,232 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { } return; } + + const clang::NamedDecl *rule = nullptr; + const clang::TemplateDecl *tmpl = nullptr; if (auto var = R.Nodes.getNodeAs("tvar")) { - clang::QualType type = var->getUnderlyingType(); + rule = var; if (auto *alias = llvm::dyn_cast(var)) { - if (auto *tdecl = alias->getDescribedAliasTemplate()) { - type = lookupType(tdecl); - } + tmpl = alias->getDescribedAliasTemplate(); } - auto src = Mapper::ToString(type, Mapper::ScalarSugar::kPreserve); - out_.try_emplace(var->getQualifiedNameAsString(), std::move(src)); + } else if (auto func = R.Nodes.getNodeAs("func")) { + rule = func; + tmpl = func->getDescribedFunctionTemplate(); + } else { + return; + } + auto name = rule->getQualifiedNameAsString(); + if (out_.find(name) != out_.end()) { return; } - if (auto func = R.Nodes.getNodeAs("func")) { - auto add = [&](std::string &&src) { - out_.try_emplace(func->getQualifiedNameAsString(), std::move(src)); - }; - - if (const auto *fcall = R.Nodes.getNodeAs("fcall")) { - if (fcall->getDirectCallee()) { - add(Mapper::ToString(fcall)); - return; + nttp_alternate_.clear(); + auto entry = build(R); + if (!entry) { + return; + } + if (tmpl) { + for (const clang::NamedDecl *param : *tmpl->getTemplateParameters()) { + auto n = paramNumber(param->getName()); + const auto *nttp = + llvm::dyn_cast(param); + if (!n || !nttp || param->isTemplateParameterPack()) { + continue; } - - LookupInfo lookup(fcall->getCallee()); - clang::FunctionDecl *rule = nullptr; - clang::FunctionDecl *decl = lookupCalledDecl( - func->getDescribedFunctionTemplate(), lookup, &rule); - if (Mapper::HasFunctionParameterPack(func) && - Mapper::HasFunctionParameterPack(decl)) { - addPackRule(func, rule, decl); - return; + nttp_alternate_ = param->getName().str(); + auto alternate = build(R); + nttp_alternate_.clear(); + if (!alternate || !markNTTP(entry->ir, alternate->ir, *n, + nttpValue(nttp->getType(), false), + nttpValue(nttp->getType(), true))) { + llvm::errs() << "ERROR: " << name << ": non-type parameter " + << param->getName() + << " must be used as a template argument as is\n"; + std::exit(EXIT_FAILURE); } - add(Mapper::ToString(decl)); - return; } - if (const auto *ctor = - R.Nodes.getNodeAs("ctor")) { - if (ctor->getConstructor()) { - add(Mapper::ToString(ctor)); - return; - } + } + + llvm::json::Object obj{{"ir", IrSrc::ToJSON(*entry->ir)}}; + if (entry->init_type) { + obj.try_emplace("init_type", + llvm::json::Object{{"depth", entry->init_type->first}, + {"index", entry->init_type->second}}); + } + out_.try_emplace(std::move(name), std::move(obj)); + } + +private: + llvm::json::Object &out_; + clang::Sema *sema_ = nullptr; + clang::SourceLocation loc_; + llvm::DenseMap stand_ins_; + std::string nttp_alternate_; + + struct Entry { + IrSrc::NodePtr ir; + std::optional> init_type; + }; + + static std::optional paramNumber(llvm::StringRef name) { + unsigned n; + if (name.consume_front("T") && !name.empty() && !name.getAsInteger(10, n)) { + return n; + } + return std::nullopt; + } + + llvm::APInt nttpAPInt(clang::QualType type, bool alternate) { + return llvm::APInt(sema_->Context.getIntWidth(type), alternate ? 2 : 1); + } + + std::string nttpValue(clang::QualType type, bool alternate) { + return std::to_string(nttpAPInt(type, alternate).getZExtValue()); + } + + static bool markNTTP(IrSrc::NodePtr &ir, const IrSrc::NodePtr &alternate, + unsigned n, const std::string &value, + const std::string &alternate_value) { + using Kind = IrSrc::Node::Kind; + if (ir->kind == Kind::kValue && alternate->kind == Kind::kValue && + ir->name != alternate->name) { + if (ir->name != value || alternate->name != alternate_value) { + return false; + } + ir = std::make_unique(n); + return true; + } + if (!ir->shallowEquals(*alternate)) { + return false; + } + return IrSrc::zipChildren( + *ir, *alternate, [&](IrSrc::NodePtr &a, const IrSrc::NodePtr &b) { + return markNTTP(a, b, n, value, alternate_value); + }); + } + + IrSrc::Builder builder() { + IrSrc::Builder b(sema_->Context); + b.stand_in = [this](const clang::Decl *decl) -> std::optional { + if (auto it = stand_ins_.find(decl->getCanonicalDecl()); + it != stand_ins_.end()) { + return it->second; } - if (const auto *muse = R.Nodes.getNodeAs("muse")) { - if (llvm::isa(muse->getMemberDecl())) { - add(Mapper::ToString(muse)); - return; + return std::nullopt; + }; + return b; + } + + Entry entry(const clang::Expr *expr, const clang::NamedDecl *rule) { + auto ir = builder().FromExpr(expr); + if (!ir) { + llvm::errs() << "ERROR: " << rule->getQualifiedNameAsString() + << ": unsupported expression\n"; + expr->dump(); + std::exit(EXIT_FAILURE); + } + return {std::move(ir), std::nullopt}; + } + + Entry entry(const clang::NamedDecl *decl) { + return {builder().FromDecl(decl), std::nullopt}; + } + + std::optional + build(const clang::ast_matchers::MatchFinder::MatchResult &R) { + if (auto var = R.Nodes.getNodeAs("tvar")) { + clang::QualType type = var->getUnderlyingType(); + if (auto *alias = llvm::dyn_cast(var)) { + if (auto *tdecl = alias->getDescribedAliasTemplate()) { + type = lookupType(tdecl); } } - if (const auto *um = - R.Nodes.getNodeAs("umuse")) { - add(Mapper::ToString(um)); - return; + auto b = builder(); + b.keep_builtin_typedef = true; + b.keep_pointee_sugar = [](clang::QualType pointee) { + return pointee.getCanonicalType()->isBuiltinType() && + (pointee->getAs() || + pointee->getAs()); + }; + return Entry{b.FromType(type), std::nullopt}; + } + + const auto *func = R.Nodes.getNodeAs("func"); + if (const auto *fcall = R.Nodes.getNodeAs("fcall")) { + if (fcall->getDirectCallee()) { + return entry(fcall, func); } - if (R.Nodes.getNodeAs("declref")) { - if (const auto *enum_val = - R.Nodes.getNodeAs("enum_val")) { - add(Mapper::ToString(enum_val)); - return; - } else if (const auto *decl = - R.Nodes.getNodeAs("decl")) { - add(Mapper::ToString(decl)); - return; - } + + LookupInfo lookup(fcall->getCallee()); + clang::FunctionDecl *rule = nullptr; + clang::FunctionDecl *decl = + lookupCalledDecl(func->getDescribedFunctionTemplate(), lookup, &rule); + if (HasFunctionParameterPack(func) && HasFunctionParameterPack(decl)) { + return packEntry(func, rule, decl); } - if (const auto *uop = - R.Nodes.getNodeAs("udeclref")) { - add(Mapper::ToString(uop)); - return; + return entry(decl); + } + if (const auto *ctor = R.Nodes.getNodeAs("ctor")) { + if (ctor->getConstructor()) { + return entry(ctor, func); } - if (const auto *dsme = - R.Nodes.getNodeAs("dsme")) { - if (dsme->isArrow()) { - clang::MemberExpr *expr = lookupArrowAccess( - func->getDescribedFunctionTemplate(), dsme->getMemberNameInfo(), - dsme->getQualifierLoc()); - add(Mapper::ToString(expr)); - return; - } - clang::NamedDecl *decl = lookupMemberAccess( - func->getDescribedFunctionTemplate(), dsme->getMember()); - add(Mapper::ToString(decl)); - return; + } + if (const auto *muse = R.Nodes.getNodeAs("muse")) { + if (llvm::isa(muse->getMemberDecl())) { + return entry(muse, func); } - if (const auto *uctor = - R.Nodes.getNodeAs("uctor")) { - LookupInfo lookup(uctor); - clang::NamedDecl *decl = lookupCalledDecl( - func->getDescribedFunctionTemplate(), lookup, nullptr); - add(Mapper::ToString(decl)); - return; + } + if (const auto *um = + R.Nodes.getNodeAs("umuse")) { + return entry(um, func); + } + if (R.Nodes.getNodeAs("declref")) { + if (const auto *enum_val = + R.Nodes.getNodeAs("enum_val")) { + return entry(enum_val); + } else if (const auto *decl = R.Nodes.getNodeAs("decl")) { + return entry(decl); } - if (const auto *lit = - R.Nodes.getNodeAs("macro_int")) { - if (lit->getBeginLoc().isMacroID()) { - add(Mapper::ToString(lit)); - } - return; + } + if (const auto *uop = R.Nodes.getNodeAs("udeclref")) { + return entry(uop, func); + } + if (const auto *dsme = + R.Nodes.getNodeAs("dsme")) { + if (dsme->isArrow()) { + clang::MemberExpr *expr = lookupArrowAccess( + func->getDescribedFunctionTemplate(), dsme->getMemberNameInfo(), + dsme->getQualifierLoc()); + return entry(expr, func); } + clang::NamedDecl *decl = lookupMemberAccess( + func->getDescribedFunctionTemplate(), dsme->getMember()); + return entry(decl); } + if (const auto *uctor = + R.Nodes.getNodeAs("uctor")) { + LookupInfo lookup(uctor); + clang::NamedDecl *decl = lookupCalledDecl( + func->getDescribedFunctionTemplate(), lookup, nullptr); + return entry(decl); + } + if (const auto *lit = + R.Nodes.getNodeAs("macro_int")) { + if (lit->getBeginLoc().isMacroID()) { + return entry(lit, func); + } + } + return std::nullopt; } -private: - llvm::json::Object &out_; - clang::Sema *sema_ = nullptr; - clang::SourceLocation loc_; - - void addPackRule(const clang::FunctionDecl *func, clang::FunctionDecl *rule, - clang::FunctionDecl *callee) { - auto key = Mapper::ToString(callee); + Entry packEntry(const clang::FunctionDecl *func, clang::FunctionDecl *rule, + clang::FunctionDecl *callee) { + auto res = entry(callee); auto init_type = getInitType(func, rule); - if (init_type.isNull()) { - out_.try_emplace(func->getQualifiedNameAsString(), std::move(key)); - return; + if (!init_type.isNull()) { + res.init_type = findTemplateArgument(callee, init_type); } - - auto [depth, index] = findTemplateArgument(callee, init_type); - out_.try_emplace(func->getQualifiedNameAsString(), - llvm::json::Object{ - {"key", std::move(key)}, - {"init_type", llvm::json::Object{{"depth", depth}, - {"index", index}}}, - }); + return res; } clang::QualType getInitType(const clang::FunctionDecl *func, @@ -589,7 +695,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { clang::ASTContext &ctx = sema_->Context; if (type->isIntegerType()) { init = clang::IntegerLiteral::Create( - ctx, llvm::APInt(ctx.getIntWidth(type), 1), type, loc_); + ctx, nttpAPInt(type, name == nttp_alternate_), type, loc_); } else { init = new (ctx) clang::ImplicitValueInitExpr(type); } @@ -624,6 +730,9 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { rdecl->getQualifier(), rdecl, false); } assert(!type.isNull() && "Template type argument creation failed"); + if (auto n = paramNumber(param->getName())) { + stand_ins_[type->getAsRecordDecl()->getCanonicalDecl()] = *n; + } out.emplace_back(type); } else if (const auto *nttp = llvm::dyn_cast(param)) { diff --git a/tests/unit/class_template_nttp.cpp b/tests/unit/class_template_nttp.cpp new file mode 100644 index 0000000000..be9492dc3b --- /dev/null +++ b/tests/unit/class_template_nttp.cpp @@ -0,0 +1,154 @@ +#include +#include +#include + +template struct Step { + int value; + void advance() { value += N; } + int scaled() const { return value * N; } +}; + +template <> struct Step<0> { + int value; + void advance() { value = -1; } + int scaled() const { return 0; } +}; + +template struct Choice { + int pick(int a, int b) const { return B ? a : b; } +}; + +template struct Buffer { + T data[N]; + int size() const { return N; } + T sum() const { + T total = 0; + for (int i = 0; i < N; ++i) { + total += data[i]; + } + return total; + } +}; + +template struct Mark { + int count(const char *s) const { + int n = 0; + for (; *s; ++s) { + if (*s == C) { + ++n; + if (!Repeat) { + break; + } + } + } + return n; + } +}; + +template struct Range { + bool contains(int x) const { return x >= Lo && x <= Hi; } + int width() const { return Hi - Lo; } +}; + +template struct Mix { + long long total() const { return U + S + L; } +}; + +int main() { + Step<2> s2{1}; + Step<3> s3{1}; + s2.advance(); + s3.advance(); + assert(s2.value == 3); + assert(s3.value == 4); + assert(s2.scaled() == 6); + assert(s3.scaled() == 12); + + Step<-1> sn{5}; + Step<1> sp{5}; + sn.advance(); + sp.advance(); + assert(sn.value == 4); + assert(sp.value == 6); + assert(sn.scaled() == -4); + assert(sp.scaled() == 6); + + Step<0> s0{5}; + s0.advance(); + assert(s0.value == -1); + assert(s0.scaled() == 0); + + Choice yes; + Choice no; + assert(yes.pick(1, 2) == 1); + assert(no.pick(1, 2) == 2); + + Buffer b2{{1, 2}}; + Buffer b3{{1, 2, 3}}; + assert(b2.size() == 2 && b2.sum() == 3); + assert(b3.size() == 3 && b3.sum() == 6); + + std::vector> v2; + std::vector> v3; + v2.push_back(Step<2>{0}); + v3.push_back(Step<3>{0}); + v2[0].advance(); + v3[0].advance(); + assert(v2.size() == 1 && v2[0].value == 2); + assert(v3.size() == 1 && v3[0].value == 3); + + std::array, 2> a2{{{1}, {2}}}; + std::array, 3> a3{{{1}, {2}, {3}}}; + for (auto &s : a2) { + s.advance(); + } + for (auto &s : a3) { + s.advance(); + } + assert(a2.size() == 2 && a2[1].scaled() == 8); + assert(a3.size() == 3 && a3[2].scaled() == 18); + + std::vector> vb2; + std::vector> vb3; + vb2.push_back(b2); + vb3.push_back(b3); + assert(vb2[0].size() == 2 && vb2[0].sum() == 3); + assert(vb3[0].size() == 3 && vb3[0].sum() == 6); + + std::array, 1> ayes{}; + std::array, 1> ano{}; + assert(ayes[0].pick(1, 2) == 1); + assert(ano[0].pick(1, 2) == 2); + + Mark<'a', true> all_a; + Mark<'a', false> first_a; + Mark<'b', true> all_b; + assert(all_a.count("banana") == 3); + assert(first_a.count("banana") == 1); + assert(all_b.count("banana") == 1); + + Range<0, 10> wide; + Range<5, 6> narrow; + Range<-3, 0> neg; + assert(wide.contains(7) && !narrow.contains(7)); + assert(wide.width() == 10 && narrow.width() == 1); + assert(neg.contains(-2) && neg.width() == 3); + + Mix<200, -3, 10000000000LL> big; + Mix<1, 1, 1> small; + assert(big.total() == 10000000197LL); + assert(small.total() == 3); + + std::vector> vwide{wide}; + std::vector> vnarrow{narrow}; + assert(vwide[0].contains(7) && !vnarrow[0].contains(7)); + + std::array, 2> amarks{}; + std::array, 2> afirst{}; + assert(amarks[1].count("aaa") == 3); + assert(afirst[1].count("aaa") == 1); + + std::vector> vbig{big, big}; + assert(vbig.size() == 2 && vbig[1].total() == 10000000197LL); + return 0; +} diff --git a/tests/unit/out/refcount/class_template_nttp.rs b/tests/unit/out/refcount/class_template_nttp.rs new file mode 100644 index 0000000000..7a967740e4 --- /dev/null +++ b/tests/unit/out/refcount/class_template_nttp.rs @@ -0,0 +1,725 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +#[derive(Default)] +pub struct Step_0_ { + pub value: Value, +} +impl Clone for Step_0_ { + fn clone(&self) -> Self { + let __this: Value = Rc::new(RefCell::new(Self { + value: Rc::new(RefCell::new((*self.value.borrow()))), + })); + let this: Ptr = __this.as_pointer(); + Rc::try_unwrap(__this).ok().unwrap().into_inner() + } +} +impl ByteRepr for Step_0_ { + fn byte_size() -> usize { + 4 + } + fn to_bytes(&self, buf: &mut [u8]) { + (*self.value.borrow()).to_bytes(&mut buf[0..4]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + value: Rc::new(RefCell::new(::from_bytes(&buf[0..4]))), + } + } +} +#[derive(Default)] +pub struct Step_2_ { + pub value: Value, +} +impl Clone for Step_2_ { + fn clone(&self) -> Self { + let __this: Value = Rc::new(RefCell::new(Self { + value: Rc::new(RefCell::new((*self.value.borrow()))), + })); + let this: Ptr = __this.as_pointer(); + Rc::try_unwrap(__this).ok().unwrap().into_inner() + } +} +impl ByteRepr for Step_2_ { + fn byte_size() -> usize { + 4 + } + fn to_bytes(&self, buf: &mut [u8]) { + (*self.value.borrow()).to_bytes(&mut buf[0..4]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + value: Rc::new(RefCell::new(::from_bytes(&buf[0..4]))), + } + } +} +#[derive(Default)] +pub struct Step_3_ { + pub value: Value, +} +impl Clone for Step_3_ { + fn clone(&self) -> Self { + let __this: Value = Rc::new(RefCell::new(Self { + value: Rc::new(RefCell::new((*self.value.borrow()))), + })); + let this: Ptr = __this.as_pointer(); + Rc::try_unwrap(__this).ok().unwrap().into_inner() + } +} +impl ByteRepr for Step_3_ { + fn byte_size() -> usize { + 4 + } + fn to_bytes(&self, buf: &mut [u8]) { + (*self.value.borrow()).to_bytes(&mut buf[0..4]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + value: Rc::new(RefCell::new(::from_bytes(&buf[0..4]))), + } + } +} +#[derive(Default)] +pub struct Step_neg1_ { + pub value: Value, +} +impl Clone for Step_neg1_ { + fn clone(&self) -> Self { + let __this: Value = Rc::new(RefCell::new(Self { + value: Rc::new(RefCell::new((*self.value.borrow()))), + })); + let this: Ptr = __this.as_pointer(); + Rc::try_unwrap(__this).ok().unwrap().into_inner() + } +} +impl ByteRepr for Step_neg1_ { + fn byte_size() -> usize { + 4 + } + fn to_bytes(&self, buf: &mut [u8]) { + (*self.value.borrow()).to_bytes(&mut buf[0..4]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + value: Rc::new(RefCell::new(::from_bytes(&buf[0..4]))), + } + } +} +#[derive(Default)] +pub struct Step_1_ { + pub value: Value, +} +impl Clone for Step_1_ { + fn clone(&self) -> Self { + let __this: Value = Rc::new(RefCell::new(Self { + value: Rc::new(RefCell::new((*self.value.borrow()))), + })); + let this: Ptr = __this.as_pointer(); + Rc::try_unwrap(__this).ok().unwrap().into_inner() + } +} +impl ByteRepr for Step_1_ { + fn byte_size() -> usize { + 4 + } + fn to_bytes(&self, buf: &mut [u8]) { + (*self.value.borrow()).to_bytes(&mut buf[0..4]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + value: Rc::new(RefCell::new(::from_bytes(&buf[0..4]))), + } + } +} +#[derive(Clone, ByteRepr, Default)] +pub struct Choice_true_ {} +#[derive(Clone, ByteRepr, Default)] +pub struct Choice_false_ {} +#[derive()] +pub struct Buffer_int__2_ { + pub data: Value>, +} +impl Clone for Buffer_int__2_ { + fn clone(&self) -> Self { + let __this: Value = Rc::new(RefCell::new(Self { + data: Rc::new(RefCell::new(Box::new(std::array::from_fn::<_, 2, _>( + |__i: usize| (*self.data.borrow())[(__i) as usize], + )))), + })); + let this: Ptr = __this.as_pointer(); + Rc::try_unwrap(__this).ok().unwrap().into_inner() + } +} +impl Default for Buffer_int__2_ { + fn default() -> Self { + Buffer_int__2_ { + data: Rc::new(RefCell::new((0..2).map(|_| 0_i32).collect::>())), + } + } +} +impl ByteRepr for Buffer_int__2_ { + fn byte_size() -> usize { + 8 + } + fn to_bytes(&self, buf: &mut [u8]) { + (*self.data.borrow()).to_bytes(&mut buf[0..8]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + data: Rc::new(RefCell::new(>::from_bytes(&buf[0..8]))), + } + } +} +#[derive()] +pub struct Buffer_int__3_ { + pub data: Value>, +} +impl Clone for Buffer_int__3_ { + fn clone(&self) -> Self { + let __this: Value = Rc::new(RefCell::new(Self { + data: Rc::new(RefCell::new(Box::new(std::array::from_fn::<_, 3, _>( + |__i: usize| (*self.data.borrow())[(__i) as usize], + )))), + })); + let this: Ptr = __this.as_pointer(); + Rc::try_unwrap(__this).ok().unwrap().into_inner() + } +} +impl Default for Buffer_int__3_ { + fn default() -> Self { + Buffer_int__3_ { + data: Rc::new(RefCell::new((0..3).map(|_| 0_i32).collect::>())), + } + } +} +impl ByteRepr for Buffer_int__3_ { + fn byte_size() -> usize { + 12 + } + fn to_bytes(&self, buf: &mut [u8]) { + (*self.data.borrow()).to_bytes(&mut buf[0..12]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + data: Rc::new(RefCell::new(>::from_bytes(&buf[0..12]))), + } + } +} +#[derive(Clone, ByteRepr, Default)] +pub struct Mark__a___true_ {} +#[derive(Clone, ByteRepr, Default)] +pub struct Mark__a___false_ {} +#[derive(Clone, ByteRepr, Default)] +pub struct Mark__b___true_ {} +#[derive(Clone, ByteRepr, Default)] +pub struct Range_0__10_ {} +#[derive(Clone, ByteRepr, Default)] +pub struct Range_5__6_ {} +#[derive(Clone, ByteRepr, Default)] +pub struct Range_neg3__0_ {} +#[derive(Clone, ByteRepr, Default)] +pub struct Mix___xc8___neg3__10000000000_ {} +#[derive(Clone, ByteRepr, Default)] +pub struct Mix___x01___1__1_ {} +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let s2: Value = Rc::new(RefCell::new(Step_2_ { + value: Rc::new(RefCell::new(1)), + })); + let s3: Value = Rc::new(RefCell::new(Step_3_ { + value: Rc::new(RefCell::new(1)), + })); + ({ Step_2_Impl::advance(&s2.as_pointer()) }); + ({ Step_3_Impl::advance(&s3.as_pointer()) }); + assert!(((*(*s2.borrow()).value.borrow()) == 3)); + assert!(((*(*s3.borrow()).value.borrow()) == 4)); + assert!((({ Step_2_Impl::scaled(&s2.as_pointer(),) }) == 6)); + assert!((({ Step_3_Impl::scaled(&s3.as_pointer(),) }) == 12)); + let sn: Value = Rc::new(RefCell::new(Step_neg1_ { + value: Rc::new(RefCell::new(5)), + })); + let sp: Value = Rc::new(RefCell::new(Step_1_ { + value: Rc::new(RefCell::new(5)), + })); + ({ Step_neg1_Impl::advance(&sn.as_pointer()) }); + ({ Step_1_Impl::advance(&sp.as_pointer()) }); + assert!(((*(*sn.borrow()).value.borrow()) == 4)); + assert!(((*(*sp.borrow()).value.borrow()) == 6)); + assert!((({ Step_neg1_Impl::scaled(&sn.as_pointer(),) }) == -4_i32)); + assert!((({ Step_1_Impl::scaled(&sp.as_pointer(),) }) == 6)); + let s0: Value = Rc::new(RefCell::new(Step_0_ { + value: Rc::new(RefCell::new(5)), + })); + ({ Step_0_Impl::advance(&s0.as_pointer()) }); + assert!(((*(*s0.borrow()).value.borrow()) == -1_i32)); + assert!((({ Step_0_Impl::scaled(&s0.as_pointer(),) }) == 0)); + let yes: Value = Rc::new(RefCell::new(::default())); + let no: Value = Rc::new(RefCell::new(::default())); + assert!((({ Choice_true_Impl::pick(&yes.as_pointer(), 1, 2,) }) == 1)); + assert!((({ Choice_false_Impl::pick(&no.as_pointer(), 1, 2,) }) == 2)); + let b2: Value = Rc::new(RefCell::new(Buffer_int__2_ { + data: Rc::new(RefCell::new(Box::new([1, 2]))), + })); + let b3: Value = Rc::new(RefCell::new(Buffer_int__3_ { + data: Rc::new(RefCell::new(Box::new([1, 2, 3]))), + })); + assert!( + (({ Buffer_int__2_Impl::size(&b2.as_pointer(),) }) == 2) + && (({ Buffer_int__2_Impl::sum(&b2.as_pointer(),) }) == 3) + ); + assert!( + (({ Buffer_int__3_Impl::size(&b3.as_pointer(),) }) == 3) + && (({ Buffer_int__3_Impl::sum(&b3.as_pointer(),) }) == 6) + ); + let v2: Value> = Rc::new(RefCell::new(Vec::new())); + let v3: Value> = Rc::new(RefCell::new(Vec::new())); + (*v2.borrow_mut()).push(Step_2_ { + value: Rc::new(RefCell::new(0)), + }); + (*v3.borrow_mut()).push(Step_3_ { + value: Rc::new(RefCell::new(0)), + }); + ({ Step_2_Impl::advance(&(v2.as_pointer() as Ptr).offset(0_usize)) }); + ({ Step_3_Impl::advance(&(v3.as_pointer() as Ptr).offset(0_usize)) }); + assert!( + ((*v2.borrow()).len() == 1_usize) + && ((*(*(v2.as_pointer() as Ptr) + .offset(0_usize) + .upgrade() + .deref()) + .value + .borrow()) + == 2) + ); + assert!( + ((*v3.borrow()).len() == 1_usize) + && ((*(*(v3.as_pointer() as Ptr) + .offset(0_usize) + .upgrade() + .deref()) + .value + .borrow()) + == 3) + ); + let a2: Value> = Rc::new(RefCell::new(vec![ + Step_2_ { + value: Rc::new(RefCell::new(1)), + }, + Step_2_ { + value: Rc::new(RefCell::new(2)), + }, + ])); + let a3: Value> = Rc::new(RefCell::new(vec![ + Step_3_ { + value: Rc::new(RefCell::new(1)), + }, + Step_3_ { + value: Rc::new(RefCell::new(2)), + }, + Step_3_ { + value: Rc::new(RefCell::new(3)), + }, + ])); + 'loop_: for mut s in a2.as_pointer() as Ptr { + ({ Step_2_Impl::advance(&s) }); + } + 'loop_: for mut s in a3.as_pointer() as Ptr { + ({ Step_3_Impl::advance(&s) }); + } + assert!( + ((*a2.borrow()).len() == 2_usize) + && (({ Step_2_Impl::scaled(&(a2.as_pointer() as Ptr).offset(1_usize),) }) + == 8) + ); + assert!( + ((*a3.borrow()).len() == 3_usize) + && (({ Step_3_Impl::scaled(&(a3.as_pointer() as Ptr).offset(2_usize),) }) + == 18) + ); + let vb2: Value> = Rc::new(RefCell::new(Vec::new())); + let vb3: Value> = Rc::new(RefCell::new(Vec::new())); + { + let a0_clone = (*b2.borrow()).clone(); + (*vb2.borrow_mut()).push(a0_clone) + }; + { + let a0_clone = (*b3.borrow()).clone(); + (*vb3.borrow_mut()).push(a0_clone) + }; + assert!( + (({ + Buffer_int__2_Impl::size(&(vb2.as_pointer() as Ptr).offset(0_usize)) + }) == 2) + && (({ + Buffer_int__2_Impl::sum(&(vb2.as_pointer() as Ptr).offset(0_usize)) + }) == 3) + ); + assert!( + (({ + Buffer_int__3_Impl::size(&(vb3.as_pointer() as Ptr).offset(0_usize)) + }) == 3) + && (({ + Buffer_int__3_Impl::sum(&(vb3.as_pointer() as Ptr).offset(0_usize)) + }) == 6) + ); + let ayes: Value> = Rc::new(RefCell::new(vec![::default()])); + let ano: Value> = Rc::new(RefCell::new(vec![::default()])); + assert!( + (({ + Choice_true_Impl::pick( + &(ayes.as_pointer() as Ptr).offset(0_usize), + 1, + 2, + ) + }) == 1) + ); + assert!( + (({ + Choice_false_Impl::pick( + &(ano.as_pointer() as Ptr).offset(0_usize), + 1, + 2, + ) + }) == 2) + ); + let all_a: Value = Rc::new(RefCell::new(::default())); + let first_a: Value = Rc::new(RefCell::new(::default())); + let all_b: Value = Rc::new(RefCell::new(::default())); + assert!( + (({ + Mark__a___true_Impl::count( + &all_a.as_pointer(), + Ptr::::from_string_literal(b"banana"), + ) + }) == 3) + ); + assert!( + (({ + Mark__a___false_Impl::count( + &first_a.as_pointer(), + Ptr::::from_string_literal(b"banana"), + ) + }) == 1) + ); + assert!( + (({ + Mark__b___true_Impl::count( + &all_b.as_pointer(), + Ptr::::from_string_literal(b"banana"), + ) + }) == 1) + ); + let wide: Value = Rc::new(RefCell::new(::default())); + let narrow: Value = Rc::new(RefCell::new(::default())); + let neg: Value = Rc::new(RefCell::new(::default())); + assert!( + ({ Range_0__10_Impl::contains(&wide.as_pointer(), 7,) }) + && (!({ Range_5__6_Impl::contains(&narrow.as_pointer(), 7,) })) + ); + assert!( + (({ Range_0__10_Impl::width(&wide.as_pointer(),) }) == 10) + && (({ Range_5__6_Impl::width(&narrow.as_pointer(),) }) == 1) + ); + assert!( + ({ Range_neg3__0_Impl::contains(&neg.as_pointer(), -2_i32,) }) + && (({ Range_neg3__0_Impl::width(&neg.as_pointer(),) }) == 3) + ); + let big: Value = + Rc::new(RefCell::new(::default())); + let small: Value = Rc::new(RefCell::new(::default())); + assert!( + (({ Mix___xc8___neg3__10000000000_Impl::total(&big.as_pointer(),) }) == 10000000197_i64) + ); + assert!((({ Mix___x01___1__1_Impl::total(&small.as_pointer(),) }) == 3_i64)); + let vwide: Value> = Rc::new(RefCell::new(vec![(*wide.borrow()).clone()])); + let vnarrow: Value> = Rc::new(RefCell::new(vec![(*narrow.borrow()).clone()])); + assert!( + ({ + Range_0__10_Impl::contains( + &(vwide.as_pointer() as Ptr).offset(0_usize), + 7, + ) + }) && (!({ + Range_5__6_Impl::contains( + &(vnarrow.as_pointer() as Ptr).offset(0_usize), + 7, + ) + })) + ); + let amarks: Value> = Rc::new(RefCell::new(vec![ + ::default(), + ::default(), + ])); + let afirst: Value> = Rc::new(RefCell::new(vec![ + ::default(), + ::default(), + ])); + assert!( + (({ + Mark__a___true_Impl::count( + &(amarks.as_pointer() as Ptr).offset(1_usize), + Ptr::::from_string_literal(b"aaa"), + ) + }) == 3) + ); + assert!( + (({ + Mark__a___false_Impl::count( + &(afirst.as_pointer() as Ptr).offset(1_usize), + Ptr::::from_string_literal(b"aaa"), + ) + }) == 1) + ); + let vbig: Value> = Rc::new(RefCell::new(vec![ + (*big.borrow()).clone(), + (*big.borrow()).clone(), + ])); + assert!( + ((*vbig.borrow()).len() == 2_usize) + && (({ + Mix___xc8___neg3__10000000000_Impl::total( + &(vbig.as_pointer() as Ptr).offset(1_usize), + ) + }) == 10000000197_i64) + ); + return 0; +} +pub trait Buffer_int__2_Impl { + fn size(&self) -> i32; + fn sum(&self) -> i32; +} +impl Buffer_int__2_Impl for Ptr { + fn size(&self) -> i32 { + return 2; + } + fn sum(&self) -> i32 { + let total: Value = Rc::new(RefCell::new(0)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((*i.borrow()) < 2) { + (*total.borrow_mut()) += + (*(*(*self).upgrade().deref()).data.borrow())[(*i.borrow()) as usize]; + (*i.borrow_mut()).prefix_inc(); + } + return (*total.borrow()); + } +} +pub trait Buffer_int__3_Impl { + fn size(&self) -> i32; + fn sum(&self) -> i32; +} +impl Buffer_int__3_Impl for Ptr { + fn size(&self) -> i32 { + return 3; + } + fn sum(&self) -> i32 { + let total: Value = Rc::new(RefCell::new(0)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((*i.borrow()) < 3) { + (*total.borrow_mut()) += + (*(*(*self).upgrade().deref()).data.borrow())[(*i.borrow()) as usize]; + (*i.borrow_mut()).prefix_inc(); + } + return (*total.borrow()); + } +} +pub trait Choice_false_Impl { + fn pick(&self, a: i32, b: i32) -> i32; +} +impl Choice_false_Impl for Ptr { + fn pick(&self, a: i32, b: i32) -> i32 { + let a: Value = Rc::new(RefCell::new(a)); + let b: Value = Rc::new(RefCell::new(b)); + return if false { (*a.borrow()) } else { (*b.borrow()) }; + } +} +pub trait Choice_true_Impl { + fn pick(&self, a: i32, b: i32) -> i32; +} +impl Choice_true_Impl for Ptr { + fn pick(&self, a: i32, b: i32) -> i32 { + let a: Value = Rc::new(RefCell::new(a)); + let b: Value = Rc::new(RefCell::new(b)); + return if true { (*a.borrow()) } else { (*b.borrow()) }; + } +} +pub trait Mark__a___false_Impl { + fn count(&self, s: Ptr) -> i32; +} +impl Mark__a___false_Impl for Ptr { + fn count(&self, s: Ptr) -> i32 { + let s: Value> = Rc::new(RefCell::new(s)); + let n: Value = Rc::new(RefCell::new(0)); + 'loop_: while (((*s.borrow()).read()) != 0) { + if ((((*s.borrow()).read()) as i32) == (('a' as u8) as i32)) { + (*n.borrow_mut()).prefix_inc(); + if !(false) { + break; + } + } + (*s.borrow_mut()).prefix_inc(); + } + return (*n.borrow()); + } +} +pub trait Mark__a___true_Impl { + fn count(&self, s: Ptr) -> i32; +} +impl Mark__a___true_Impl for Ptr { + fn count(&self, s: Ptr) -> i32 { + let s: Value> = Rc::new(RefCell::new(s)); + let n: Value = Rc::new(RefCell::new(0)); + 'loop_: while (((*s.borrow()).read()) != 0) { + if ((((*s.borrow()).read()) as i32) == (('a' as u8) as i32)) { + (*n.borrow_mut()).prefix_inc(); + if !(true) { + break; + } + } + (*s.borrow_mut()).prefix_inc(); + } + return (*n.borrow()); + } +} +pub trait Mark__b___true_Impl { + fn count(&self, s: Ptr) -> i32; +} +impl Mark__b___true_Impl for Ptr { + fn count(&self, s: Ptr) -> i32 { + let s: Value> = Rc::new(RefCell::new(s)); + let n: Value = Rc::new(RefCell::new(0)); + 'loop_: while (((*s.borrow()).read()) != 0) { + if ((((*s.borrow()).read()) as i32) == (('b' as u8) as i32)) { + (*n.borrow_mut()).prefix_inc(); + if !(true) { + break; + } + } + (*s.borrow_mut()).prefix_inc(); + } + return (*n.borrow()); + } +} +pub trait Mix___x01___1__1_Impl { + fn total(&self) -> i64; +} +impl Mix___x01___1__1_Impl for Ptr { + fn total(&self) -> i64 { + return ((((('\x01' as u8) as i32) + (1_i16 as i32)) as i64) + 1_i64); + } +} +pub trait Mix___xc8___neg3__10000000000_Impl { + fn total(&self) -> i64; +} +impl Mix___xc8___neg3__10000000000_Impl for Ptr { + fn total(&self) -> i64 { + return (((((b'\xc8' as u8) as i32) + (-3_i16 as i32)) as i64) + 10000000000_i64); + } +} +pub trait Range_0__10_Impl { + fn contains(&self, x: i32) -> bool; + fn width(&self) -> i32; +} +impl Range_0__10_Impl for Ptr { + fn contains(&self, x: i32) -> bool { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) >= 0) && ((*x.borrow()) <= 10); + } + fn width(&self) -> i32 { + return (10 - 0); + } +} +pub trait Range_5__6_Impl { + fn contains(&self, x: i32) -> bool; + fn width(&self) -> i32; +} +impl Range_5__6_Impl for Ptr { + fn contains(&self, x: i32) -> bool { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) >= 5) && ((*x.borrow()) <= 6); + } + fn width(&self) -> i32 { + return (6 - 5); + } +} +pub trait Range_neg3__0_Impl { + fn contains(&self, x: i32) -> bool; + fn width(&self) -> i32; +} +impl Range_neg3__0_Impl for Ptr { + fn contains(&self, x: i32) -> bool { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) >= -3) && ((*x.borrow()) <= 0); + } + fn width(&self) -> i32 { + return (0 - -3); + } +} +pub trait Step_0_Impl { + fn advance(&self); + fn scaled(&self) -> i32; +} +impl Step_0_Impl for Ptr { + fn advance(&self) { + (*(*(*self).upgrade().deref()).value.borrow_mut()) = -1_i32; + } + fn scaled(&self) -> i32 { + return 0; + } +} +pub trait Step_1_Impl { + fn advance(&self); + fn scaled(&self) -> i32; +} +impl Step_1_Impl for Ptr { + fn advance(&self) { + (*(*(*self).upgrade().deref()).value.borrow_mut()) += 1; + } + fn scaled(&self) -> i32 { + return ((*(*(*self).upgrade().deref()).value.borrow()) * 1); + } +} +pub trait Step_2_Impl { + fn advance(&self); + fn scaled(&self) -> i32; +} +impl Step_2_Impl for Ptr { + fn advance(&self) { + (*(*(*self).upgrade().deref()).value.borrow_mut()) += 2; + } + fn scaled(&self) -> i32 { + return ((*(*(*self).upgrade().deref()).value.borrow()) * 2); + } +} +pub trait Step_3_Impl { + fn advance(&self); + fn scaled(&self) -> i32; +} +impl Step_3_Impl for Ptr { + fn advance(&self) { + (*(*(*self).upgrade().deref()).value.borrow_mut()) += 3; + } + fn scaled(&self) -> i32 { + return ((*(*(*self).upgrade().deref()).value.borrow()) * 3); + } +} +pub trait Step_neg1_Impl { + fn advance(&self); + fn scaled(&self) -> i32; +} +impl Step_neg1_Impl for Ptr { + fn advance(&self) { + (*(*(*self).upgrade().deref()).value.borrow_mut()) += -1; + } + fn scaled(&self) -> i32 { + return ((*(*(*self).upgrade().deref()).value.borrow()) * -1); + } +} +pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/class_template_nttp.rs b/tests/unit/out/unsafe/class_template_nttp.rs new file mode 100644 index 0000000000..f4a8b96278 --- /dev/null +++ b/tests/unit/out/unsafe/class_template_nttp.rs @@ -0,0 +1,373 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Step_0_ { + pub value: i32, +} +impl Step_0_ { + pub unsafe fn advance(&mut self) { + self.value = -1_i32; + } + pub unsafe fn scaled(&self) -> i32 { + return 0; + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Step_2_ { + pub value: i32, +} +impl Step_2_ { + pub unsafe fn advance(&mut self) { + self.value += 2; + } + pub unsafe fn scaled(&self) -> i32 { + return ((self.value) * (2)); + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Step_3_ { + pub value: i32, +} +impl Step_3_ { + pub unsafe fn advance(&mut self) { + self.value += 3; + } + pub unsafe fn scaled(&self) -> i32 { + return ((self.value) * (3)); + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Step_neg1_ { + pub value: i32, +} +impl Step_neg1_ { + pub unsafe fn advance(&mut self) { + self.value += -1; + } + pub unsafe fn scaled(&self) -> i32 { + return ((self.value) * (-1)); + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Step_1_ { + pub value: i32, +} +impl Step_1_ { + pub unsafe fn advance(&mut self) { + self.value += 1; + } + pub unsafe fn scaled(&self) -> i32 { + return ((self.value) * (1)); + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Choice_true_ {} +impl Choice_true_ { + pub unsafe fn pick(&self, mut a: i32, mut b: i32) -> i32 { + return if true { a } else { b }; + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Choice_false_ {} +impl Choice_false_ { + pub unsafe fn pick(&self, mut a: i32, mut b: i32) -> i32 { + return if false { a } else { b }; + } +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct Buffer_int__2_ { + pub data: [i32; 2], +} +impl Buffer_int__2_ { + pub unsafe fn size(&self) -> i32 { + return 2; + } + pub unsafe fn sum(&self) -> i32 { + let mut total: i32 = 0; + let mut i: i32 = 0; + 'loop_: while ((i) < (2)) { + total += self.data[(i) as usize]; + i.prefix_inc(); + } + return total; + } +} +impl Default for Buffer_int__2_ { + fn default() -> Self { + Buffer_int__2_ { data: [0_i32; 2] } + } +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct Buffer_int__3_ { + pub data: [i32; 3], +} +impl Buffer_int__3_ { + pub unsafe fn size(&self) -> i32 { + return 3; + } + pub unsafe fn sum(&self) -> i32 { + let mut total: i32 = 0; + let mut i: i32 = 0; + 'loop_: while ((i) < (3)) { + total += self.data[(i) as usize]; + i.prefix_inc(); + } + return total; + } +} +impl Default for Buffer_int__3_ { + fn default() -> Self { + Buffer_int__3_ { data: [0_i32; 3] } + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Mark__a___true_ {} +impl Mark__a___true_ { + pub unsafe fn count(&self, mut s: *const libc::c_char) -> i32 { + let mut n: i32 = 0; + 'loop_: while ((*s) != 0) { + if (((*s) as i32) == (('a' as libc::c_char) as i32)) { + n.prefix_inc(); + if !(true) { + break; + } + } + s.prefix_inc(); + } + return n; + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Mark__a___false_ {} +impl Mark__a___false_ { + pub unsafe fn count(&self, mut s: *const libc::c_char) -> i32 { + let mut n: i32 = 0; + 'loop_: while ((*s) != 0) { + if (((*s) as i32) == (('a' as libc::c_char) as i32)) { + n.prefix_inc(); + if !(false) { + break; + } + } + s.prefix_inc(); + } + return n; + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Mark__b___true_ {} +impl Mark__b___true_ { + pub unsafe fn count(&self, mut s: *const libc::c_char) -> i32 { + let mut n: i32 = 0; + 'loop_: while ((*s) != 0) { + if (((*s) as i32) == (('b' as libc::c_char) as i32)) { + n.prefix_inc(); + if !(true) { + break; + } + } + s.prefix_inc(); + } + return n; + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Range_0__10_ {} +impl Range_0__10_ { + pub unsafe fn contains(&self, mut x: i32) -> bool { + return ((x) >= (0)) && ((x) <= (10)); + } + pub unsafe fn width(&self) -> i32 { + return ((10) - (0)); + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Range_5__6_ {} +impl Range_5__6_ { + pub unsafe fn contains(&self, mut x: i32) -> bool { + return ((x) >= (5)) && ((x) <= (6)); + } + pub unsafe fn width(&self) -> i32 { + return ((6) - (5)); + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Range_neg3__0_ {} +impl Range_neg3__0_ { + pub unsafe fn contains(&self, mut x: i32) -> bool { + return ((x) >= (-3)) && ((x) <= (0)); + } + pub unsafe fn width(&self) -> i32 { + return ((0) - (-3)); + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Mix___xc8___neg3__10000000000_ {} +impl Mix___xc8___neg3__10000000000_ { + pub unsafe fn total(&self) -> i64 { + return (((((b'\xc8' as u8) as i32) + (-3_i16 as i32)) as i64) + (10000000000_i64)); + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Mix___x01___1__1_ {} +impl Mix___x01___1__1_ { + pub unsafe fn total(&self) -> i64 { + return ((((('\x01' as u8) as i32) + (1_i16 as i32)) as i64) + (1_i64)); + } +} +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut s2: Step_2_ = Step_2_ { value: 1 }; + let mut s3: Step_3_ = Step_3_ { value: 1 }; + (unsafe { Step_2_::advance(&mut s2) }); + (unsafe { Step_3_::advance(&mut s3) }); + assert!(((s2.value) == (3))); + assert!(((s3.value) == (4))); + assert!(((unsafe { Step_2_::scaled(&s2,) }) == (6))); + assert!(((unsafe { Step_3_::scaled(&s3,) }) == (12))); + let mut sn: Step_neg1_ = Step_neg1_ { value: 5 }; + let mut sp: Step_1_ = Step_1_ { value: 5 }; + (unsafe { Step_neg1_::advance(&mut sn) }); + (unsafe { Step_1_::advance(&mut sp) }); + assert!(((sn.value) == (4))); + assert!(((sp.value) == (6))); + assert!(((unsafe { Step_neg1_::scaled(&sn,) }) == (-4_i32))); + assert!(((unsafe { Step_1_::scaled(&sp,) }) == (6))); + let mut s0: Step_0_ = Step_0_ { value: 5 }; + (unsafe { Step_0_::advance(&mut s0) }); + assert!(((s0.value) == (-1_i32))); + assert!(((unsafe { Step_0_::scaled(&s0,) }) == (0))); + let mut yes: Choice_true_ = ::default(); + let mut no: Choice_false_ = ::default(); + assert!(((unsafe { Choice_true_::pick(&yes, 1, 2,) }) == (1))); + assert!(((unsafe { Choice_false_::pick(&no, 1, 2,) }) == (2))); + let mut b2: Buffer_int__2_ = Buffer_int__2_ { data: [1, 2] }; + let mut b3: Buffer_int__3_ = Buffer_int__3_ { data: [1, 2, 3] }; + assert!( + ((unsafe { Buffer_int__2_::size(&b2,) }) == (2)) + && ((unsafe { Buffer_int__2_::sum(&b2,) }) == (3)) + ); + assert!( + ((unsafe { Buffer_int__3_::size(&b3,) }) == (3)) + && ((unsafe { Buffer_int__3_::sum(&b3,) }) == (6)) + ); + let mut v2: Vec = Vec::new(); + let mut v3: Vec = Vec::new(); + v2.push(Step_2_ { value: 0 }); + v3.push(Step_3_ { value: 0 }); + (unsafe { Step_2_::advance(&mut v2[(0_usize)]) }); + (unsafe { Step_3_::advance(&mut v3[(0_usize)]) }); + assert!(((v2.len()) == (1_usize)) && ((v2[(0_usize)].value) == (2))); + assert!(((v3.len()) == (1_usize)) && ((v3[(0_usize)].value) == (3))); + let mut a2: Vec = vec![Step_2_ { value: 1 }, Step_2_ { value: 2 }]; + let mut a3: Vec = vec![ + Step_3_ { value: 1 }, + Step_3_ { value: 2 }, + Step_3_ { value: 3 }, + ]; + 'loop_: for s in 0..(a2.len()) { + let mut s = a2.as_mut_ptr().add(s); + (unsafe { Step_2_::advance(&mut (*s)) }); + } + 'loop_: for s in 0..(a3.len()) { + let mut s = a3.as_mut_ptr().add(s); + (unsafe { Step_3_::advance(&mut (*s)) }); + } + assert!(((a2.len()) == (2_usize)) && ((unsafe { Step_2_::scaled(&a2[(1_usize)],) }) == (8))); + assert!(((a3.len()) == (3_usize)) && ((unsafe { Step_3_::scaled(&a3[(2_usize)],) }) == (18))); + let mut vb2: Vec = Vec::new(); + let mut vb3: Vec = Vec::new(); + { + let a0_clone = b2.clone(); + vb2.push(a0_clone) + }; + { + let a0_clone = b3.clone(); + vb3.push(a0_clone) + }; + assert!( + ((unsafe { Buffer_int__2_::size(&vb2[(0_usize)],) }) == (2)) + && ((unsafe { Buffer_int__2_::sum(&vb2[(0_usize)],) }) == (3)) + ); + assert!( + ((unsafe { Buffer_int__3_::size(&vb3[(0_usize)],) }) == (3)) + && ((unsafe { Buffer_int__3_::sum(&vb3[(0_usize)],) }) == (6)) + ); + let mut ayes: Vec = vec![::default()]; + let mut ano: Vec = vec![::default()]; + assert!(((unsafe { Choice_true_::pick(&ayes[(0_usize)], 1, 2,) }) == (1))); + assert!(((unsafe { Choice_false_::pick(&ano[(0_usize)], 1, 2,) }) == (2))); + let mut all_a: Mark__a___true_ = ::default(); + let mut first_a: Mark__a___false_ = ::default(); + let mut all_b: Mark__b___true_ = ::default(); + assert!(((unsafe { Mark__a___true_::count(&all_a, c"banana".as_ptr(),) }) == (3))); + assert!(((unsafe { Mark__a___false_::count(&first_a, c"banana".as_ptr(),) }) == (1))); + assert!(((unsafe { Mark__b___true_::count(&all_b, c"banana".as_ptr(),) }) == (1))); + let mut wide: Range_0__10_ = ::default(); + let mut narrow: Range_5__6_ = ::default(); + let mut neg: Range_neg3__0_ = ::default(); + assert!( + (unsafe { Range_0__10_::contains(&wide, 7,) }) + && (!(unsafe { Range_5__6_::contains(&narrow, 7,) })) + ); + assert!( + ((unsafe { Range_0__10_::width(&wide,) }) == (10)) + && ((unsafe { Range_5__6_::width(&narrow,) }) == (1)) + ); + assert!( + (unsafe { Range_neg3__0_::contains(&neg, -2_i32,) }) + && ((unsafe { Range_neg3__0_::width(&neg,) }) == (3)) + ); + let mut big: Mix___xc8___neg3__10000000000_ = ::default(); + let mut small: Mix___x01___1__1_ = ::default(); + assert!(((unsafe { Mix___xc8___neg3__10000000000_::total(&big,) }) == (10000000197_i64))); + assert!(((unsafe { Mix___x01___1__1_::total(&small,) }) == (3_i64))); + let mut vwide: Vec = vec![wide]; + let mut vnarrow: Vec = vec![narrow]; + assert!( + (unsafe { Range_0__10_::contains(&vwide[(0_usize)], 7,) }) + && (!(unsafe { Range_5__6_::contains(&vnarrow[(0_usize)], 7,) })) + ); + let mut amarks: Vec = + vec![::default(), ::default()]; + let mut afirst: Vec = + vec![::default(), ::default()]; + assert!(((unsafe { Mark__a___true_::count(&amarks[(1_usize)], c"aaa".as_ptr(),) }) == (3))); + assert!(((unsafe { Mark__a___false_::count(&afirst[(1_usize)], c"aaa".as_ptr(),) }) == (1))); + let mut vbig: Vec = vec![big, big]; + assert!( + ((vbig.len()) == (2_usize)) + && ((unsafe { Mix___xc8___neg3__10000000000_::total(&vbig[(1_usize)],) }) + == (10000000197_i64)) + ); + return 0; +} +pub unsafe fn __cpp2rust_init_globals() {}