From c087ded28d1225c3f43a9fa1af0151d5362652fd Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 09:15:54 +0100 Subject: [PATCH 01/23] Add user-defined template with NTTP test --- tests/unit/class_template_nttp.cpp | 154 +++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 tests/unit/class_template_nttp.cpp 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; +} From c7ee48d78cc190bb229d1bbbd8a752239fa13fe7 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 09:28:58 +0100 Subject: [PATCH 02/23] Add rules dir --- cpp2rust/converter/rules/ir.cpp | 39 ++++ cpp2rust/converter/rules/ir.h | 45 +++++ cpp2rust/converter/rules/ir_src.cpp | 222 +++++++++++++++++++++ cpp2rust/converter/rules/ir_src.h | 143 ++++++++++++++ cpp2rust/converter/rules/ir_tgt.cpp | 286 ++++++++++++++++++++++++++++ cpp2rust/converter/rules/ir_tgt.h | 106 +++++++++++ 6 files changed, 841 insertions(+) create mode 100644 cpp2rust/converter/rules/ir.cpp create mode 100644 cpp2rust/converter/rules/ir.h create mode 100644 cpp2rust/converter/rules/ir_src.cpp create mode 100644 cpp2rust/converter/rules/ir_src.h create mode 100644 cpp2rust/converter/rules/ir_tgt.cpp create mode 100644 cpp2rust/converter/rules/ir_tgt.h 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..00a33463de --- /dev/null +++ b/cpp2rust/converter/rules/ir_src.cpp @@ -0,0 +1,222 @@ +// 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 "logging.h" + +namespace cpp2rust::IrSrc { + +namespace { + +using Kind = Node::Kind; + +constexpr std::pair kKindNames[] = { + {Kind::kParam, "param"}, {Kind::kValue, "value"}, + {Kind::kBuiltin, "builtin"}, {Kind::kRecord, "record"}, + {Kind::kEnum, "enum"}, {Kind::kTypedef, "typedef"}, + {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); +} + +Node ParseNodeJSON(const llvm::json::Value &value) { + const auto *obj = value.getAsObject(); + assert(obj && "IR node must be an object"); + Node node; + node.kind = kindFromName(*obj->getString("kind")); + if (auto name = obj->getString("name")) { + node.name = name->str(); + } + if (auto param = obj->getInteger("param")) { + node.param = *param; + } + node.is_const = obj->getBoolean("is_const").value_or(false); + node.is_volatile = obj->getBoolean("is_volatile").value_or(false); + node.variadic = obj->getBoolean("variadic").value_or(false); + if (auto ref = obj->getString("ref")) { + node.ref = ref->str(); + } + for (const auto &[key, field] : kNodeFields) { + if (const auto *child = obj->get(key)) { + node.*field = std::make_shared(ParseNodeJSON(*child)); + } + } + for (const auto &[key, field] : kNodeLists) { + if (const auto *list = obj->getArray(key)) { + for (const auto &child : *list) { + (node.*field).push_back(ParseNodeJSON(child)); + } + } + } + return node; +} + +ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { + ExprRule rule; + rule.ir = ParseNodeJSON(*obj.get("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")); + return rule; +} + +} // namespace + +bool Node::operator==(const Node &other) const { + return kind == other.kind && name == other.name && param == other.param && + is_const == other.is_const && is_volatile == other.is_volatile && + variadic == other.variadic && ref == other.ref && + zipChildren(*this, other, + [](const Node &a, const Node &b) { return a == b; }); +} + +unsigned Node::specificity() const { + unsigned n = kind == Kind::kParam ? 0 : 1; + zipChildren(*this, *this, [&](const Node &child, const Node &) { + n += child.specificity(); + return true; + }); + return n; +} + +void Node::forEachParam(const std::function &fn) const { + if (kind == Kind::kParam) { + fn(param); + } + zipChildren(*this, *this, [&](const Node &child, const Node &) { + child.forEachParam(fn); + return true; + }); +} + +std::string Node::str() const { + std::string out; + if (is_const) { + out += "const "; + } + if (is_volatile) { + out += "volatile "; + } + if (kind == Kind::kParam) { + return out + "T" + std::to_string(param); + } + out += kindName(kind); + if (!name.empty()) { + out += " " + name; + } + std::vector fields; + for (const auto &[key, field] : kNodeFields) { + if (this->*field) { + fields.push_back(std::string(key) + ": " + (this->*field)->str()); + } + } + for (const auto &[key, field] : kNodeLists) { + if (!(this->*field).empty()) { + std::string list; + for (const auto &child : this->*field) { + list += (list.empty() ? "" : ", ") + child.str(); + } + fields.push_back(std::string(key) + ": [" + list + "]"); + } + } + 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 (node.kind == Kind::kParam) { + obj["param"] = node.param; + } + if (node.is_const) { + obj["is_const"] = true; + } + if (node.is_volatile) { + obj["is_volatile"] = true; + } + if (node.variadic) { + obj["variadic"] = true; + } + if (!node.ref.empty()) { + obj["ref"] = node.ref; + } + for (const auto &[key, field] : kNodeFields) { + if (node.*field) { + obj[key] = ToJSON(*(node.*field)); + } + } + for (const auto &[key, field] : kNodeLists) { + if (!(node.*field).empty()) { + llvm::json::Array list; + for (const auto &child : node.*field) { + list.push_back(ToJSON(child)); + } + obj[key] = std::move(list); + } + } + 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..c6e52036df --- /dev/null +++ b/cpp2rust/converter/rules/ir_src.h @@ -0,0 +1,143 @@ +#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 "converter/rules/ir.h" + +namespace cpp2rust::IrSrc { + +struct Node { + enum class Kind { + kParam, + kValue, + kBuiltin, + kRecord, + kEnum, + kTypedef, + kPointer, + kLRef, + kRRef, + kArray, + kFunctionType, + kOpaque, + kFunction, + kDecl, + kMacro, + kUnary, + kArrow, + kPackParams, + }; + + Kind kind = Kind::kOpaque; + std::string name; + unsigned param = 0; + bool is_const = false; + bool is_volatile = false; + bool variadic = false; + std::string ref; + + std::shared_ptr class_; + std::shared_ptr return_type; + std::vector params; + std::vector args; + std::shared_ptr pointee; + std::shared_ptr element; + std::shared_ptr size; + std::shared_ptr operand; + std::shared_ptr object; + std::shared_ptr member; + + clang::QualType type; + + bool operator==(const Node &other) const; + unsigned specificity() const; + void forEachParam(const std::function &fn) const; + std::string str() const; + + template + static bool zipChildren(A &a, B &b, Fn fn); +}; + +inline constexpr std::pair Node::*> + kNodeFields[] = { + {"class", &Node::class_}, {"return_type", &Node::return_type}, + {"pointee", &Node::pointee}, {"element", &Node::element}, + {"size", &Node::size}, {"operand", &Node::operand}, + {"object", &Node::object}, {"member", &Node::member}, +}; + +inline constexpr std::pair Node::*> + kNodeLists[] = { + {"params", &Node::params}, + {"args", &Node::args}, +}; + +template +bool Node::zipChildren(A &a, B &b, Fn fn) { + for (const auto &entry : kNodeFields) { + auto &x = a.*entry.second; + auto &y = b.*entry.second; + if (!x || !y) { + if (x || y) { + return false; + } + continue; + } + if (!fn(*x, *y)) { + return false; + } + } + for (const auto &entry : kNodeLists) { + auto &x = a.*entry.second; + auto &y = b.*entry.second; + 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; +} + +llvm::json::Value ToJSON(const Node &node); + +struct InitTypeLocation { + unsigned depth = -1u; + unsigned index = -1u; + + bool valid() const { return depth != -1u; } +}; + +struct ExprRule { + Node ir; + InitTypeLocation init_type; + + void dump() const; +}; + +struct TypeRule { + Node ir; + + void dump() const; +}; + +using Rules = Ir::Rules; + +Rules Load(const std::filesystem::path &dir); + +} // namespace cpp2rust::IrSrc diff --git a/cpp2rust/converter/rules/ir_tgt.cpp b/cpp2rust/converter/rules/ir_tgt.cpp new file mode 100644 index 0000000000..3f7a52a71c --- /dev/null +++ b/cpp2rust/converter/rules/ir_tgt.cpp @@ -0,0 +1,286 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include "converter/rules/ir_tgt.h" + +#include + +#include +#include +#include + +#include "logging.h" + +namespace cpp2rust::IrTgt { + +namespace { + +TypeInfo ParseTypeInfoJSON(const llvm::json::Object &obj) { + TypeInfo info; + if (auto ty = obj.getString("type")) + info.type = ty->str(); + if (auto v = obj.getBoolean("is_refcount_pointer")) + info.is_refcount_pointer = *v; + if (auto v = obj.getBoolean("is_unsafe_pointer")) + info.is_unsafe_pointer = *v; + assert(!(info.is_refcount_pointer && info.is_unsafe_pointer)); + if (auto *arr = obj.getArray("derives")) { + for (const auto &elem : *arr) { + if (auto s = elem.getAsString()) + info.derives.emplace_back(s->str()); + } + } + return info; +} + +Access ParseAccessJSON(llvm::StringRef value) { + if (value == "borrow") { + return Access::kBorrow; + } else if (value == "borrow_mut") { + return Access::kBorrowMut; + } else if (value == "move") { + return Access::kMove; + } else if (value == "take") { + return Access::kTake; + } else { + llvm::errs() << "Invalid access value: " << value << '\n'; + assert(0); + return Access::kBorrow; + } +} + +PlaceholderFragment +ParsePlaceholderFragmentJSON(const llvm::json::Object &obj) { + auto access = obj.getString("access"); + return { + (unsigned)*obj.getInteger("arg"), + ParseAccessJSON(*access), + obj.getBoolean("is_index_base").value_or(false), + }; +} + +std::vector ParseBodyFragmentsJSON(const llvm::json::Array &arr); + +MethodCallFragment ParseMethodCallFragmentJSON(const llvm::json::Object &obj) { + MethodCallFragment mc; + if (auto *receiver = obj.getArray("receiver")) { + mc.receiver = ParseBodyFragmentsJSON(*receiver); + } + if (auto *body = obj.getArray("body")) { + mc.body = ParseBodyFragmentsJSON(*body); + } + return mc; +} + +std::vector ParseBodyFragmentsJSON(const llvm::json::Array &arr) { + std::vector result; + for (auto &frag : arr) { + auto *frag_obj = frag.getAsObject(); + if (!frag_obj) + continue; + if (auto str = frag_obj->getString("text")) { + result.push_back(TextFragment{str->str()}); + } else if (auto n = frag_obj->getInteger("generic")) { + result.push_back(GenericFragment{(unsigned)*n}); + } else if (auto *ph = frag_obj->getObject("placeholder")) { + result.push_back(ParsePlaceholderFragmentJSON(*ph)); + } else if (auto *mc = frag_obj->getObject("method_call")) { + result.push_back(std::make_unique( + ParseMethodCallFragmentJSON(*mc))); + } else if (frag_obj->get("va_args")) { + result.push_back(VaArgsFragment{}); + } else if (frag_obj->get("init")) { + result.push_back(InitFragment{}); + } + } + return result; +} + +ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { + ExprRule ir; + + if (auto *params = obj.getObject("params")) { + for (auto &[key, val] : *params) { + if (auto *param_obj = val.getAsObject()) { + size_t id = atoi(llvm::StringRef(key).data() + 1); + ir.params.resize(std::max(ir.params.size(), id + 1)); + ir.params[id] = ParseTypeInfoJSON(*param_obj); + } + } + } + + if (auto *rt = obj.getObject("return_type")) { + ir.return_type = ParseTypeInfoJSON(*rt); + } + + if (auto ms = obj.getBoolean("multi_statement")) { + ir.multi_statement = *ms; + } + + if (auto v = obj.getBoolean("is_extern")) { + ir.is_extern = *v; + } + + if (auto *generics = obj.getObject("generics")) { + for (auto &[key, val] : *generics) { + if (auto *arr = val.getAsArray()) { + std::vector bounds; + for (auto &b : *arr) { + if (auto s = b.getAsString()) + bounds.push_back(s->str()); + } + size_t id = atoi(llvm::StringRef(key).data() + 1) - 1; // starts in T1 + ir.generics.resize(std::max(ir.generics.size(), id + 1)); + ir.generics[id] = std::move(bounds); + } + } + } + + if (auto *body = obj.getArray("body")) { + ir.body = ParseBodyFragmentsJSON(*body); + } + + return ir; +} + +TypeRule ParseTypeRuleJSON(const llvm::json::Object &obj) { + TypeRule rule; + if (auto init = obj.getString("init")) + rule.initializer = init->str(); + rule.type_info = ParseTypeInfoJSON(obj); + return rule; +} + +void BodyFragmentDump(const BodyFragment &frag) { + if (auto *t = std::get_if(&frag)) { + t->dump(); + } else if (auto *p = std::get_if(&frag)) { + p->dump(); + } else if (auto *g = std::get_if(&frag)) { + g->dump(); + } else if (auto *v = std::get_if(&frag)) { + v->dump(); + } else if (auto *i = std::get_if(&frag)) { + i->dump(); + } else if (auto *mc = + std::get_if>(&frag)) { + (*mc)->dump(); + } +} + +bool HasInitFragment(const std::vector &body) { + return std::any_of(body.begin(), body.end(), [](const BodyFragment &frag) { + if (auto *mc = std::get_if>(&frag)) { + return HasInitFragment((*mc)->receiver) || HasInitFragment((*mc)->body); + } + return std::holds_alternative(frag); + }); +} + +} // namespace + +void TextFragment::dump() const { log() << " text: \"" << text << "\"\n"; } + +void VaArgsFragment::dump() const { log() << " va_args\n"; } + +void InitFragment::dump() const { log() << " init\n"; } + +void PlaceholderFragment::dump() const { + log() << " placeholder: " << n; + switch (access) { + case Access::kBorrow: + log() << " (borrow)\n"; + break; + case Access::kBorrowMut: + log() << " (borrow_mut)\n"; + break; + case Access::kMove: + log() << " (move)\n"; + break; + case Access::kTake: + log() << " (take)\n"; + break; + } +} + +const PlaceholderFragment *MethodCallFragment::getReceiverPlaceholder() const { + for (auto &frag : receiver) { + if (auto *ph = std::get_if(&frag)) { + return ph; + } + } + return nullptr; +} + +void MethodCallFragment::dump() const { + log() << " method_call:\n" + " receiver:\n"; + for (const auto &frag : receiver) { + BodyFragmentDump(frag); + } + log() << " body:\n"; + for (const auto &frag : body) { + BodyFragmentDump(frag); + } +} + +void ExprRule::dump() const { + unsigned i = 0; + for (auto &info : params) { + log() << " param a" << i++ << ": "; + info.dump(); + log() << '\n'; + } + if (!return_type.type.empty()) { + log() << " return: "; + return_type.dump(); + log() << '\n'; + } + i = 0; + for (auto &bounds : generics) { + log() << " generic T" << ++i << ':'; + for (auto &b : bounds) { + log() << ' ' << b; + } + log() << '\n'; + } + for (const auto &frag : body) { + BodyFragmentDump(frag); + } +} + +bool ExprRule::usesInit() const { return HasInitFragment(body); } + +void GenericFragment::dump() const { log() << " generic: " << n << '\n'; } + +void TypeInfo::dump() const { + log() << type; + if (is_refcount_pointer) + log() << " [rc_ptr]"; + if (is_unsafe_pointer) + log() << " [unsafe_ptr]"; + for (const auto &d : derives) + log() << " +" << d; +} + +void TypeRule::dump() const { + log() << " Rust type: "; + type_info.dump(); + log() << '\n'; + if (!initializer.empty()) { + log() << " init: " << initializer << '\n'; + } +} + +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) { + Ir::LoadJSON(rules, dir / "ir_refcount.json", false, ParseExprRuleJSON, + ParseTypeRuleJSON); + } + return rules; +} + +} // namespace cpp2rust::IrTgt diff --git a/cpp2rust/converter/rules/ir_tgt.h b/cpp2rust/converter/rules/ir_tgt.h new file mode 100644 index 0000000000..4f9aeded17 --- /dev/null +++ b/cpp2rust/converter/rules/ir_tgt.h @@ -0,0 +1,106 @@ +#pragma once + +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +#include +#include +#include +#include + +#include "converter/factory.h" +#include "converter/rules/ir.h" + +namespace cpp2rust::IrTgt { + +struct TextFragment { + std::string text; + + void dump() const; +}; + +enum class Access : int8_t { kBorrow, kBorrowMut, kMove, kTake }; + +struct PlaceholderFragment { + unsigned n; // "a0", "a1", ... + Access access; + bool is_index_base = false; + + void dump() const; +}; + +struct GenericFragment { + unsigned n; // "T1", "T2", ... + + void dump() const; +}; + +struct VaArgsFragment { + void dump() const; +}; + +struct InitFragment { + void dump() const; +}; + +struct MethodCallFragment; // forward declaration + +using BodyFragment = std::variant>; + +struct MethodCallFragment { + std::vector receiver; + std::vector body; + + const PlaceholderFragment *getReceiverPlaceholder() const; + void dump() const; +}; + +struct TypeInfo { + std::vector derives; + std::string type; + bool is_refcount_pointer = false; + bool is_unsafe_pointer = false; + + bool is_pointer() const { return is_refcount_pointer || is_unsafe_pointer; } + + void dump() const; +}; + +struct ExprRule { + std::vector params; + TypeInfo return_type; + std::vector> generics; // "T1" -> ["Ord", "Clone"] + std::vector body; + bool multi_statement = false; + bool is_extern = false; + + bool usesInit() const; + void dump() const; +}; + +struct TypeRule { + std::string initializer; // Rust initializer expression + TypeInfo type_info; + + void dump() const; + + static TypeRule Plain(std::string type) { + return {{}, {{}, std::move(type), false, false}}; + } + static TypeRule RefcountPtr(std::string type) { + return {{}, {{}, std::move(type), true, false}}; + } + static TypeRule UnsafePtr(std::string type) { + return {{}, {{}, std::move(type), false, true}}; + } +}; + +using Rules = Ir::Rules; + +Rules Load(const std::filesystem::path &dir, Model model); + +} // namespace cpp2rust::IrTgt From 02d4372294b14a51fedec59242b09e66c70830c6 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 09:29:21 +0100 Subject: [PATCH 03/23] Use tree based matching instead of string matching --- cpp2rust/converter/mapper.cpp | 978 ++++++++++++++++++++-------------- cpp2rust/converter/mapper.h | 30 +- 2 files changed, 603 insertions(+), 405 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 0464976ce2..59e8c39735 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -3,7 +3,10 @@ #include "converter/mapper.h" +#include +#include #include +#include #include #include #include @@ -19,7 +22,8 @@ #include #include "converter/converter_lib.h" -#include "converter/translation_rule.h" +#include "converter/rules/ir_src.h" +#include "converter/rules/ir_tgt.h" namespace cpp2rust::Mapper { @@ -29,10 +33,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 +58,144 @@ 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(); - } - - // 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; -} - constexpr const char kPackMarker[] = "&&..."; -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); -} +using Node = IrSrc::Node; +using Kind = Node::Kind; +using Bindings = std::vector>; -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)); +std::shared_ptr share(Node node) { + return std::make_shared(std::move(node)); } -// 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; - } +Node make(Kind kind, std::string name = {}) { + Node node; + node.kind = kind; + node.name = std::move(name); + return node; +} - if (i >= input_str.size()) { - return false; - } +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(); +} - if (input_str[i] != literal[j]) { - return false; +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()); } - - i++; - j++; + } else if (const auto *named = llvm::dyn_cast(dc); + named && (llvm::isa(named) || + llvm::isa(named))) { + parts.push_back(nameOf(named)); } - }; - - 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"); + } + std::string out; + for (auto it = parts.rbegin(); it != parts.rend(); ++it) { + if (!out.empty()) { + out += "::"; } + out += *it; + } + return out; +} - 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; - } +std::string IndexKey(const Node &node) { + switch (node.kind) { + case Kind::kFunction: + case Kind::kDecl: + case Kind::kRecord: + case Kind::kEnum: + case Kind::kTypedef: + case Kind::kBuiltin: + return node.name; + case Kind::kMacro: + return "macro:" + node.name; + case Kind::kUnary: + return "unary" + node.name + ":" + IndexKey(*node.operand); + case Kind::kArrow: + return IndexKey(*node.member); + case Kind::kPointer: + return "*" + IndexKey(*node.pointee); + case Kind::kLRef: + return "&" + IndexKey(*node.pointee); + case Kind::kRRef: + return "&&" + IndexKey(*node.pointee); + case Kind::kArray: + return "[]" + IndexKey(*node.element); + default: + return ""; + } +} - if (ang < 0 || par < 0 || sq < 0) { - return std::string::npos; - } +bool Match(const Node &rule, const Node &use, Bindings &bindings) { + if (rule.kind == Kind::kParam) { + if ((rule.is_const && !use.is_const) || + (rule.is_volatile && !use.is_volatile)) { + return false; } - - 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; + Node bound = use; + if (rule.is_const) { + bound.is_const = false; + bound.type.removeLocalConst(); + } + if (rule.is_volatile) { + bound.is_volatile = false; + bound.type.removeLocalVolatile(); + } + if (bindings.size() <= rule.param) { + bindings.resize(rule.param + 1); + } + auto &slot = bindings[rule.param]; + if (!slot) { + slot = std::move(bound); + return true; } + return *slot == bound; } - - while (si < instantiated.size() && std::isspace(instantiated[si])) { - si++; + if (rule.kind != use.kind || rule.name != use.name || + rule.is_const != use.is_const || rule.is_volatile != use.is_volatile || + rule.variadic != use.variadic || rule.ref != use.ref) { + return false; } + return Node::zipChildren(rule, use, [&](const Node &r, const Node &u) { + return Match(r, u, bindings); + }); +} - if (si != instantiated.size()) { - return std::nullopt; +void AddTypeRule(clang::QualType type, IrTgt::TypeRule &&rule) { + auto src = IrSrcBuilder(*ctx_).FromType(type); + auto key = IndexKey(src); + auto [begin, end] = types_.equal_range(key); + for (auto it = begin; it != end; ++it) { + if (it->second.src.ir == src) { + return; + } } - - return captured; + types_.emplace(std::move(key), TypeRule{{std::move(src)}, std::move(rule)}); } -// 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 +206,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.kind == Kind::kValue) { + repl = bound.name; + } 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,62 +228,125 @@ 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; - + Bindings bindings; + unsigned specificity = 0; for (; it != end; ++it) { - auto &this_rule = it->second; - auto this_subs = matchTemplate(this_rule.src, txt); - if (!this_subs) { + Bindings these; + const auto &ir = it->second.src.ir; + if (!Match(ir, use, these)) { 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); + if (!rule || ir.specificity() > specificity) { + rule = &it->second; + bindings = std::move(these); + specificity = ir.specificity(); } } - return {rule, std::move(subs)}; + return {rule, std::move(bindings)}; } -TranslationRule::ExprRule *search(const clang::Expr *expr) { +std::pair search(const clang::Expr *expr) { if (RefersToUserDefinedDecl(expr)) { - return nullptr; + return {}; } - 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(); + auto use = IrSrcBuilder(*ctx_).FromExpr(expr); + if (!use) { + return {}; + } + auto res = search(exprs_, *use, IndexKey(*use)); + log() << "search expr " << use->str() << ", result:\n"; + if (res.first) { + res.first->src.dump(); + res.first->tgt.dump(); } else { log() << "None\n"; } - return rule; + 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; - } - auto type = ToString(qual_type); - if (type == sugared) { - log() << "search type " << type << ", result: None\n"; - return {}; +Node typeIR(clang::QualType qual_type, bool sugared) { + IrSrcBuilder 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 res = search(types_, type, GetTypeMapKey(type)); - log() << "search type " << type - << ", result: " << (res.first ? res.first->type_info.type : "None") - << '\n'; - return res; + auto node = builder.FromType(qual_type); + if (node.kind != Kind::kArray) { + node.is_const = false; + node.is_volatile = false; + } + return node; +} + +std::pair search(clang::QualType qual_type) { + for (bool sugared : {true, false}) { + auto use = typeIR(qual_type, sugared); + auto key = IndexKey(use); + 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'; + return {&rule->tgt, std::move(bindings)}; + } + } + log() << "search type " << ToString(qual_type) << ", result: None\n"; + return {}; +} + +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"); + } + + bool has_generic[Ir::kMaxGenerics] = {false}; + src.ir.forEachParam([&](unsigned n) { + if (n >= 1 && n <= Ir::kMaxGenerics) { + has_generic[n - 1] = true; + } + }); + + for (size_t i = 0, e = tgt.generics.size(); i < e; ++i) { + if (!has_generic[i]) { + 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) { @@ -433,27 +356,37 @@ 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 = IndexKey(paired.src.ir); + 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 = IndexKey(paired.src.ir); 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)); + types_.emplace(std::move(key), std::move(paired)); + } + 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); } } } @@ -501,20 +434,6 @@ clang::QualType normalizeQualType(clang::QualType qual_type) { *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); - } - } - return instantiateTgt(subs, rule->type_info.type); -} - std::string normalizeTranslationRule(std::string rule) { // Detach pointer from double reference. Useful for matching translation // rules. @@ -535,6 +454,274 @@ std::string normalizeTranslationRule(std::string rule) { } // namespace +Node IrSrcBuilder::FromType(clang::QualType type) { + return fromType(type, true); +} + +Node IrSrcBuilder::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) { + Node node = make(Kind::kTypedef, name->getName().str()); + node.is_const = canonical.isConstQualified(); + node.is_volatile = canonical.isVolatileQualified(); + node.type = canonical; + return node; + } + if (const auto *predef = type->getAs()) { + Node node = + make(Kind::kTypedef, predef->getIdentifier()->getName().str()); + node.is_const = canonical.isConstQualified(); + node.is_volatile = canonical.isVolatileQualified(); + node.type = canonical; + return node; + } + if (const auto *ptr = type->getAs(); + ptr && keep_pointee_sugar && + keep_pointee_sugar(ptr->getPointeeType())) { + Node node = make(Kind::kPointer); + node.pointee = share(fromType(ptr->getPointeeType(), true)); + node.is_const = canonical.isConstQualified(); + node.is_volatile = canonical.isVolatileQualified(); + node.type = canonical; + return node; + } + } + return fromCanonical(type.getCanonicalType()); +} + +Node IrSrcBuilder::fromCanonical(clang::QualType canonical) { + Node 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 = make(Kind::kBuiltin, builtin->getName(policy).str()); + } else if (const auto *ptr = llvm::dyn_cast(type)) { + node = make(Kind::kPointer); + node.pointee = share(fromType(ptr->getPointeeType(), false)); + } else if (const auto *ref = + llvm::dyn_cast(type)) { + node = make(Kind::kLRef); + node.pointee = share(fromType(ref->getPointeeType(), false)); + } else if (const auto *ref = + llvm::dyn_cast(type)) { + node = make(Kind::kRRef); + node.pointee = share(fromType(ref->getPointeeType(), false)); + } else if (const auto *array = ctx_.getAsConstantArrayType(canonical)) { + node = make(Kind::kArray); + node.element = share(fromType(array->getElementType(), false)); + node.size = + share(make(Kind::kValue, llvm::toString(array->getSize(), 10, false))); + quals_on_element = true; + } else if (const auto *array = ctx_.getAsIncompleteArrayType(canonical)) { + node = make(Kind::kArray); + node.element = share(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 = make(Kind::kEnum, QualifiedName(enum_type->getDecl())); + } else if (const auto *proto = + llvm::dyn_cast(type)) { + node = make(Kind::kFunctionType); + node.variadic = proto->isVariadic(); + node.return_type = share(fromType(proto->getReturnType(), false)); + for (auto param : proto->getParamTypes()) { + node.params.push_back(fromType(param, false)); + } + } else { + node = make(Kind::kOpaque, canonical.getUnqualifiedType().getAsString()); + } + if (!quals_on_element) { + node.is_const |= canonical.isConstQualified(); + node.is_volatile |= canonical.isVolatileQualified(); + } + node.type = canonical; + return node; +} + +Node IrSrcBuilder::fromRecord(const clang::RecordDecl *decl) { + if (stand_in) { + if (auto param = stand_in(decl)) { + Node node = make(Kind::kParam); + node.param = *param; + return node; + } + } + if (const auto *cxx = llvm::dyn_cast(decl); + cxx && cxx->isLambda()) { + return make(Kind::kOpaque, "lambda"); + } + Node node = make(Kind::kRecord, QualifiedName(decl)); + if (const auto *spec = + llvm::dyn_cast(decl)) { + for (const auto &arg : spec->getTemplateArgs().asArray()) { + node.args.push_back(fromTemplateArg(arg)); + } + } + return node; +} + +Node IrSrcBuilder::fromTemplateArg(const clang::TemplateArgument &arg) { + switch (arg.getKind()) { + case clang::TemplateArgument::Type: + return fromType(arg.getAsType(), false); + case clang::TemplateArgument::Integral: { + Node node = make(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 make(Kind::kOpaque, spelling); + } + } +} + +std::shared_ptr IrSrcBuilder::classOf(const clang::Decl *decl) { + if (const auto *record = + llvm::dyn_cast(decl->getDeclContext())) { + Node node = fromRecord(record); + node.type = ctx_.getCanonicalTagType(record); + return share(std::move(node)); + } + return nullptr; +} + +Node IrSrcBuilder::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) { + Node node = make(Kind::kDecl, QualifiedName(decl)); + node.class_ = classOf(decl); + return node; + } + + Node node = make(Kind::kFunction, QualifiedName(func)); + node.variadic = func->isVariadic(); + node.class_ = classOf(func); + node.return_type = share(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(make(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; +} + +std::optional IrSrcBuilder::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 make(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) { + Node node = make(Kind::kArrow); + node.object = share(FromType(object)); + node.member = share(FromDecl(decl)); + return node; + }; + 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 std::nullopt; + } + Node node = + make(Kind::kUnary, + (uop->isPostfix() ? "post" : "") + + clang::UnaryOperator::getOpcodeStr(uop->getOpcode()).str()); + node.operand = share(std::move(*sub)); + return node; + } + + return std::nullopt; +} + PushASTContext::PushASTContext(clang::ASTContext &ctx) : prev_(ctx_) { ctx_ = &ctx; } @@ -544,10 +731,17 @@ 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 TranslationRule::ExprRule *GetExprRule(const clang::Expr *expr) { - return search(expr); +const IrTgt::ExprRule *GetExprRule(const clang::Expr *expr) { + auto rule = search(expr).first; + return rule ? &rule->tgt : nullptr; +} + +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 +760,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(IndexKey(IrSrcBuilder(*ctx_).FromDecl(decl)))) { return std::format("libcc2rs::{}_{}", decl->getNameAsString(), model_ == Model::kRefCount ? "refcount" : "unsafe"); } @@ -574,41 +768,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 +813,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 +855,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 = GetTypeForDecl(decl); + auto ptr = ctx_->getPointerType(type); + auto rs_name = ToRustName(ToString(type)); - 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 +871,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; } } diff --git a/cpp2rust/converter/mapper.h b/cpp2rust/converter/mapper.h index dbd6380f86..c14a83b4d2 100644 --- a/cpp2rust/converter/mapper.h +++ b/cpp2rust/converter/mapper.h @@ -7,10 +7,13 @@ #include #include +#include +#include #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 { @@ -24,12 +27,35 @@ class PushASTContext { clang::ASTContext *prev_; }; +class IrSrcBuilder { +public: + explicit IrSrcBuilder(clang::ASTContext &ctx) : ctx_(ctx) {} + + std::function(const clang::Decl *)> stand_in; + bool keep_builtin_typedef = false; + std::function keep_pointee_sugar; + + IrSrc::Node FromType(clang::QualType type); + IrSrc::Node FromDecl(const clang::NamedDecl *decl); + std::optional FromExpr(const clang::Expr *expr); + +private: + clang::ASTContext &ctx_; + + IrSrc::Node fromType(clang::QualType type, bool top); + IrSrc::Node fromCanonical(clang::QualType canonical); + IrSrc::Node fromTemplateArg(const clang::TemplateArgument &arg); + IrSrc::Node fromRecord(const clang::RecordDecl *decl); + std::shared_ptr classOf(const clang::Decl *decl); +}; + bool Contains(clang::QualType qual_type); 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); From af220899810c3208b38d51a24c9088622d69ec0c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 09:29:34 +0100 Subject: [PATCH 04/23] Build src IR in cpp-rule-preprocessor --- cpp2rust/cpp_rule_preprocessor.cpp | 314 ++++++++++++++++++++--------- 1 file changed, 214 insertions(+), 100 deletions(-) diff --git a/cpp2rust/cpp_rule_preprocessor.cpp b/cpp2rust/cpp_rule_preprocessor.cpp index 43a38ab6aa..a346af87e2 100644 --- a/cpp2rust/cpp_rule_preprocessor.cpp +++ b/cpp2rust/cpp_rule_preprocessor.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include "compat/platform_flags.h" @@ -118,128 +119,238 @@ 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::Node 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::Node &ir, const IrSrc::Node &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.kind = Kind::kParam; + ir.name.clear(); + ir.param = n; + return true; + } + if (ir.kind != alternate.kind || ir.name != alternate.name || + ir.param != alternate.param || ir.is_const != alternate.is_const || + ir.is_volatile != alternate.is_volatile || + ir.variadic != alternate.variadic || ir.ref != alternate.ref) { + return false; + } + return IrSrc::Node::zipChildren( + ir, alternate, [&](IrSrc::Node &a, const IrSrc::Node &b) { + return markNTTP(a, b, n, value, alternate_value); + }); + } + + Mapper::IrSrcBuilder builder() { + Mapper::IrSrcBuilder 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 (Mapper::HasFunctionParameterPack(func) && + Mapper::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 +700,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 +735,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)) { From 1787da3bc33ac587e680587606f5de6526ec4dea Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 09:29:50 +0100 Subject: [PATCH 05/23] Delete translation_rule.cpp --- cpp2rust/converter/translation_rule.cpp | 433 ------------------------ cpp2rust/converter/translation_rule.h | 119 ------- 2 files changed, 552 deletions(-) delete mode 100644 cpp2rust/converter/translation_rule.cpp delete mode 100644 cpp2rust/converter/translation_rule.h diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp deleted file mode 100644 index 34c7696c96..0000000000 --- a/cpp2rust/converter/translation_rule.cpp +++ /dev/null @@ -1,433 +0,0 @@ -// 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 -#include - -#include -#include -#include - -#include "logging.h" - -namespace cpp2rust::TranslationRule { - -namespace { - -TypeInfo ParseTypeInfoJSON(const llvm::json::Object &obj) { - TypeInfo info; - if (auto ty = obj.getString("type")) - info.type = ty->str(); - if (auto v = obj.getBoolean("is_refcount_pointer")) - info.is_refcount_pointer = *v; - if (auto v = obj.getBoolean("is_unsafe_pointer")) - info.is_unsafe_pointer = *v; - assert(!(info.is_refcount_pointer && info.is_unsafe_pointer)); - if (auto *arr = obj.getArray("derives")) { - for (const auto &elem : *arr) { - if (auto s = elem.getAsString()) - info.derives.emplace_back(s->str()); - } - } - return info; -} - -Access ParseAccessJSON(llvm::StringRef value) { - if (value == "borrow") { - return Access::kBorrow; - } else if (value == "borrow_mut") { - return Access::kBorrowMut; - } else if (value == "move") { - return Access::kMove; - } else if (value == "take") { - return Access::kTake; - } else { - llvm::errs() << "Invalid access value: " << value << '\n'; - assert(0); - return Access::kBorrow; - } -} - -PlaceholderFragment -ParsePlaceholderFragmentJSON(const llvm::json::Object &obj) { - auto access = obj.getString("access"); - return { - (unsigned)*obj.getInteger("arg"), - ParseAccessJSON(*access), - obj.getBoolean("is_index_base").value_or(false), - }; -} - -std::vector ParseBodyFragmentsJSON(const llvm::json::Array &arr); - -MethodCallFragment ParseMethodCallFragmentJSON(const llvm::json::Object &obj) { - MethodCallFragment mc; - if (auto *receiver = obj.getArray("receiver")) { - mc.receiver = ParseBodyFragmentsJSON(*receiver); - } - if (auto *body = obj.getArray("body")) { - mc.body = ParseBodyFragmentsJSON(*body); - } - return mc; -} - -std::vector ParseBodyFragmentsJSON(const llvm::json::Array &arr) { - std::vector result; - for (auto &frag : arr) { - auto *frag_obj = frag.getAsObject(); - if (!frag_obj) - continue; - if (auto str = frag_obj->getString("text")) { - result.push_back(TextFragment{str->str()}); - } else if (auto n = frag_obj->getInteger("generic")) { - result.push_back(GenericFragment{(unsigned)*n}); - } else if (auto *ph = frag_obj->getObject("placeholder")) { - result.push_back(ParsePlaceholderFragmentJSON(*ph)); - } else if (auto *mc = frag_obj->getObject("method_call")) { - result.push_back(std::make_unique( - ParseMethodCallFragmentJSON(*mc))); - } else if (frag_obj->get("va_args")) { - result.push_back(VaArgsFragment{}); - } else if (frag_obj->get("init")) { - result.push_back(InitFragment{}); - } - } - return result; -} - -ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { - ExprRule ir; - - if (auto *params = obj.getObject("params")) { - for (auto &[key, val] : *params) { - if (auto *param_obj = val.getAsObject()) { - size_t id = atoi(llvm::StringRef(key).data() + 1); - ir.params.resize(std::max(ir.params.size(), id + 1)); - ir.params[id] = ParseTypeInfoJSON(*param_obj); - } - } - } - - if (auto *rt = obj.getObject("return_type")) { - ir.return_type = ParseTypeInfoJSON(*rt); - } - - if (auto ms = obj.getBoolean("multi_statement")) { - ir.multi_statement = *ms; - } - - if (auto v = obj.getBoolean("is_extern")) { - ir.is_extern = *v; - } - - if (auto *generics = obj.getObject("generics")) { - for (auto &[key, val] : *generics) { - if (auto *arr = val.getAsArray()) { - std::vector bounds; - for (auto &b : *arr) { - if (auto s = b.getAsString()) - bounds.push_back(s->str()); - } - size_t id = atoi(llvm::StringRef(key).data() + 1) - 1; // starts in T1 - ir.generics.resize(std::max(ir.generics.size(), id + 1)); - ir.generics[id] = std::move(bounds); - } - } - } - - if (auto *body = obj.getArray("body")) { - ir.body = ParseBodyFragmentsJSON(*body); - } - - return ir; -} - -TypeRule ParseTypeRuleJSON(const llvm::json::Object &obj) { - TypeRule rule; - if (auto init = obj.getString("init")) - rule.initializer = init->str(); - rule.type_info = ParseTypeInfoJSON(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(); - } else if (auto *p = std::get_if(&frag)) { - p->dump(); - } else if (auto *g = std::get_if(&frag)) { - g->dump(); - } else if (auto *v = std::get_if(&frag)) { - v->dump(); - } else if (auto *i = std::get_if(&frag)) { - i->dump(); - } else if (auto *mc = - std::get_if>(&frag)) { - (*mc)->dump(); - } -} - -bool HasInitFragment(const std::vector &body) { - return std::any_of(body.begin(), body.end(), [](const BodyFragment &frag) { - if (auto *mc = std::get_if>(&frag)) { - return HasInitFragment((*mc)->receiver) || HasInitFragment((*mc)->body); - } - return std::holds_alternative(frag); - }); -} - -} // namespace - -void TextFragment::dump() const { log() << " text: \"" << text << "\"\n"; } - -void VaArgsFragment::dump() const { log() << " va_args\n"; } - -void InitFragment::dump() const { log() << " init\n"; } - -void PlaceholderFragment::dump() const { - log() << " placeholder: " << n; - switch (access) { - case Access::kBorrow: - log() << " (borrow)\n"; - break; - case Access::kBorrowMut: - log() << " (borrow_mut)\n"; - break; - case Access::kMove: - log() << " (move)\n"; - break; - case Access::kTake: - log() << " (take)\n"; - break; - } -} - -const PlaceholderFragment *MethodCallFragment::getReceiverPlaceholder() const { - for (auto &frag : receiver) { - if (auto *ph = std::get_if(&frag)) { - return ph; - } - } - return nullptr; -} - -void MethodCallFragment::dump() const { - log() << " method_call:\n" - " receiver:\n"; - for (const auto &frag : receiver) { - BodyFragmentDump(frag); - } - log() << " body:\n"; - for (const auto &frag : body) { - BodyFragmentDump(frag); - } -} - -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++ << ": "; - info.dump(); - log() << '\n'; - } - if (!return_type.type.empty()) { - log() << " return: "; - return_type.dump(); - log() << '\n'; - } - i = 0; - for (auto &bounds : generics) { - log() << " generic T" << ++i << ':'; - for (auto &b : bounds) { - log() << ' ' << b; - } - log() << '\n'; - } - for (const auto &frag : body) { - BodyFragmentDump(frag); - } -} - -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"); - } - } -} - -void GenericFragment::dump() const { log() << " generic: " << n << '\n'; } - -void TypeInfo::dump() const { - log() << type; - if (is_refcount_pointer) - log() << " [rc_ptr]"; - if (is_unsafe_pointer) - log() << " [unsafe_ptr]"; - for (const auto &d : derives) - log() << " +" << d; -} - -void TypeRule::dump() const { - log() << "name: " << src << "\n Rust type: "; - type_info.dump(); - log() << '\n'; - if (!initializer.empty()) { - log() << " init: " << initializer << '\n'; - } -} - -std::pair Load(const std::filesystem::path &dir, - Model model) { - ExprRules exprs; - TypeRules types; - LoadTgtFromIR(exprs, types, dir / "ir_unsafe.json"); - - 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"); - } - } - return {std::move(exprs), std::move(types)}; -} - -} // namespace cpp2rust::TranslationRule diff --git a/cpp2rust/converter/translation_rule.h b/cpp2rust/converter/translation_rule.h deleted file mode 100644 index 8d6846ef8e..0000000000 --- a/cpp2rust/converter/translation_rule.h +++ /dev/null @@ -1,119 +0,0 @@ -#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/factory.h" - -namespace cpp2rust::TranslationRule { - -static inline constexpr unsigned kMaxGenerics = 9; - -struct TextFragment { - std::string text; - - void dump() const; -}; - -enum class Access : int8_t { kBorrow, kBorrowMut, kMove, kTake }; - -struct PlaceholderFragment { - unsigned n; // "a0", "a1", ... - Access access; - bool is_index_base = false; - - void dump() const; -}; - -struct GenericFragment { - unsigned n; // "T1", "T2", ... - - void dump() const; -}; - -struct VaArgsFragment { - void dump() const; -}; - -struct InitFragment { - void dump() const; -}; - -struct MethodCallFragment; // forward declaration - -using BodyFragment = std::variant>; - -struct MethodCallFragment { - std::vector receiver; - std::vector body; - - const PlaceholderFragment *getReceiverPlaceholder() const; - void dump() const; -}; - -struct TypeInfo { - std::vector derives; - std::string type; - bool is_refcount_pointer = false; - bool is_unsafe_pointer = false; - - bool is_pointer() const { return is_refcount_pointer || is_unsafe_pointer; } - - 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"] - std::vector body; - bool multi_statement = false; - bool is_extern = false; - - 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}}; - } - static TypeRule RefcountPtr(std::string type) { - return {{}, {}, {{}, std::move(type), true, false}}; - } - static TypeRule UnsafePtr(std::string type) { - return {{}, {}, {{}, std::move(type), false, true}}; - } -}; - -using ExprRules = std::unordered_map; -using TypeRules = std::unordered_map; - -std::pair Load(const std::filesystem::path &dir, - Model model); -} // namespace cpp2rust::TranslationRule From dea66d4d2ac1e18053d7e9e23b597e49d97ca3c2 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 09:30:11 +0100 Subject: [PATCH 06/23] Use new names in the converter --- cpp2rust/converter/converter.cpp | 35 +++++++++---------- cpp2rust/converter/converter.h | 12 +++---- .../converter/models/converter_refcount.cpp | 8 ++--- .../converter/models/converter_refcount.h | 9 +++-- 4 files changed, 30 insertions(+), 34 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index d5c6ed73bb..2f8203acdc 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 { @@ -4886,8 +4885,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 +4900,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 +4924,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 +4932,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 +4952,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 +4987,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 +5023,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/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, From 3059ca8533600287c854e71264cea947db6d41b0 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 09:35:36 +0100 Subject: [PATCH 07/23] Avoid clash of anonymous records --- cpp2rust/converter/mapper.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 59e8c39735..4bf116a758 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -119,6 +119,13 @@ std::string QualifiedName(const clang::NamedDecl *decl) { return out; } +std::string tagName(const clang::TagDecl *tag) { + if (!tag->getIdentifier() || tag->getDeclContext()->isFunctionOrMethod()) { + return ToString(tag->getASTContext().getCanonicalTagType(tag)); + } + return QualifiedName(tag); +} + std::string IndexKey(const Node &node) { switch (node.kind) { case Kind::kFunction: @@ -531,7 +538,7 @@ Node IrSrcBuilder::fromCanonical(clang::QualType canonical) { } else if (const auto *record = type->getAsRecordDecl()) { node = fromRecord(record); } else if (const auto *enum_type = llvm::dyn_cast(type)) { - node = make(Kind::kEnum, QualifiedName(enum_type->getDecl())); + node = make(Kind::kEnum, tagName(enum_type->getDecl())); } else if (const auto *proto = llvm::dyn_cast(type)) { node = make(Kind::kFunctionType); @@ -563,7 +570,7 @@ Node IrSrcBuilder::fromRecord(const clang::RecordDecl *decl) { cxx && cxx->isLambda()) { return make(Kind::kOpaque, "lambda"); } - Node node = make(Kind::kRecord, QualifiedName(decl)); + Node node = make(Kind::kRecord, tagName(decl)); if (const auto *spec = llvm::dyn_cast(decl)) { for (const auto &arg : spec->getTemplateArgs().asArray()) { From d5fe14cab161f078e9fac2a8ae07b9e4c6354423 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 09:42:16 +0100 Subject: [PATCH 08/23] Stop masking NTTPs --- cpp2rust/converter/mapper.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 4bf116a758..e5805162df 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -445,17 +444,6 @@ 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; } From 169b2f94fedef0259102e5965b5259dc25a95ad1 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 09:42:33 +0100 Subject: [PATCH 09/23] Use tag decl when adding a rule for a user-defined record --- cpp2rust/converter/mapper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index e5805162df..a359af4f06 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -850,9 +850,9 @@ clang::QualType GetTypeForDecl(const clang::NamedDecl *decl) { } void AddRuleForUserDefinedType(clang::NamedDecl *decl) { - auto type = GetTypeForDecl(decl); + auto type = ctx_->getCanonicalTagType(clang::cast(decl)); auto ptr = ctx_->getPointerType(type); - auto rs_name = ToRustName(ToString(type)); + auto rs_name = ToRustName(ToString(GetTypeForDecl(decl))); AddTypeRule(type, IrTgt::TypeRule::Plain(rs_name)); From 206c4309c45feccd95d79e76a4a5fe5e49d4b440 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 09:44:45 +0100 Subject: [PATCH 10/23] Fix printing of integer NTTPs --- cpp2rust/converter/converter.cpp | 3 ++- cpp2rust/converter/converter_lib.h | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 2f8203acdc..cba5db2ffb 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2203,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) { diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 6e0a8cc98f..20999f882f 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -175,6 +176,17 @@ 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); From 7d26a1ff317c4acfcec8053fe4b636412928effc Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 09:44:56 +0100 Subject: [PATCH 11/23] Include class inside node --- cpp2rust/converter/mapper.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index a359af4f06..ecc02e250b 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -559,6 +559,7 @@ Node IrSrcBuilder::fromRecord(const clang::RecordDecl *decl) { return make(Kind::kOpaque, "lambda"); } Node node = make(Kind::kRecord, tagName(decl)); + node.class_ = classOf(decl); if (const auto *spec = llvm::dyn_cast(decl)) { for (const auto &arg : spec->getTemplateArgs().asArray()) { From 30b0c2440e11a543e7fc9b9bf21571b124d4fefc Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 09:45:34 +0100 Subject: [PATCH 12/23] Update tests --- .../unit/out/refcount/class_template_nttp.rs | 725 ++++++++++++++++++ tests/unit/out/unsafe/class_template_nttp.rs | 373 +++++++++ 2 files changed, 1098 insertions(+) create mode 100644 tests/unit/out/refcount/class_template_nttp.rs create mode 100644 tests/unit/out/unsafe/class_template_nttp.rs 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() {} From 2b4705feb2724c847ae31ec63a9619dc0fe81dd1 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 10:21:37 +0100 Subject: [PATCH 13/23] Drop namespace --- cpp2rust/converter/converter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index cba5db2ffb..deff1a2957 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -4988,9 +4988,9 @@ Converter::ConvertIRFragment(const std::vector &fragments, .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)) { From 875194befc3d0cf3f198cfb26988d6d5fdc6d98e Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 10:24:24 +0100 Subject: [PATCH 14/23] Move IrSrcBuilder in ir_src.cpp --- cpp2rust/converter/converter_lib.cpp | 7 + cpp2rust/converter/converter_lib.h | 2 + cpp2rust/converter/mapper.cpp | 349 +-------------------------- cpp2rust/converter/mapper.h | 26 -- cpp2rust/converter/rules/ir_src.cpp | 344 +++++++++++++++++++++++++- cpp2rust/converter/rules/ir_src.h | 29 +++ cpp2rust/cpp_rule_preprocessor.cpp | 7 +- 7 files changed, 388 insertions(+), 376 deletions(-) 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 20999f882f..47fcc06a6b 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -193,6 +193,8 @@ 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 ecc02e250b..0163e54c85 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -3,10 +3,7 @@ #include "converter/mapper.h" -#include -#include #include -#include #include #include #include @@ -63,68 +60,6 @@ using Node = IrSrc::Node; using Kind = Node::Kind; using Bindings = std::vector>; -std::shared_ptr share(Node node) { - return std::make_shared(std::move(node)); -} - -Node make(Kind kind, std::string name = {}) { - Node node; - node.kind = kind; - node.name = std::move(name); - 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->getDeclContext()->isFunctionOrMethod()) { - return ToString(tag->getASTContext().getCanonicalTagType(tag)); - } - return QualifiedName(tag); -} - std::string IndexKey(const Node &node) { switch (node.kind) { case Kind::kFunction: @@ -189,7 +124,7 @@ bool Match(const Node &rule, const Node &use, Bindings &bindings) { } void AddTypeRule(clang::QualType type, IrTgt::TypeRule &&rule) { - auto src = IrSrcBuilder(*ctx_).FromType(type); + auto src = IrSrc::Builder(*ctx_).FromType(type); auto key = IndexKey(src); auto [begin, end] = types_.equal_range(key); for (auto it = begin; it != end; ++it) { @@ -259,7 +194,7 @@ std::pair search(const clang::Expr *expr) { if (RefersToUserDefinedDecl(expr)) { return {}; } - auto use = IrSrcBuilder(*ctx_).FromExpr(expr); + auto use = IrSrc::Builder(*ctx_).FromExpr(expr); if (!use) { return {}; } @@ -275,7 +210,7 @@ std::pair search(const clang::Expr *expr) { } Node typeIR(clang::QualType qual_type, bool sugared) { - IrSrcBuilder builder(*ctx_); + IrSrc::Builder builder(*ctx_); if (sugared) { builder.keep_builtin_typedef = true; builder.keep_pointee_sugar = [](clang::QualType pointee) { @@ -449,275 +384,6 @@ std::string normalizeTranslationRule(std::string rule) { } // namespace -Node IrSrcBuilder::FromType(clang::QualType type) { - return fromType(type, true); -} - -Node IrSrcBuilder::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) { - Node node = make(Kind::kTypedef, name->getName().str()); - node.is_const = canonical.isConstQualified(); - node.is_volatile = canonical.isVolatileQualified(); - node.type = canonical; - return node; - } - if (const auto *predef = type->getAs()) { - Node node = - make(Kind::kTypedef, predef->getIdentifier()->getName().str()); - node.is_const = canonical.isConstQualified(); - node.is_volatile = canonical.isVolatileQualified(); - node.type = canonical; - return node; - } - if (const auto *ptr = type->getAs(); - ptr && keep_pointee_sugar && - keep_pointee_sugar(ptr->getPointeeType())) { - Node node = make(Kind::kPointer); - node.pointee = share(fromType(ptr->getPointeeType(), true)); - node.is_const = canonical.isConstQualified(); - node.is_volatile = canonical.isVolatileQualified(); - node.type = canonical; - return node; - } - } - return fromCanonical(type.getCanonicalType()); -} - -Node IrSrcBuilder::fromCanonical(clang::QualType canonical) { - Node 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 = make(Kind::kBuiltin, builtin->getName(policy).str()); - } else if (const auto *ptr = llvm::dyn_cast(type)) { - node = make(Kind::kPointer); - node.pointee = share(fromType(ptr->getPointeeType(), false)); - } else if (const auto *ref = - llvm::dyn_cast(type)) { - node = make(Kind::kLRef); - node.pointee = share(fromType(ref->getPointeeType(), false)); - } else if (const auto *ref = - llvm::dyn_cast(type)) { - node = make(Kind::kRRef); - node.pointee = share(fromType(ref->getPointeeType(), false)); - } else if (const auto *array = ctx_.getAsConstantArrayType(canonical)) { - node = make(Kind::kArray); - node.element = share(fromType(array->getElementType(), false)); - node.size = - share(make(Kind::kValue, llvm::toString(array->getSize(), 10, false))); - quals_on_element = true; - } else if (const auto *array = ctx_.getAsIncompleteArrayType(canonical)) { - node = make(Kind::kArray); - node.element = share(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 = make(Kind::kEnum, tagName(enum_type->getDecl())); - } else if (const auto *proto = - llvm::dyn_cast(type)) { - node = make(Kind::kFunctionType); - node.variadic = proto->isVariadic(); - node.return_type = share(fromType(proto->getReturnType(), false)); - for (auto param : proto->getParamTypes()) { - node.params.push_back(fromType(param, false)); - } - } else { - node = make(Kind::kOpaque, canonical.getUnqualifiedType().getAsString()); - } - if (!quals_on_element) { - node.is_const |= canonical.isConstQualified(); - node.is_volatile |= canonical.isVolatileQualified(); - } - node.type = canonical; - return node; -} - -Node IrSrcBuilder::fromRecord(const clang::RecordDecl *decl) { - if (stand_in) { - if (auto param = stand_in(decl)) { - Node node = make(Kind::kParam); - node.param = *param; - return node; - } - } - if (const auto *cxx = llvm::dyn_cast(decl); - cxx && cxx->isLambda()) { - return make(Kind::kOpaque, "lambda"); - } - Node node = make(Kind::kRecord, 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; -} - -Node IrSrcBuilder::fromTemplateArg(const clang::TemplateArgument &arg) { - switch (arg.getKind()) { - case clang::TemplateArgument::Type: - return fromType(arg.getAsType(), false); - case clang::TemplateArgument::Integral: { - Node node = make(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 make(Kind::kOpaque, spelling); - } - } -} - -std::shared_ptr IrSrcBuilder::classOf(const clang::Decl *decl) { - if (const auto *record = - llvm::dyn_cast(decl->getDeclContext())) { - Node node = fromRecord(record); - node.type = ctx_.getCanonicalTagType(record); - return share(std::move(node)); - } - return nullptr; -} - -Node IrSrcBuilder::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) { - Node node = make(Kind::kDecl, QualifiedName(decl)); - node.class_ = classOf(decl); - return node; - } - - Node node = make(Kind::kFunction, QualifiedName(func)); - node.variadic = func->isVariadic(); - node.class_ = classOf(func); - node.return_type = share(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(make(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; -} - -std::optional IrSrcBuilder::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 make(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) { - Node node = make(Kind::kArrow); - node.object = share(FromType(object)); - node.member = share(FromDecl(decl)); - return node; - }; - 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 std::nullopt; - } - Node node = - make(Kind::kUnary, - (uop->isPostfix() ? "post" : "") + - clang::UnaryOperator::getOpcodeStr(uop->getOpcode()).str()); - node.operand = share(std::move(*sub)); - return node; - } - - return std::nullopt; -} - PushASTContext::PushASTContext(clang::ASTContext &ctx) : prev_(ctx_) { ctx_ = &ctx; } @@ -756,7 +422,7 @@ bool IsLibcPassthrough(const clang::Expr *expr) { std::string MapFunctionName(const clang::FunctionDecl *decl) { assert(decl); if (!IsUserDefinedDecl(decl) && - exprs_.contains(IndexKey(IrSrcBuilder(*ctx_).FromDecl(decl)))) { + exprs_.contains(IndexKey(IrSrc::Builder(*ctx_).FromDecl(decl)))) { return std::format("libcc2rs::{}_{}", decl->getNameAsString(), model_ == Model::kRefCount ? "refcount" : "unsafe"); } @@ -967,13 +633,6 @@ std::string ToString(clang::QualType qual_type, ScalarSugar sugar) { 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(); -} - std::string ToString(const clang::NamedDecl *decl) { if (auto *record = clang::dyn_cast(decl); record && !record->getIdentifier()) { diff --git a/cpp2rust/converter/mapper.h b/cpp2rust/converter/mapper.h index c14a83b4d2..28408d5eeb 100644 --- a/cpp2rust/converter/mapper.h +++ b/cpp2rust/converter/mapper.h @@ -7,8 +7,6 @@ #include #include -#include -#include #include #include "converter/factory.h" @@ -27,28 +25,6 @@ class PushASTContext { clang::ASTContext *prev_; }; -class IrSrcBuilder { -public: - explicit IrSrcBuilder(clang::ASTContext &ctx) : ctx_(ctx) {} - - std::function(const clang::Decl *)> stand_in; - bool keep_builtin_typedef = false; - std::function keep_pointee_sugar; - - IrSrc::Node FromType(clang::QualType type); - IrSrc::Node FromDecl(const clang::NamedDecl *decl); - std::optional FromExpr(const clang::Expr *expr); - -private: - clang::ASTContext &ctx_; - - IrSrc::Node fromType(clang::QualType type, bool top); - IrSrc::Node fromCanonical(clang::QualType canonical); - IrSrc::Node fromTemplateArg(const clang::TemplateArgument &arg); - IrSrc::Node fromRecord(const clang::RecordDecl *decl); - std::shared_ptr classOf(const clang::Decl *decl); -}; - bool Contains(clang::QualType qual_type); bool Contains(const clang::Expr *expr); @@ -72,8 +48,6 @@ enum class ScalarSugar { 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); diff --git a/cpp2rust/converter/rules/ir_src.cpp b/cpp2rust/converter/rules/ir_src.cpp index 00a33463de..72793aac38 100644 --- a/cpp2rust/converter/rules/ir_src.cpp +++ b/cpp2rust/converter/rules/ir_src.cpp @@ -3,9 +3,16 @@ #include "converter/rules/ir_src.h" +#include +#include +#include +#include +#include + #include #include +#include "converter/converter_lib.h" #include "logging.h" namespace cpp2rust::IrSrc { @@ -65,7 +72,7 @@ Node ParseNodeJSON(const llvm::json::Value &value) { } for (const auto &[key, field] : kNodeFields) { if (const auto *child = obj->get(key)) { - node.*field = std::make_shared(ParseNodeJSON(*child)); + node.*field = Share(ParseNodeJSON(*child)); } } for (const auto &[key, field] : kNodeLists) { @@ -96,6 +103,63 @@ TypeRule ParseTypeRuleJSON(const llvm::json::Object &obj) { return rule; } +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 bool Node::operator==(const Node &other) const { @@ -165,6 +229,17 @@ std::string Node::str() const { return out; } +Node Make(Kind kind, std::string name) { + Node node; + node.kind = kind; + node.name = std::move(name); + return node; +} + +std::shared_ptr Share(Node node) { + return std::make_shared(std::move(node)); +} + llvm::json::Value ToJSON(const Node &node) { llvm::json::Object obj{{"kind", kindName(node.kind)}}; if (!node.name.empty()) { @@ -219,4 +294,271 @@ Rules Load(const std::filesystem::path &dir) { return rules; } +Node Builder::FromType(clang::QualType type) { return fromType(type, true); } + +Node 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) { + Node node = Make(Kind::kTypedef, name->getName().str()); + node.is_const = canonical.isConstQualified(); + node.is_volatile = canonical.isVolatileQualified(); + node.type = canonical; + return node; + } + if (const auto *predef = type->getAs()) { + Node node = + Make(Kind::kTypedef, predef->getIdentifier()->getName().str()); + node.is_const = canonical.isConstQualified(); + node.is_volatile = canonical.isVolatileQualified(); + node.type = canonical; + return node; + } + if (const auto *ptr = type->getAs(); + ptr && keep_pointee_sugar && + keep_pointee_sugar(ptr->getPointeeType())) { + Node node = Make(Kind::kPointer); + node.pointee = Share(fromType(ptr->getPointeeType(), true)); + node.is_const = canonical.isConstQualified(); + node.is_volatile = canonical.isVolatileQualified(); + node.type = canonical; + return node; + } + } + return fromCanonical(type.getCanonicalType()); +} + +Node Builder::fromCanonical(clang::QualType canonical) { + Node 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 = Make(Kind::kBuiltin, builtin->getName(policy).str()); + } else if (const auto *ptr = llvm::dyn_cast(type)) { + node = Make(Kind::kPointer); + node.pointee = Share(fromType(ptr->getPointeeType(), false)); + } else if (const auto *ref = + llvm::dyn_cast(type)) { + node = Make(Kind::kLRef); + node.pointee = Share(fromType(ref->getPointeeType(), false)); + } else if (const auto *ref = + llvm::dyn_cast(type)) { + node = Make(Kind::kRRef); + node.pointee = Share(fromType(ref->getPointeeType(), false)); + } else if (const auto *array = ctx_.getAsConstantArrayType(canonical)) { + node = Make(Kind::kArray); + node.element = Share(fromType(array->getElementType(), false)); + node.size = + Share(Make(Kind::kValue, llvm::toString(array->getSize(), 10, false))); + quals_on_element = true; + } else if (const auto *array = ctx_.getAsIncompleteArrayType(canonical)) { + node = Make(Kind::kArray); + node.element = Share(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 = Make(Kind::kEnum, tagName(enum_type->getDecl())); + } else if (const auto *proto = + llvm::dyn_cast(type)) { + node = Make(Kind::kFunctionType); + node.variadic = proto->isVariadic(); + node.return_type = Share(fromType(proto->getReturnType(), false)); + for (auto param : proto->getParamTypes()) { + node.params.push_back(fromType(param, false)); + } + } else { + node = Make(Kind::kOpaque, canonical.getUnqualifiedType().getAsString()); + } + if (!quals_on_element) { + node.is_const |= canonical.isConstQualified(); + node.is_volatile |= canonical.isVolatileQualified(); + } + node.type = canonical; + return node; +} + +Node Builder::fromRecord(const clang::RecordDecl *decl) { + if (stand_in) { + if (auto param = stand_in(decl)) { + Node node = Make(Kind::kParam); + node.param = *param; + return node; + } + } + if (const auto *cxx = llvm::dyn_cast(decl); + cxx && cxx->isLambda()) { + return Make(Kind::kOpaque, "lambda"); + } + Node node = Make(Kind::kRecord, 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; +} + +Node Builder::fromTemplateArg(const clang::TemplateArgument &arg) { + switch (arg.getKind()) { + case clang::TemplateArgument::Type: + return fromType(arg.getAsType(), false); + case clang::TemplateArgument::Integral: { + Node node = Make(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 Make(Kind::kOpaque, spelling); + } + } +} + +std::shared_ptr Builder::classOf(const clang::Decl *decl) { + if (const auto *record = + llvm::dyn_cast(decl->getDeclContext())) { + Node node = fromRecord(record); + node.type = ctx_.getCanonicalTagType(record); + return Share(std::move(node)); + } + return nullptr; +} + +Node 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) { + Node node = Make(Kind::kDecl, QualifiedName(decl)); + node.class_ = classOf(decl); + return node; + } + + Node node = Make(Kind::kFunction, QualifiedName(func)); + node.variadic = func->isVariadic(); + node.class_ = classOf(func); + node.return_type = Share(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(Make(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; +} + +std::optional 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 Make(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) { + Node node = Make(Kind::kArrow); + node.object = Share(FromType(object)); + node.member = Share(FromDecl(decl)); + return node; + }; + 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 std::nullopt; + } + Node node = + Make(Kind::kUnary, + (uop->isPostfix() ? "post" : "") + + clang::UnaryOperator::getOpcodeStr(uop->getOpcode()).str()); + node.operand = Share(std::move(*sub)); + return node; + } + + return std::nullopt; +} + } // namespace cpp2rust::IrSrc diff --git a/cpp2rust/converter/rules/ir_src.h b/cpp2rust/converter/rules/ir_src.h index c6e52036df..7e697f10d7 100644 --- a/cpp2rust/converter/rules/ir_src.h +++ b/cpp2rust/converter/rules/ir_src.h @@ -3,6 +3,9 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. +#include +#include +#include #include #include @@ -10,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -114,8 +118,33 @@ bool Node::zipChildren(A &a, B &b, Fn fn) { return true; } +Node Make(Node::Kind kind, std::string name = {}); +std::shared_ptr Share(Node node); + llvm::json::Value ToJSON(const Node &node); +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; + + Node FromType(clang::QualType type); + Node FromDecl(const clang::NamedDecl *decl); + std::optional FromExpr(const clang::Expr *expr); + +private: + clang::ASTContext &ctx_; + + Node fromType(clang::QualType type, bool top); + Node fromCanonical(clang::QualType canonical); + Node fromTemplateArg(const clang::TemplateArgument &arg); + Node fromRecord(const clang::RecordDecl *decl); + std::shared_ptr classOf(const clang::Decl *decl); +}; + struct InitTypeLocation { unsigned depth = -1u; unsigned index = -1u; diff --git a/cpp2rust/cpp_rule_preprocessor.cpp b/cpp2rust/cpp_rule_preprocessor.cpp index a346af87e2..5e8e909693 100644 --- a/cpp2rust/cpp_rule_preprocessor.cpp +++ b/cpp2rust/cpp_rule_preprocessor.cpp @@ -228,8 +228,8 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { }); } - Mapper::IrSrcBuilder builder() { - Mapper::IrSrcBuilder b(sema_->Context); + 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()) { @@ -284,8 +284,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { clang::FunctionDecl *rule = nullptr; clang::FunctionDecl *decl = lookupCalledDecl(func->getDescribedFunctionTemplate(), lookup, &rule); - if (Mapper::HasFunctionParameterPack(func) && - Mapper::HasFunctionParameterPack(decl)) { + if (HasFunctionParameterPack(func) && HasFunctionParameterPack(decl)) { return packEntry(func, rule, decl); } return entry(decl); From 071cf50c55508ac01d055d106a2a8d41ef149958 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 10:34:55 +0100 Subject: [PATCH 15/23] Remove normalizeQualType --- cpp2rust/converter/mapper.cpp | 45 +---------------------------------- 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 0163e54c85..5c709166f5 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -332,49 +332,6 @@ void addRulesFromDirectory(const std::filesystem::path &dir, Model model) { } } -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; - } - 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 normalizeTranslationRule(std::string rule) { // Detach pointer from double reference. Useful for matching translation // rules. @@ -629,7 +586,7 @@ std::string ToString(clang::QualType qual_type, ScalarSugar sugar) { std::string type; llvm::raw_string_ostream os(type); - normalizeQualType(qual_type).print(os, getPrintPolicy()); + qual_type.getCanonicalType().getUnqualifiedType().print(os, getPrintPolicy()); return normalizeTranslationRule(std::move(type)); } From 96e9a0ba7d8ce46d574619241c9e1aa1f28afcd4 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 10:42:28 +0100 Subject: [PATCH 16/23] Delete unused mapper code --- cpp2rust/converter/mapper.cpp | 134 ++-------------------------------- cpp2rust/converter/mapper.h | 8 +- 2 files changed, 8 insertions(+), 134 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 5c709166f5..a7dd3e5b85 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -54,8 +53,6 @@ clang::PrintingPolicy getPrintPolicy() { return policy; } -constexpr const char kPackMarker[] = "&&..."; - using Node = IrSrc::Node; using Kind = Node::Kind; using Bindings = std::vector>; @@ -332,13 +329,6 @@ void addRulesFromDirectory(const std::filesystem::path &dir, Model model) { } } -std::string normalizeTranslationRule(std::string rule) { - // Detach pointer from double reference. Useful for matching translation - // rules. - ReplaceAll(rule, "*&&", "* &&"); - return rule; -} - } // namespace PushASTContext::PushASTContext(clang::ASTContext &ctx) : prev_(ctx_) { @@ -531,37 +521,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()); @@ -587,7 +549,7 @@ std::string ToString(clang::QualType qual_type, ScalarSugar sugar) { std::string type; llvm::raw_string_ostream os(type); qual_type.getCanonicalType().getUnqualifiedType().print(os, getPrintPolicy()); - return normalizeTranslationRule(std::move(type)); + return type; } std::string ToString(const clang::NamedDecl *decl) { @@ -624,34 +586,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()); @@ -662,27 +602,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 << ", "; @@ -711,7 +637,7 @@ std::string ToString(const clang::NamedDecl *decl) { } } - return normalizeTranslationRule(std::move(out)); + return out; } std::string ToString(const clang::Expr *expr) { @@ -721,53 +647,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"); @@ -785,14 +673,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"; } diff --git a/cpp2rust/converter/mapper.h b/cpp2rust/converter/mapper.h index 28408d5eeb..6be03215b4 100644 --- a/cpp2rust/converter/mapper.h +++ b/cpp2rust/converter/mapper.h @@ -43,14 +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, -}; - 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); From 2a79982293ba4313138ed1c0bae1f352873e5a66 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 11:05:57 +0100 Subject: [PATCH 17/23] Add nodes for const and volatile --- cpp2rust/converter/mapper.cpp | 66 ++++----------------- cpp2rust/converter/rules/ir_src.cpp | 89 ++++++++++++++++++++++------- cpp2rust/converter/rules/ir_src.h | 4 ++ cpp2rust/cpp_rule_preprocessor.cpp | 5 +- 4 files changed, 85 insertions(+), 79 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index a7dd3e5b85..85a73664c9 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -57,62 +57,19 @@ using Node = IrSrc::Node; using Kind = Node::Kind; using Bindings = std::vector>; -std::string IndexKey(const Node &node) { - switch (node.kind) { - case Kind::kFunction: - case Kind::kDecl: - case Kind::kRecord: - case Kind::kEnum: - case Kind::kTypedef: - case Kind::kBuiltin: - return node.name; - case Kind::kMacro: - return "macro:" + node.name; - case Kind::kUnary: - return "unary" + node.name + ":" + IndexKey(*node.operand); - case Kind::kArrow: - return IndexKey(*node.member); - case Kind::kPointer: - return "*" + IndexKey(*node.pointee); - case Kind::kLRef: - return "&" + IndexKey(*node.pointee); - case Kind::kRRef: - return "&&" + IndexKey(*node.pointee); - case Kind::kArray: - return "[]" + IndexKey(*node.element); - default: - return ""; - } -} - bool Match(const Node &rule, const Node &use, Bindings &bindings) { if (rule.kind == Kind::kParam) { - if ((rule.is_const && !use.is_const) || - (rule.is_volatile && !use.is_volatile)) { - return false; - } - Node bound = use; - if (rule.is_const) { - bound.is_const = false; - bound.type.removeLocalConst(); - } - if (rule.is_volatile) { - bound.is_volatile = false; - bound.type.removeLocalVolatile(); - } if (bindings.size() <= rule.param) { bindings.resize(rule.param + 1); } auto &slot = bindings[rule.param]; if (!slot) { - slot = std::move(bound); + slot = use; return true; } - return *slot == bound; + return *slot == use; } - if (rule.kind != use.kind || rule.name != use.name || - rule.is_const != use.is_const || rule.is_volatile != use.is_volatile || - rule.variadic != use.variadic || rule.ref != use.ref) { + if (!rule.shallowEquals(use)) { return false; } return Node::zipChildren(rule, use, [&](const Node &r, const Node &u) { @@ -122,7 +79,7 @@ bool Match(const Node &rule, const Node &use, Bindings &bindings) { void AddTypeRule(clang::QualType type, IrTgt::TypeRule &&rule) { auto src = IrSrc::Builder(*ctx_).FromType(type); - auto key = IndexKey(src); + auto key = src.indexKey(); auto [begin, end] = types_.equal_range(key); for (auto it = begin; it != end; ++it) { if (it->second.src.ir == src) { @@ -195,7 +152,7 @@ std::pair search(const clang::Expr *expr) { if (!use) { return {}; } - auto res = search(exprs_, *use, IndexKey(*use)); + auto res = search(exprs_, *use, use->indexKey()); log() << "search expr " << use->str() << ", result:\n"; if (res.first) { res.first->src.dump(); @@ -219,9 +176,8 @@ Node typeIR(clang::QualType qual_type, bool sugared) { }; } auto node = builder.FromType(qual_type); - if (node.kind != Kind::kArray) { - node.is_const = false; - node.is_volatile = false; + while (node.kind == Kind::kConst || node.kind == Kind::kVolatile) { + node = Node(*node.operand); } return node; } @@ -229,7 +185,7 @@ Node typeIR(clang::QualType qual_type, bool sugared) { std::pair search(clang::QualType qual_type) { for (bool sugared : {true, false}) { auto use = typeIR(qual_type, sugared); - auto key = IndexKey(use); + auto key = use.indexKey(); auto [rule, bindings] = search(types_, use, key); if (!rule && !key.empty()) { std::tie(rule, bindings) = search(types_, use, ""); @@ -303,12 +259,12 @@ void addRulesFromDirectory(const std::filesystem::path &dir, Model model) { for (auto &[name, rule] : tgt.exprs) { ExprRule paired{takeSrc(src.exprs, name, path), std::move(rule)}; validate(name, paired); - auto key = IndexKey(paired.src.ir); + auto key = paired.src.ir.indexKey(); exprs_.emplace(std::move(key), std::move(paired)); } for (auto &[name, rule] : tgt.types) { TypeRule paired{takeSrc(src.types, name, path), std::move(rule)}; - auto key = IndexKey(paired.src.ir); + auto key = paired.src.ir.indexKey(); auto [begin, end] = types_.equal_range(key); for (auto it = begin; it != end; ++it) { if (it->second.src.ir == paired.src.ir) { @@ -369,7 +325,7 @@ bool IsLibcPassthrough(const clang::Expr *expr) { std::string MapFunctionName(const clang::FunctionDecl *decl) { assert(decl); if (!IsUserDefinedDecl(decl) && - exprs_.contains(IndexKey(IrSrc::Builder(*ctx_).FromDecl(decl)))) { + exprs_.contains(IrSrc::Builder(*ctx_).FromDecl(decl).indexKey())) { return std::format("libcc2rs::{}_{}", decl->getNameAsString(), model_ == Model::kRefCount ? "refcount" : "unsafe"); } diff --git a/cpp2rust/converter/rules/ir_src.cpp b/cpp2rust/converter/rules/ir_src.cpp index 72793aac38..2e56f34bbf 100644 --- a/cpp2rust/converter/rules/ir_src.cpp +++ b/cpp2rust/converter/rules/ir_src.cpp @@ -25,6 +25,7 @@ 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"}, @@ -103,6 +104,22 @@ TypeRule ParseTypeRuleJSON(const llvm::json::Object &obj) { return rule; } +Node qualify(Node node, clang::QualType type) { + if (type.isVolatileQualified()) { + Node wrapper = Make(Kind::kVolatile); + wrapper.type = node.type.withVolatile(); + wrapper.operand = Share(std::move(node)); + node = std::move(wrapper); + } + if (type.isConstQualified()) { + Node wrapper = Make(Kind::kConst); + wrapper.type = node.type.withConst(); + wrapper.operand = Share(std::move(node)); + node = std::move(wrapper); + } + return node; +} + std::string nameOf(const clang::NamedDecl *decl) { if (const auto *ctor = llvm::dyn_cast(decl)) { return ctor->getParent()->getName().str(); @@ -163,13 +180,48 @@ std::string tagName(const clang::TagDecl *tag) { } // namespace bool Node::operator==(const Node &other) const { - return kind == other.kind && name == other.name && param == other.param && - is_const == other.is_const && is_volatile == other.is_volatile && - variadic == other.variadic && ref == other.ref && + return shallowEquals(other) && zipChildren(*this, other, [](const Node &a, const Node &b) { return a == b; }); } +bool Node::shallowEquals(const Node &other) const { + return kind == other.kind && name == other.name && param == other.param && + is_const == other.is_const && is_volatile == other.is_volatile && + variadic == other.variadic && ref == other.ref; +} + +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 + ":" + operand->indexKey(); + case Kind::kArrow: + return member->indexKey(); + case Kind::kConst: + case Kind::kVolatile: + return operand->indexKey(); + case Kind::kPointer: + return "*" + pointee->indexKey(); + case Kind::kLRef: + return "&" + pointee->indexKey(); + case Kind::kRRef: + return "&&" + pointee->indexKey(); + case Kind::kArray: + return "[]" + element->indexKey(); + default: + return ""; + } +} + unsigned Node::specificity() const { unsigned n = kind == Kind::kParam ? 0 : 1; zipChildren(*this, *this, [&](const Node &child, const Node &) { @@ -200,6 +252,9 @@ std::string Node::str() const { if (kind == Kind::kParam) { return out + "T" + std::to_string(param); } + if (kind == Kind::kConst || kind == Kind::kVolatile) { + return out + kindName(kind) + " " + operand->str(); + } out += kindName(kind); if (!name.empty()) { out += " " + name; @@ -310,28 +365,22 @@ Node Builder::fromType(clang::QualType type, bool top) { } if (name) { Node node = Make(Kind::kTypedef, name->getName().str()); - node.is_const = canonical.isConstQualified(); - node.is_volatile = canonical.isVolatileQualified(); - node.type = canonical; - return node; + node.type = canonical.getUnqualifiedType(); + return qualify(std::move(node), canonical); } if (const auto *predef = type->getAs()) { Node node = Make(Kind::kTypedef, predef->getIdentifier()->getName().str()); - node.is_const = canonical.isConstQualified(); - node.is_volatile = canonical.isVolatileQualified(); - node.type = canonical; - return node; + 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())) { Node node = Make(Kind::kPointer); node.pointee = Share(fromType(ptr->getPointeeType(), true)); - node.is_const = canonical.isConstQualified(); - node.is_volatile = canonical.isVolatileQualified(); - node.type = canonical; - return node; + node.type = canonical.getUnqualifiedType(); + return qualify(std::move(node), canonical); } } return fromCanonical(type.getCanonicalType()); @@ -381,12 +430,12 @@ Node Builder::fromCanonical(clang::QualType canonical) { } else { node = Make(Kind::kOpaque, canonical.getUnqualifiedType().getAsString()); } - if (!quals_on_element) { - node.is_const |= canonical.isConstQualified(); - node.is_volatile |= canonical.isVolatileQualified(); + if (quals_on_element) { + node.type = canonical; + return node; } - node.type = canonical; - return node; + node.type = canonical.getUnqualifiedType(); + return qualify(std::move(node), canonical); } Node Builder::fromRecord(const clang::RecordDecl *decl) { diff --git a/cpp2rust/converter/rules/ir_src.h b/cpp2rust/converter/rules/ir_src.h index 7e697f10d7..482280aac4 100644 --- a/cpp2rust/converter/rules/ir_src.h +++ b/cpp2rust/converter/rules/ir_src.h @@ -30,6 +30,8 @@ struct Node { kRecord, kEnum, kTypedef, + kConst, + kVolatile, kPointer, kLRef, kRRef, @@ -66,6 +68,8 @@ struct Node { clang::QualType type; 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; std::string str() const; diff --git a/cpp2rust/cpp_rule_preprocessor.cpp b/cpp2rust/cpp_rule_preprocessor.cpp index 5e8e909693..5f53a369e0 100644 --- a/cpp2rust/cpp_rule_preprocessor.cpp +++ b/cpp2rust/cpp_rule_preprocessor.cpp @@ -216,10 +216,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { ir.param = n; return true; } - if (ir.kind != alternate.kind || ir.name != alternate.name || - ir.param != alternate.param || ir.is_const != alternate.is_const || - ir.is_volatile != alternate.is_volatile || - ir.variadic != alternate.variadic || ir.ref != alternate.ref) { + if (!ir.shallowEquals(alternate)) { return false; } return IrSrc::Node::zipChildren( From 96810e9b63b99b1188e92bd782e5562d965fe187 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 11:07:10 +0100 Subject: [PATCH 18/23] Precompute number of bindings --- cpp2rust/converter/mapper.cpp | 9 ++++----- cpp2rust/converter/rules/ir_src.cpp | 9 +++++++++ cpp2rust/converter/rules/ir_src.h | 2 ++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 85a73664c9..5855d28ef3 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -59,9 +59,7 @@ using Bindings = std::vector>; bool Match(const Node &rule, const Node &use, Bindings &bindings) { if (rule.kind == Kind::kParam) { - if (bindings.size() <= rule.param) { - bindings.resize(rule.param + 1); - } + assert(rule.param < bindings.size()); auto &slot = bindings[rule.param]; if (!slot) { slot = use; @@ -130,8 +128,9 @@ std::pair search(std::unordered_multimap &map, Bindings bindings; unsigned specificity = 0; for (; it != end; ++it) { - Bindings these; - const auto &ir = it->second.src.ir; + const auto &src = it->second.src; + const auto &ir = src.ir; + Bindings these(src.num_params + 1); if (!Match(ir, use, these)) { continue; } diff --git a/cpp2rust/converter/rules/ir_src.cpp b/cpp2rust/converter/rules/ir_src.cpp index 2e56f34bbf..aef3ab1d89 100644 --- a/cpp2rust/converter/rules/ir_src.cpp +++ b/cpp2rust/converter/rules/ir_src.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -86,9 +87,16 @@ Node ParseNodeJSON(const llvm::json::Value &value) { return node; } +unsigned NumParams(const Node &ir) { + unsigned n = 0; + ir.forEachParam([&](unsigned param) { n = std::max(n, param); }); + 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"), @@ -101,6 +109,7 @@ ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { TypeRule ParseTypeRuleJSON(const llvm::json::Object &obj) { TypeRule rule; rule.ir = ParseNodeJSON(*obj.get("ir")); + rule.num_params = NumParams(rule.ir); return rule; } diff --git a/cpp2rust/converter/rules/ir_src.h b/cpp2rust/converter/rules/ir_src.h index 482280aac4..24ef20fc0f 100644 --- a/cpp2rust/converter/rules/ir_src.h +++ b/cpp2rust/converter/rules/ir_src.h @@ -158,6 +158,7 @@ struct InitTypeLocation { struct ExprRule { Node ir; + unsigned num_params = 0; InitTypeLocation init_type; void dump() const; @@ -165,6 +166,7 @@ struct ExprRule { struct TypeRule { Node ir; + unsigned num_params = 0; void dump() const; }; From 3e42dfb452a036f2e510e2b7979b711e47567737 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 11:27:28 +0100 Subject: [PATCH 19/23] Add comments --- cpp2rust/converter/mapper.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 5855d28ef3..798540b36a 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -61,10 +61,12 @@ bool Match(const Node &rule, const Node &use, Bindings &bindings) { if (rule.kind == Kind::kParam) { assert(rule.param < bindings.size()); auto &slot = bindings[rule.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; } if (!rule.shallowEquals(use)) { @@ -81,6 +83,7 @@ void AddTypeRule(clang::QualType type, IrTgt::TypeRule &&rule) { 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; } } From 5c92580fb23bab18db8586b60f257f133e6a5a41 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 11:27:48 +0100 Subject: [PATCH 20/23] Check that all generics used in the tgt are present in the src --- cpp2rust/converter/mapper.cpp | 9 +-------- cpp2rust/converter/rules/ir_src.cpp | 10 ++++++++++ cpp2rust/converter/rules/ir_src.h | 1 + 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 798540b36a..d98fb51192 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -212,15 +212,8 @@ void validate(const std::string &name, const ExprRule &rule) { "Args>"); } - bool has_generic[Ir::kMaxGenerics] = {false}; - src.ir.forEachParam([&](unsigned n) { - if (n >= 1 && n <= Ir::kMaxGenerics) { - has_generic[n - 1] = true; - } - }); - for (size_t i = 0, e = tgt.generics.size(); i < e; ++i) { - if (!has_generic[i]) { + if (!src.ir.hasParam(i + 1)) { llvm::errs() << name << '\n'; tgt.dump(); llvm::errs() << "generic T" << (i + 1) diff --git a/cpp2rust/converter/rules/ir_src.cpp b/cpp2rust/converter/rules/ir_src.cpp index aef3ab1d89..e121469daa 100644 --- a/cpp2rust/converter/rules/ir_src.cpp +++ b/cpp2rust/converter/rules/ir_src.cpp @@ -90,6 +90,7 @@ Node ParseNodeJSON(const llvm::json::Value &value) { 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; } @@ -240,6 +241,15 @@ unsigned Node::specificity() const { return n; } +bool Node::hasParam(unsigned n) const { + if (kind == Kind::kParam) { + return param == n; + } + return !zipChildren(*this, *this, [&](const Node &child, const Node &) { + return !child.hasParam(n); + }); +} + void Node::forEachParam(const std::function &fn) const { if (kind == Kind::kParam) { fn(param); diff --git a/cpp2rust/converter/rules/ir_src.h b/cpp2rust/converter/rules/ir_src.h index 24ef20fc0f..b0224b9596 100644 --- a/cpp2rust/converter/rules/ir_src.h +++ b/cpp2rust/converter/rules/ir_src.h @@ -72,6 +72,7 @@ struct Node { std::string indexKey() const; unsigned specificity() const; void forEachParam(const std::function &fn) const; + bool hasParam(unsigned n) const; std::string str() const; template From a7d9bf1fb7316c576dc0bd534b4c532baca1dddb Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 12:41:18 +0100 Subject: [PATCH 21/23] Use node hierarchy --- cpp2rust/converter/mapper.cpp | 79 ++++-- cpp2rust/converter/rules/ir_src.cpp | 426 +++++++++++++++------------- cpp2rust/converter/rules/ir_src.h | 233 ++++++++++----- cpp2rust/cpp_rule_preprocessor.cpp | 24 +- 4 files changed, 458 insertions(+), 304 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index d98fb51192..1e4dea3114 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -54,16 +54,23 @@ clang::PrintingPolicy getPrintPolicy() { } using Node = IrSrc::Node; +using NodePtr = IrSrc::NodePtr; using Kind = Node::Kind; -using Bindings = std::vector>; -bool Match(const Node &rule, const Node &use, Bindings &bindings) { - if (rule.kind == Kind::kParam) { - assert(rule.param < bindings.size()); - auto &slot = bindings[rule.param]; +struct Binding { + clang::QualType type; + std::optional value; +}; +using Bindings = std::vector>; + +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; + slot = &use; return true; } // Second time we see the binding, check that it equals the first usage. @@ -72,17 +79,17 @@ bool Match(const Node &rule, const Node &use, Bindings &bindings) { if (!rule.shallowEquals(use)) { return false; } - return Node::zipChildren(rule, use, [&](const Node &r, const Node &u) { - return Match(r, u, bindings); + return IrSrc::zipChildren(rule, use, [&](const NodePtr &r, const NodePtr &u) { + return Match(*r, *u, bindings); }); } void AddTypeRule(clang::QualType type, IrTgt::TypeRule &&rule) { auto src = IrSrc::Builder(*ctx_).FromType(type); - auto key = src.indexKey(); + auto key = src->indexKey(); auto [begin, end] = types_.equal_range(key); for (auto it = begin; it != end; ++it) { - if (it->second.src.ir == src) { + if (*it->second.src.ir == *src) { // Skip if the rule already exists return; } @@ -107,8 +114,8 @@ std::string instantiateTgt(const Bindings &bindings, "target uses a template parameter the use site does not bind"); const auto &bound = *bindings[n]; std::string repl; - if (bound.kind == Kind::kValue) { - repl = bound.name; + if (bound.value) { + repl = *bound.value; } else { assert(!bound.type.isNull() && "template parameter bound to a non-type"); repl = Map(bound.type); @@ -128,21 +135,33 @@ std::pair search(std::unordered_multimap &map, const Node &use, const std::string &key) { auto [it, end] = map.equal_range(key); T *rule = nullptr; - Bindings bindings; + std::vector best; unsigned specificity = 0; for (; it != end; ++it) { const auto &src = it->second.src; - const auto &ir = src.ir; - Bindings these(src.num_params + 1); + 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; - bindings = std::move(these); + best = std::move(these); specificity = ir.specificity(); } } + Bindings bindings; + for (const auto *bound : best) { + if (!bound) { + bindings.emplace_back(); + continue; + } + std::optional value; + if (bound->kind == Kind::kValue) { + value = bound->name; + } + bindings.push_back(Binding{bound->type, std::move(value)}); + } return {rule, std::move(bindings)}; } @@ -165,7 +184,7 @@ std::pair search(const clang::Expr *expr) { return res; } -Node typeIR(clang::QualType qual_type, bool sugared) { +NodePtr typeIR(clang::QualType qual_type, bool sugared) { IrSrc::Builder builder(*ctx_); if (sugared) { builder.keep_builtin_typedef = true; @@ -178,8 +197,8 @@ Node typeIR(clang::QualType qual_type, bool sugared) { }; } auto node = builder.FromType(qual_type); - while (node.kind == Kind::kConst || node.kind == Kind::kVolatile) { - node = Node(*node.operand); + while (auto *qual = llvm::dyn_cast(node.get())) { + node = std::move(qual->operand); } return node; } @@ -187,13 +206,13 @@ Node typeIR(clang::QualType qual_type, bool sugared) { std::pair search(clang::QualType qual_type) { for (bool sugared : {true, false}) { auto use = typeIR(qual_type, sugared); - auto key = use.indexKey(); - auto [rule, bindings] = search(types_, use, key); + auto key = use->indexKey(); + auto [rule, bindings] = search(types_, *use, key); if (!rule && !key.empty()) { - std::tie(rule, bindings) = search(types_, use, ""); + std::tie(rule, bindings) = search(types_, *use, ""); } if (rule) { - log() << "search type " << use.str() + log() << "search type " << use->str() << ", result: " << rule->tgt.type_info.type << '\n'; return {&rule->tgt, std::move(bindings)}; } @@ -213,11 +232,11 @@ void validate(const std::string &name, const ExprRule &rule) { } for (size_t i = 0, e = tgt.generics.size(); i < e; ++i) { - if (!src.ir.hasParam(i + 1)) { + 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() + << " declared but missing from src: " << src.ir->str() << '\n'; llvm::report_fatal_error("Absent generic from src"); } @@ -254,17 +273,17 @@ void addRulesFromDirectory(const std::filesystem::path &dir, Model model) { 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(); + auto key = paired.src.ir->indexKey(); exprs_.emplace(std::move(key), std::move(paired)); } for (auto &[name, rule] : tgt.types) { TypeRule paired{takeSrc(src.types, name, path), std::move(rule)}; - auto key = paired.src.ir.indexKey(); + auto key = paired.src.ir->indexKey(); auto [begin, end] = types_.equal_range(key); for (auto it = begin; it != end; ++it) { - if (it->second.src.ir == paired.src.ir) { + if (*it->second.src.ir == *paired.src.ir) { llvm::errs() << "ERROR: duplicate type rule for C++ type '" - << paired.src.ir.str() << "': maps to both '" + << paired.src.ir->str() << "': maps to both '" << it->second.tgt.type_info.type << "' and '" << paired.tgt.type_info.type << "'\n"; std::exit(EXIT_FAILURE); @@ -320,7 +339,7 @@ bool IsLibcPassthrough(const clang::Expr *expr) { std::string MapFunctionName(const clang::FunctionDecl *decl) { assert(decl); if (!IsUserDefinedDecl(decl) && - exprs_.contains(IrSrc::Builder(*ctx_).FromDecl(decl).indexKey())) { + exprs_.contains(IrSrc::Builder(*ctx_).FromDecl(decl)->indexKey())) { return std::format("libcc2rs::{}_{}", decl->getNameAsString(), model_ == Model::kRefCount ? "refcount" : "unsafe"); } diff --git a/cpp2rust/converter/rules/ir_src.cpp b/cpp2rust/converter/rules/ir_src.cpp index e121469daa..9f3d69c1c6 100644 --- a/cpp2rust/converter/rules/ir_src.cpp +++ b/cpp2rust/converter/rules/ir_src.cpp @@ -21,6 +21,8 @@ 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"}, @@ -55,35 +57,66 @@ Kind kindFromName(llvm::StringRef name) { std::exit(EXIT_FAILURE); } -Node ParseNodeJSON(const llvm::json::Value &value) { +NodePtr ParseNodeJSON(const llvm::json::Value &value) { const auto *obj = value.getAsObject(); assert(obj && "IR node must be an object"); - Node node; - node.kind = kindFromName(*obj->getString("kind")); - if (auto name = obj->getString("name")) { - node.name = name->str(); - } - if (auto param = obj->getInteger("param")) { - node.param = *param; - } - node.is_const = obj->getBoolean("is_const").value_or(false); - node.is_volatile = obj->getBoolean("is_volatile").value_or(false); - node.variadic = obj->getBoolean("variadic").value_or(false); - if (auto ref = obj->getString("ref")) { - node.ref = ref->str(); - } - for (const auto &[key, field] : kNodeFields) { - if (const auto *child = obj->get(key)) { - node.*field = Share(ParseNodeJSON(*child)); - } + 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; } - for (const auto &[key, field] : kNodeLists) { - if (const auto *list = obj->getArray(key)) { - for (const auto &child : *list) { - (node.*field).push_back(ParseNodeJSON(child)); + 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; } @@ -97,7 +130,7 @@ unsigned NumParams(const Node &ir) { ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { ExprRule rule; rule.ir = ParseNodeJSON(*obj.get("ir")); - rule.num_params = NumParams(rule.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"), @@ -110,22 +143,20 @@ ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { TypeRule ParseTypeRuleJSON(const llvm::json::Object &obj) { TypeRule rule; rule.ir = ParseNodeJSON(*obj.get("ir")); - rule.num_params = NumParams(rule.ir); + rule.num_params = NumParams(*rule.ir); return rule; } -Node qualify(Node node, clang::QualType type) { +NodePtr qualify(NodePtr node, clang::QualType type) { if (type.isVolatileQualified()) { - Node wrapper = Make(Kind::kVolatile); - wrapper.type = node.type.withVolatile(); - wrapper.operand = Share(std::move(node)); - node = std::move(wrapper); + auto qualified = node->type.withVolatile(); + node = std::make_unique(Kind::kVolatile, std::move(node)); + node->type = qualified; } if (type.isConstQualified()) { - Node wrapper = Make(Kind::kConst); - wrapper.type = node.type.withConst(); - wrapper.operand = Share(std::move(node)); - node = std::move(wrapper); + auto qualified = node->type.withConst(); + node = std::make_unique(Kind::kConst, std::move(node)); + node->type = qualified; } return node; } @@ -191,14 +222,25 @@ std::string tagName(const clang::TagDecl *tag) { bool Node::operator==(const Node &other) const { return shallowEquals(other) && - zipChildren(*this, other, - [](const Node &a, const Node &b) { return a == b; }); + zipChildren(*this, other, [](const NodePtr &a, const NodePtr &b) { + return *a == *b; + }); } bool Node::shallowEquals(const Node &other) const { - return kind == other.kind && name == other.name && param == other.param && - is_const == other.is_const && is_volatile == other.is_volatile && - variadic == other.variadic && ref == other.ref; + 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 { @@ -213,20 +255,20 @@ std::string Node::indexKey() const { case Kind::kMacro: return "macro:" + name; case Kind::kUnary: - return "unary" + name + ":" + operand->indexKey(); + return "unary" + name + ":" + cast(this)->operand->indexKey(); case Kind::kArrow: - return member->indexKey(); + return cast(this)->member->indexKey(); case Kind::kConst: case Kind::kVolatile: - return operand->indexKey(); + return cast(this)->operand->indexKey(); case Kind::kPointer: - return "*" + pointee->indexKey(); + return "*" + cast(this)->pointee->indexKey(); case Kind::kLRef: - return "&" + pointee->indexKey(); + return "&" + cast(this)->pointee->indexKey(); case Kind::kRRef: - return "&&" + pointee->indexKey(); + return "&&" + cast(this)->pointee->indexKey(); case Kind::kArray: - return "[]" + element->indexKey(); + return "[]" + cast(this)->element->indexKey(); default: return ""; } @@ -234,65 +276,68 @@ std::string Node::indexKey() const { unsigned Node::specificity() const { unsigned n = kind == Kind::kParam ? 0 : 1; - zipChildren(*this, *this, [&](const Node &child, const Node &) { - n += child.specificity(); + zipChildren(*this, *this, [&](const NodePtr &child, const NodePtr &) { + n += child->specificity(); return true; }); return n; } bool Node::hasParam(unsigned n) const { - if (kind == Kind::kParam) { - return param == n; + if (const auto *param = dyn_cast(this)) { + return param->param == n; } - return !zipChildren(*this, *this, [&](const Node &child, const Node &) { - return !child.hasParam(n); + return !zipChildren(*this, *this, [&](const NodePtr &child, const NodePtr &) { + return !child->hasParam(n); }); } void Node::forEachParam(const std::function &fn) const { - if (kind == Kind::kParam) { - fn(param); + if (const auto *param = dyn_cast(this)) { + fn(param->param); } - zipChildren(*this, *this, [&](const Node &child, const Node &) { - child.forEachParam(fn); + zipChildren(*this, *this, [&](const NodePtr &child, const NodePtr &) { + child->forEachParam(fn); return true; }); } std::string Node::str() const { - std::string out; - if (is_const) { - out += "const "; - } - if (is_volatile) { - out += "volatile "; + if (const auto *param = dyn_cast(this)) { + return "T" + std::to_string(param->param); } - if (kind == Kind::kParam) { - return out + "T" + std::to_string(param); + if (const auto *qual = dyn_cast(this)) { + return std::string(kindName(kind)) + " " + qual->operand->str(); } - if (kind == Kind::kConst || kind == Kind::kVolatile) { - return out + kindName(kind) + " " + 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; - for (const auto &[key, field] : kNodeFields) { - if (this->*field) { - fields.push_back(std::string(key) + ": " + (this->*field)->str()); - } - } - for (const auto &[key, field] : kNodeLists) { - if (!(this->*field).empty()) { - std::string list; - for (const auto &child : this->*field) { - list += (list.empty() ? "" : ", ") + child.str(); - } - fields.push_back(std::string(key) + ": [" + list + "]"); - } - } + 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) { @@ -303,63 +348,55 @@ std::string Node::str() const { return out; } -Node Make(Kind kind, std::string name) { - Node node; - node.kind = kind; - node.name = std::move(name); - return node; -} - -std::shared_ptr Share(Node node) { - return std::make_shared(std::move(node)); -} - llvm::json::Value ToJSON(const Node &node) { llvm::json::Object obj{{"kind", kindName(node.kind)}}; if (!node.name.empty()) { obj["name"] = node.name; } - if (node.kind == Kind::kParam) { - obj["param"] = node.param; + if (const auto *param = dyn_cast(&node)) { + obj["param"] = param->param; } - if (node.is_const) { - obj["is_const"] = true; - } - if (node.is_volatile) { - obj["is_volatile"] = true; - } - if (node.variadic) { - obj["variadic"] = true; - } - if (!node.ref.empty()) { - obj["ref"] = node.ref; - } - for (const auto &[key, field] : kNodeFields) { - if (node.*field) { - obj[key] = ToJSON(*(node.*field)); + if (const auto *fn = dyn_cast(&node)) { + if (fn->is_const) { + obj["is_const"] = true; } - } - for (const auto &[key, field] : kNodeLists) { - if (!(node.*field).empty()) { - llvm::json::Array list; - for (const auto &child : node.*field) { - list.push_back(ToJSON(child)); - } - obj[key] = std::move(list); + 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'; + 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'; } +void TypeRule::dump() const { log() << "name: " << ir->str() << '\n'; } Rules Load(const std::filesystem::path &dir) { Rules rules; @@ -368,9 +405,9 @@ Rules Load(const std::filesystem::path &dir) { return rules; } -Node Builder::FromType(clang::QualType type) { return fromType(type, true); } +NodePtr Builder::FromType(clang::QualType type) { return fromType(type, true); } -Node Builder::fromType(clang::QualType type, bool top) { +NodePtr Builder::fromType(clang::QualType type, bool top) { if (keep_builtin_typedef && top) { if (const auto *decltype_type = llvm::dyn_cast(type.getTypePtr())) { @@ -383,110 +420,113 @@ Node Builder::fromType(clang::QualType type, bool top) { name = typedef_type->getDecl(); } if (name) { - Node node = Make(Kind::kTypedef, name->getName().str()); - node.type = canonical.getUnqualifiedType(); + 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()) { - Node node = - Make(Kind::kTypedef, predef->getIdentifier()->getName().str()); - node.type = canonical.getUnqualifiedType(); + 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())) { - Node node = Make(Kind::kPointer); - node.pointee = Share(fromType(ptr->getPointeeType(), true)); - node.type = canonical.getUnqualifiedType(); + 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()); } -Node Builder::fromCanonical(clang::QualType canonical) { - Node node; +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 = Make(Kind::kBuiltin, builtin->getName(policy).str()); + node = + std::make_unique(Kind::kBuiltin, builtin->getName(policy).str()); } else if (const auto *ptr = llvm::dyn_cast(type)) { - node = Make(Kind::kPointer); - node.pointee = Share(fromType(ptr->getPointeeType(), false)); + node = std::make_unique( + Kind::kPointer, fromType(ptr->getPointeeType(), false)); } else if (const auto *ref = llvm::dyn_cast(type)) { - node = Make(Kind::kLRef); - node.pointee = Share(fromType(ref->getPointeeType(), false)); + node = std::make_unique( + Kind::kLRef, fromType(ref->getPointeeType(), false)); } else if (const auto *ref = llvm::dyn_cast(type)) { - node = Make(Kind::kRRef); - node.pointee = Share(fromType(ref->getPointeeType(), false)); + node = std::make_unique( + Kind::kRRef, fromType(ref->getPointeeType(), false)); } else if (const auto *array = ctx_.getAsConstantArrayType(canonical)) { - node = Make(Kind::kArray); - node.element = Share(fromType(array->getElementType(), false)); - node.size = - Share(Make(Kind::kValue, llvm::toString(array->getSize(), 10, false))); + 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 = Make(Kind::kArray); - node.element = Share(fromType(array->getElementType(), false)); + 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 = Make(Kind::kEnum, tagName(enum_type->getDecl())); + node = std::make_unique(Kind::kEnum, tagName(enum_type->getDecl())); } else if (const auto *proto = llvm::dyn_cast(type)) { - node = Make(Kind::kFunctionType); - node.variadic = proto->isVariadic(); - node.return_type = Share(fromType(proto->getReturnType(), false)); + auto fn = std::make_unique(Kind::kFunctionType); + fn->variadic = proto->isVariadic(); + fn->return_type = fromType(proto->getReturnType(), false); for (auto param : proto->getParamTypes()) { - node.params.push_back(fromType(param, false)); + fn->params.push_back(fromType(param, false)); } + node = std::move(fn); } else { - node = Make(Kind::kOpaque, canonical.getUnqualifiedType().getAsString()); + node = std::make_unique(Kind::kOpaque, + canonical.getUnqualifiedType().getAsString()); } if (quals_on_element) { - node.type = canonical; + node->type = canonical; return node; } - node.type = canonical.getUnqualifiedType(); + node->type = canonical.getUnqualifiedType(); return qualify(std::move(node), canonical); } -Node Builder::fromRecord(const clang::RecordDecl *decl) { +NodePtr Builder::fromRecord(const clang::RecordDecl *decl) { if (stand_in) { if (auto param = stand_in(decl)) { - Node node = Make(Kind::kParam); - node.param = *param; - return node; + return std::make_unique(*param); } } if (const auto *cxx = llvm::dyn_cast(decl); cxx && cxx->isLambda()) { - return Make(Kind::kOpaque, "lambda"); + return std::make_unique(Kind::kOpaque, "lambda"); } - Node node = Make(Kind::kRecord, tagName(decl)); - node.class_ = classOf(decl); + 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)); + node->args.push_back(fromTemplateArg(arg)); } } return node; } -Node Builder::fromTemplateArg(const clang::TemplateArgument &arg) { +NodePtr Builder::fromTemplateArg(const clang::TemplateArgument &arg) { switch (arg.getKind()) { case clang::TemplateArgument::Type: return fromType(arg.getAsType(), false); case clang::TemplateArgument::Integral: { - Node node = Make(Kind::kValue, llvm::toString(arg.getAsIntegral(), 10)); - node.type = arg.getIntegralType(); + auto node = std::make_unique(Kind::kValue, + llvm::toString(arg.getAsIntegral(), 10)); + node->type = arg.getIntegralType(); return node; } default: { @@ -494,36 +534,37 @@ Node Builder::fromTemplateArg(const clang::TemplateArgument &arg) { llvm::raw_string_ostream os(spelling); arg.print(clang::PrintingPolicy(ctx_.getLangOpts()), os, /*IncludeType=*/true); - return Make(Kind::kOpaque, spelling); + return std::make_unique(Kind::kOpaque, spelling); } } } -std::shared_ptr Builder::classOf(const clang::Decl *decl) { +NodePtr Builder::classOf(const clang::Decl *decl) { if (const auto *record = llvm::dyn_cast(decl->getDeclContext())) { - Node node = fromRecord(record); - node.type = ctx_.getCanonicalTagType(record); - return Share(std::move(node)); + auto node = fromRecord(record); + node->type = ctx_.getCanonicalTagType(record); + return node; } return nullptr; } -Node Builder::FromDecl(const clang::NamedDecl *decl) { +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) { - Node node = Make(Kind::kDecl, QualifiedName(decl)); - node.class_ = classOf(decl); + auto node = std::make_unique(QualifiedName(decl)); + node->class_ = classOf(decl); return node; } - Node node = Make(Kind::kFunction, QualifiedName(func)); - node.variadic = func->isVariadic(); - node.class_ = classOf(func); - node.return_type = Share(fromType(func->getReturnType(), false)); + 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) { @@ -532,20 +573,20 @@ Node Builder::FromDecl(const clang::NamedDecl *decl) { (primary ? primary->getTemplatedDecl() : func)->getNumParams() - 1; } for (unsigned i = 0; i < num_params; ++i) { - node.params.push_back(fromType(func->getParamDecl(i)->getType(), false)); + node->params.push_back(fromType(func->getParamDecl(i)->getType(), false)); } if (has_pack) { - node.params.push_back(Make(Kind::kPackParams)); + 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(); + node->is_const = method->isConst(); + node->is_volatile = method->isVolatile(); switch (method->getRefQualifier()) { case clang::RQ_LValue: - node.ref = "&"; + node->ref = "&"; break; case clang::RQ_RValue: - node.ref = "&&"; + node->ref = "&&"; break; default: break; @@ -554,7 +595,7 @@ Node Builder::FromDecl(const clang::NamedDecl *decl) { return node; } -std::optional Builder::FromExpr(const clang::Expr *expr) { +NodePtr Builder::FromExpr(const clang::Expr *expr) { expr = expr->IgnoreParenImpCasts(); if (llvm::isa(expr) && @@ -562,7 +603,7 @@ std::optional Builder::FromExpr(const clang::Expr *expr) { auto name = clang::Lexer::getImmediateMacroName( expr->getBeginLoc(), ctx_.getSourceManager(), ctx_.getLangOpts()); if (!name.empty()) { - return Make(Kind::kMacro, name.str()); + return std::make_unique(Kind::kMacro, name.str()); } } @@ -584,10 +625,7 @@ std::optional Builder::FromExpr(const clang::Expr *expr) { return FromDecl(decl); } auto arrow = [&](clang::QualType object) { - Node node = Make(Kind::kArrow); - node.object = Share(FromType(object)); - node.member = Share(FromDecl(decl)); - return node; + return std::make_unique(FromType(object), FromDecl(decl)); }; if (member->isArrow()) { const auto *base = member->getBase()->IgnoreParenImpCasts(); @@ -616,17 +654,15 @@ std::optional Builder::FromExpr(const clang::Expr *expr) { if (const auto *uop = llvm::dyn_cast(expr)) { auto sub = FromExpr(uop->getSubExpr()); if (!sub) { - return std::nullopt; + return nullptr; } - Node node = - Make(Kind::kUnary, - (uop->isPostfix() ? "post" : "") + - clang::UnaryOperator::getOpcodeStr(uop->getOpcode()).str()); - node.operand = Share(std::move(*sub)); - return node; + return std::make_unique( + (uop->isPostfix() ? "post" : "") + + clang::UnaryOperator::getOpcodeStr(uop->getOpcode()).str(), + std::move(sub)); } - return std::nullopt; + return nullptr; } } // namespace cpp2rust::IrSrc diff --git a/cpp2rust/converter/rules/ir_src.h b/cpp2rust/converter/rules/ir_src.h index b0224b9596..8174dde121 100644 --- a/cpp2rust/converter/rules/ir_src.h +++ b/cpp2rust/converter/rules/ir_src.h @@ -7,14 +7,17 @@ #include #include #include +#include #include +#include #include #include #include #include #include #include +#include #include #include @@ -22,6 +25,9 @@ namespace cpp2rust::IrSrc { +struct Node; +using NodePtr = std::unique_ptr; + struct Node { enum class Kind { kParam, @@ -46,27 +52,16 @@ struct Node { kPackParams, }; - Kind kind = Kind::kOpaque; + const Kind kind; std::string name; - unsigned param = 0; - bool is_const = false; - bool is_volatile = false; - bool variadic = false; - std::string ref; - - std::shared_ptr class_; - std::shared_ptr return_type; - std::vector params; - std::vector args; - std::shared_ptr pointee; - std::shared_ptr element; - std::shared_ptr size; - std::shared_ptr operand; - std::shared_ptr object; - std::shared_ptr member; - 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; @@ -74,58 +69,164 @@ struct Node { 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_; - template - static bool zipChildren(A &a, B &b, Fn fn); + explicit DeclNode(std::string name) : Node(Kind::kDecl, std::move(name)) {} + static bool classof(const Node *node) { return node->kind == Kind::kDecl; } }; -inline constexpr std::pair Node::*> - kNodeFields[] = { - {"class", &Node::class_}, {"return_type", &Node::return_type}, - {"pointee", &Node::pointee}, {"element", &Node::element}, - {"size", &Node::size}, {"operand", &Node::operand}, - {"object", &Node::object}, {"member", &Node::member}, +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; } }; -inline constexpr std::pair Node::*> - kNodeLists[] = { - {"params", &Node::params}, - {"args", &Node::args}, +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 Node::zipChildren(A &a, B &b, Fn fn) { - for (const auto &entry : kNodeFields) { - auto &x = a.*entry.second; - auto &y = b.*entry.second; - if (!x || !y) { - if (x || y) { - return false; - } - continue; - } - if (!fn(*x, *y)) { - return false; - } +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; } - for (const auto &entry : kNodeLists) { - auto &x = a.*entry.second; - auto &y = b.*entry.second; - if (x.size() != y.size()) { - return false; - } - for (std::size_t i = 0; i < x.size(); ++i) { - if (!fn(x[i], y[i])) { +} + +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); } - } - return true; + }); } -Node Make(Node::Kind kind, std::string name = {}); -std::shared_ptr Share(Node node); - llvm::json::Value ToJSON(const Node &node); class Builder { @@ -136,18 +237,18 @@ class Builder { bool keep_builtin_typedef = false; std::function keep_pointee_sugar; - Node FromType(clang::QualType type); - Node FromDecl(const clang::NamedDecl *decl); - std::optional FromExpr(const clang::Expr *expr); + NodePtr FromType(clang::QualType type); + NodePtr FromDecl(const clang::NamedDecl *decl); + NodePtr FromExpr(const clang::Expr *expr); private: clang::ASTContext &ctx_; - Node fromType(clang::QualType type, bool top); - Node fromCanonical(clang::QualType canonical); - Node fromTemplateArg(const clang::TemplateArgument &arg); - Node fromRecord(const clang::RecordDecl *decl); - std::shared_ptr classOf(const clang::Decl *decl); + 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); }; struct InitTypeLocation { @@ -158,7 +259,7 @@ struct InitTypeLocation { }; struct ExprRule { - Node ir; + NodePtr ir; unsigned num_params = 0; InitTypeLocation init_type; @@ -166,7 +267,7 @@ struct ExprRule { }; struct TypeRule { - Node ir; + NodePtr ir; unsigned num_params = 0; void dump() const; diff --git a/cpp2rust/cpp_rule_preprocessor.cpp b/cpp2rust/cpp_rule_preprocessor.cpp index 5f53a369e0..6251c01a9a 100644 --- a/cpp2rust/cpp_rule_preprocessor.cpp +++ b/cpp2rust/cpp_rule_preprocessor.cpp @@ -165,7 +165,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { } } - llvm::json::Object obj{{"ir", IrSrc::ToJSON(entry->ir)}}; + 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}, @@ -182,7 +182,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { std::string nttp_alternate_; struct Entry { - IrSrc::Node ir; + IrSrc::NodePtr ir; std::optional> init_type; }; @@ -202,25 +202,23 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { return std::to_string(nttpAPInt(type, alternate).getZExtValue()); } - static bool markNTTP(IrSrc::Node &ir, const IrSrc::Node &alternate, + 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) { + if (ir->kind == Kind::kValue && alternate->kind == Kind::kValue && + ir->name != alternate->name) { + if (ir->name != value || alternate->name != alternate_value) { return false; } - ir.kind = Kind::kParam; - ir.name.clear(); - ir.param = n; + ir = std::make_unique(n); return true; } - if (!ir.shallowEquals(alternate)) { + if (!ir->shallowEquals(*alternate)) { return false; } - return IrSrc::Node::zipChildren( - ir, alternate, [&](IrSrc::Node &a, const IrSrc::Node &b) { + return IrSrc::zipChildren( + *ir, *alternate, [&](IrSrc::NodePtr &a, const IrSrc::NodePtr &b) { return markNTTP(a, b, n, value, alternate_value); }); } @@ -245,7 +243,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { expr->dump(); std::exit(EXIT_FAILURE); } - return {std::move(*ir), std::nullopt}; + return {std::move(ir), std::nullopt}; } Entry entry(const clang::NamedDecl *decl) { From 4652c51b074153a86e1835a1bca0fe127a9e4f9c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 13:01:13 +0100 Subject: [PATCH 22/23] Add cache for Mapper::search --- cpp2rust/converter/mapper.cpp | 62 +++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 1e4dea3114..21f1df726c 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -63,6 +63,15 @@ struct Binding { }; using Bindings = std::vector>; +std::unordered_map> + expr_cache_; +std::unordered_map> type_cache_; + +void clearCaches() { + expr_cache_.clear(); + type_cache_.clear(); +} + bool Match(const Node &rule, const Node &use, std::vector &bindings) { if (const auto *param = llvm::dyn_cast(&rule)) { @@ -95,6 +104,7 @@ void AddTypeRule(clang::QualType type, IrTgt::TypeRule &&rule) { } } types_.emplace(std::move(key), TypeRule{{std::move(src)}, std::move(rule)}); + type_cache_.clear(); } std::string instantiateTgt(const Bindings &bindings, @@ -166,21 +176,23 @@ std::pair search(std::unordered_multimap &map, } std::pair search(const clang::Expr *expr) { - if (RefersToUserDefinedDecl(expr)) { - return {}; - } - auto use = IrSrc::Builder(*ctx_).FromExpr(expr); - if (!use) { - return {}; - } - auto 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"; + if (auto it = expr_cache_.find(expr); it != expr_cache_.end()) { + return it->second; + } + 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"; + } + } } + expr_cache_.emplace(expr, res); return res; } @@ -204,6 +216,11 @@ NodePtr typeIR(clang::QualType qual_type, bool sugared) { } 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; + } + std::pair res; for (bool sugared : {true, false}) { auto use = typeIR(qual_type, sugared); auto key = use->indexKey(); @@ -214,11 +231,15 @@ std::pair search(clang::QualType qual_type) { if (rule) { log() << "search type " << use->str() << ", result: " << rule->tgt.type_info.type << '\n'; - return {&rule->tgt, std::move(bindings)}; + res = {&rule->tgt, std::move(bindings)}; + break; } } - log() << "search type " << ToString(qual_type) << ", result: None\n"; - return {}; + 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) { @@ -303,8 +324,12 @@ void addRulesFromDirectory(const std::filesystem::path &dir, Model model) { 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; @@ -650,6 +675,7 @@ void LoadTranslationRules(Model model, clang::ASTContext &ctx, const std::string &rules_dir) { ctx_ = &ctx; model_ = model; + clearCaches(); if (translation_rules_loaded_) { return; From 3073510637ff966c3dbf54df5ef49b55ede1c2c6 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 29 Sep 2026 13:05:16 +0100 Subject: [PATCH 23/23] Move IrSrc::Builder in a different file --- cpp2rust/converter/mapper.cpp | 1 + cpp2rust/converter/rules/ir_src.cpp | 338 ------------------- cpp2rust/converter/rules/ir_src.h | 26 -- cpp2rust/converter/rules/ir_src_builder.cpp | 355 ++++++++++++++++++++ cpp2rust/converter/rules/ir_src_builder.h | 40 +++ cpp2rust/cpp_rule_preprocessor.cpp | 1 + 6 files changed, 397 insertions(+), 364 deletions(-) create mode 100644 cpp2rust/converter/rules/ir_src_builder.cpp create mode 100644 cpp2rust/converter/rules/ir_src_builder.h diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 21f1df726c..551d8cdf9f 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -18,6 +18,7 @@ #include "converter/converter_lib.h" #include "converter/rules/ir_src.h" +#include "converter/rules/ir_src_builder.h" #include "converter/rules/ir_tgt.h" namespace cpp2rust::Mapper { diff --git a/cpp2rust/converter/rules/ir_src.cpp b/cpp2rust/converter/rules/ir_src.cpp index 9f3d69c1c6..ec6d84faf7 100644 --- a/cpp2rust/converter/rules/ir_src.cpp +++ b/cpp2rust/converter/rules/ir_src.cpp @@ -3,17 +3,10 @@ #include "converter/rules/ir_src.h" -#include -#include -#include -#include -#include - #include #include #include -#include "converter/converter_lib.h" #include "logging.h" namespace cpp2rust::IrSrc { @@ -147,77 +140,6 @@ TypeRule ParseTypeRuleJSON(const llvm::json::Object &obj) { return rule; } -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 bool Node::operator==(const Node &other) const { @@ -405,264 +327,4 @@ Rules Load(const std::filesystem::path &dir) { return rules; } -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.h b/cpp2rust/converter/rules/ir_src.h index 8174dde121..3924514c23 100644 --- a/cpp2rust/converter/rules/ir_src.h +++ b/cpp2rust/converter/rules/ir_src.h @@ -3,9 +3,6 @@ // 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 @@ -15,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -229,28 +225,6 @@ bool zipChildren(A &a, B &b, Fn fn) { llvm::json::Value ToJSON(const Node &node); -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); -}; - struct InitTypeLocation { unsigned depth = -1u; unsigned index = -1u; 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/cpp_rule_preprocessor.cpp b/cpp2rust/cpp_rule_preprocessor.cpp index 6251c01a9a..e27fe390b6 100644 --- a/cpp2rust/cpp_rule_preprocessor.cpp +++ b/cpp2rust/cpp_rule_preprocessor.cpp @@ -35,6 +35,7 @@ #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;