Skip to content

Commit 173bbee

Browse files
authored
Convert late instantiated methods on Ptr<Self> instead of self (#334)
After #333, late instantiated methods from templates were not added in the `impl X for Ptr<Self>` block. For exmaple: ```cpp // 2 methods, each instantiated in separate TUs. Second TU got it wrong template <typename T> struct S { T x = 0; // visited in first TU: correctly added in `trait SImpl` and `impl SImpl for Ptr<Self>` void set(T v) { x = v; } // visited in the second TU: not added in `trait SImpl` at all and incorrectly added // in `impl S` instead of `impl SImpl for Ptr<Self>` T get() { return x; } }; ``` This PR solves it by also deferring trait blocks at the end of the file and adding late instantiated methods in both `trait SImpl` and `impl SImpl for Ptr<Self>`.
1 parent 2e429e7 commit 173bbee

33 files changed

Lines changed: 397 additions & 236 deletions

‎cpp2rust/converter/converter.cpp‎

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ std::unordered_set<std::string> Converter::decl_ids_;
2626
std::unordered_set<std::string> Converter::globals_;
2727
std::unordered_set<std::string> Converter::abstract_structs_;
2828
Converter::RecordIndex Converter::record_decls_;
29-
std::map<Converter::DeferredImplHeader, Converter::DeferredImplBody>
30-
Converter::deferred_impls_;
29+
std::map<std::string, Converter::MethodsOnPtr> Converter::methods_on_ptr_;
3130

3231
void Converter::ConvertUniquePtrDeref(clang::CXXOperatorCallExpr *expr) {
3332
bool is_star = expr->getOperator() == clang::OverloadedOperatorKind::OO_Star;
@@ -58,12 +57,16 @@ use std::rc::Rc;
5857
)");
5958
}
6059

61-
std::string Converter::EmitDeferredImpls() {
60+
std::string Converter::EmitMethodsOnPtr() {
6261
std::string out;
63-
for (const auto &[header, body] : deferred_impls_) {
64-
out += header;
62+
for (const auto &[name, methods] : methods_on_ptr_) {
63+
out += methods.trait_header;
6564
out += " {\n";
66-
out += body;
65+
out += methods.trait_body;
66+
out += "}\n";
67+
out += methods.impl_header;
68+
out += " {\n";
69+
out += methods.impl_body;
6770
out += "}\n";
6871
}
6972
return out;
@@ -855,6 +858,15 @@ void Converter::EmitRustStructOrUnion(clang::RecordDecl *decl) {
855858
AddByteReprTrait(decl);
856859
}
857860

861+
void Converter::ConvertLateInstantiatedMethods(clang::CXXRecordDecl *decl) {
862+
ConvertCXXMethodDecls(
863+
decl, std::format("{} {}", keyword::kImpl, GetRecordName(decl)),
864+
[](auto *method) {
865+
return IsEmittableMethod(method) && method->hasBody() &&
866+
!decl_ids_.contains(GetMethodID(method));
867+
});
868+
}
869+
858870
void Converter::ConvertCXXRecordMethods(clang::CXXRecordDecl *decl) {
859871
ConvertCXXMethodDecls(
860872
decl, std::format("{} {}", keyword::kImpl, GetRecordName(decl)),
@@ -941,12 +953,7 @@ bool Converter::VisitCXXRecordDecl(clang::CXXRecordDecl *decl) {
941953
if (!record_decls_.MarkDefined(GetRecordName(decl))) {
942954
// Other translation units may instantiate members this one did not.
943955
if (clang::isa<clang::ClassTemplateSpecializationDecl>(decl)) {
944-
ConvertCXXMethodDecls(
945-
decl, std::format("{} {}", keyword::kImpl, GetRecordName(decl)),
946-
[](auto *method) {
947-
return IsEmittableMethod(method) && method->hasBody() &&
948-
!decl_ids_.contains(GetMethodID(method));
949-
});
956+
ConvertLateInstantiatedMethods(decl);
950957
}
951958
return false;
952959
}

‎cpp2rust/converter/converter.h‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
5454

5555
static std::string EmitOpaqueRecords();
5656

57-
static std::string EmitDeferredImpls();
57+
static std::string EmitMethodsOnPtr();
5858

5959
virtual bool VisitBuiltinType(clang::BuiltinType *type);
6060

@@ -133,6 +133,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
133133
std::string GetMethodName(const clang::CXXMethodDecl *decl);
134134
virtual std::string GetSelfMaybeWithMut(const clang::CXXMethodDecl *decl);
135135
virtual void ConvertCXXRecordMethods(clang::CXXRecordDecl *decl);
136+
virtual void ConvertLateInstantiatedMethods(clang::CXXRecordDecl *decl);
136137
virtual std::string DestroyMembers(const clang::CXXRecordDecl *decl);
137138
virtual void EmitScopedDestructor(const clang::VarDecl *decl);
138139
void EmitDeallocation(clang::CXXDeleteExpr *expr,
@@ -846,9 +847,15 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
846847
std::unordered_map<std::string, bool> entries_;
847848
};
848849
static RecordIndex record_decls_;
849-
using DeferredImplHeader = std::string;
850-
using DeferredImplBody = std::string;
851-
static std::map<DeferredImplHeader, DeferredImplBody> deferred_impls_;
850+
struct MethodsOnPtr {
851+
std::string trait_header;
852+
std::string trait_body;
853+
std::string impl_header;
854+
std::string impl_body;
855+
};
856+
// record name -> trait and impl for Ptr<record>, emitted after all
857+
// translation units.
858+
static std::map<std::string, MethodsOnPtr> methods_on_ptr_;
852859

853860
enum class ExprKind : uint8_t {
854861
Callee,

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,10 +2604,16 @@ ConverterRefCount::TraitName(const clang::CXXRecordDecl *decl) const {
26042604
return GetRecordName(decl) + "Impl";
26052605
}
26062606

2607-
std::string
2608-
ConverterRefCount::ImplHeader(const clang::CXXRecordDecl *decl) const {
2609-
return std::format("impl {} for Ptr<{}>", TraitName(decl),
2610-
GetRecordName(decl));
2607+
Converter::MethodsOnPtr &
2608+
ConverterRefCount::MethodsOnPtrFor(const clang::CXXRecordDecl *decl) {
2609+
auto name = GetRecordName(decl);
2610+
auto [it, inserted] = methods_on_ptr_.try_emplace(name);
2611+
if (inserted) {
2612+
it->second.trait_header = std::format("pub trait {}", TraitName(decl));
2613+
it->second.impl_header =
2614+
std::format("impl {} for Ptr<{}>", TraitName(decl), name);
2615+
}
2616+
return it->second;
26112617
}
26122618

26132619
bool ConverterRefCount::ConvertOutOfLineMethod(clang::CXXMethodDecl *decl) {
@@ -2619,10 +2625,49 @@ bool ConverterRefCount::ConvertOutOfLineMethod(clang::CXXMethodDecl *decl) {
26192625
PushMethodTarget push(*this, MethodTarget::PtrImpl);
26202626
ConvertCXXMethodDecl(decl);
26212627
}
2622-
deferred_impls_[ImplHeader(decl->getParent())] += std::move(buf).str();
2628+
MethodsOnPtrFor(decl->getParent()).impl_body += std::move(buf).str();
26232629
return false;
26242630
}
26252631

2632+
void ConverterRefCount::ConvertMethodOnPtr(clang::CXXMethodDecl *method) {
2633+
auto *record = method->getParent();
2634+
{
2635+
Buffer buf(*this);
2636+
{
2637+
PushCurrFunction push_fn(*this, method);
2638+
PushMethodTarget push(*this, MethodTarget::TraitDecl);
2639+
ConvertCXXMethodDecl(method);
2640+
}
2641+
MethodsOnPtrFor(record).trait_body += std::move(buf).str();
2642+
}
2643+
if (!method->isThisDeclarationADefinition()) {
2644+
return;
2645+
}
2646+
Buffer buf(*this);
2647+
{
2648+
PushMethodTarget push(*this, MethodTarget::PtrImpl);
2649+
VisitCXXMethodDecl(method);
2650+
}
2651+
MethodsOnPtrFor(record).impl_body += std::move(buf).str();
2652+
}
2653+
2654+
void ConverterRefCount::ConvertLateInstantiatedMethods(
2655+
clang::CXXRecordDecl *decl) {
2656+
Converter::ConvertCXXMethodDecls(
2657+
decl, std::format("{} {}", keyword::kImpl, GetRecordName(decl)),
2658+
[](auto *method) {
2659+
return IsEmittableMethod(method) && method->hasBody() &&
2660+
!IsMethodOnPtr(method) &&
2661+
!decl_ids_.contains(GetMethodID(method));
2662+
});
2663+
for (auto *method : decl->methods()) {
2664+
if (IsEmittableMethod(method) && method->hasBody() &&
2665+
IsMethodOnPtr(method) && !decl_ids_.contains(GetMethodID(method))) {
2666+
ConvertMethodOnPtr(method);
2667+
}
2668+
}
2669+
}
2670+
26262671
void ConverterRefCount::ConvertCXXRecordMethods(clang::CXXRecordDecl *decl) {
26272672
auto struct_name = GetRecordName(decl);
26282673

@@ -2632,42 +2677,16 @@ void ConverterRefCount::ConvertCXXRecordMethods(clang::CXXRecordDecl *decl) {
26322677
!IsMethodOnPtr(method);
26332678
});
26342679

2635-
bool synthesize_dtor =
2636-
!GetUserDefinedDestructor(decl) && HasFieldsNeedingDestruction(decl);
2637-
if (!synthesize_dtor &&
2638-
!std::ranges::any_of(decl->methods(), [](auto *method) {
2639-
return IsMethodOnPtr(method) && method->getDefinition();
2640-
})) {
2641-
return;
2642-
}
2643-
2644-
auto header = ImplHeader(decl);
2645-
StrCat(keyword::kPub, keyword::kTrait, TraitName(decl));
2646-
PushBrace trait_brace(*this);
2647-
26482680
for (auto *method : decl->methods()) {
2649-
if (!IsMethodOnPtr(method) || !method->getDefinition()) {
2650-
continue;
2651-
}
2652-
{
2653-
PushCurrFunction push_fn(*this, method);
2654-
PushMethodTarget push(*this, MethodTarget::TraitDecl);
2655-
ConvertCXXMethodDecl(method);
2656-
}
2657-
if (!method->isThisDeclarationADefinition()) {
2658-
continue;
2659-
}
2660-
Buffer buf(*this);
2661-
{
2662-
PushMethodTarget push(*this, MethodTarget::PtrImpl);
2663-
VisitCXXMethodDecl(method);
2681+
if (IsMethodOnPtr(method) && method->getDefinition()) {
2682+
ConvertMethodOnPtr(method);
26642683
}
2665-
deferred_impls_[header] += std::move(buf).str();
26662684
}
26672685

2668-
if (synthesize_dtor) {
2669-
StrCat(std::format("fn {}(&self)", kDestructorName), token::kSemiColon);
2670-
deferred_impls_[header] += std::format(
2686+
if (!GetUserDefinedDestructor(decl) && HasFieldsNeedingDestruction(decl)) {
2687+
MethodsOnPtrFor(decl).trait_body +=
2688+
std::format("fn {}(&self);\n", kDestructorName);
2689+
MethodsOnPtrFor(decl).impl_body += std::format(
26712690
"fn {}(&self) {{ {} }}\n", kDestructorName, DestroyMembers(decl));
26722691
}
26732692
}

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ class ConverterRefCount final : public Converter {
6060

6161
void ConvertCXXRecordMethods(clang::CXXRecordDecl *decl) override;
6262

63+
void ConvertLateInstantiatedMethods(clang::CXXRecordDecl *decl) override;
64+
65+
void ConvertMethodOnPtr(clang::CXXMethodDecl *method);
66+
6367
bool VisitCXXThisExpr(clang::CXXThisExpr *expr) override;
6468

6569
bool ThisIsRustPtr() const override;
@@ -215,7 +219,7 @@ class ConverterRefCount final : public Converter {
215219

216220
private:
217221
std::string TraitName(const clang::CXXRecordDecl *decl) const;
218-
std::string ImplHeader(const clang::CXXRecordDecl *decl) const;
222+
MethodsOnPtr &MethodsOnPtrFor(const clang::CXXRecordDecl *decl);
219223
std::string DestroyMembers(const clang::CXXRecordDecl *decl) override;
220224
void EmitScopedDestructor(const clang::VarDecl *decl) override;
221225

‎cpp2rust/cpp2rust_lib.cpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ std::string TranspileSrc(std::string_view cc_code, Model model,
3131
cc_code, tool_args, std::filesystem::path(filename).filename().string(),
3232
filename.ends_with(".c") ? CLANG_C_COMPILER : CLANG_CXX_COMPILER);
3333
rs_code += Converter::EmitOpaqueRecords();
34-
rs_code += Converter::EmitDeferredImpls();
34+
rs_code += Converter::EmitMethodsOnPtr();
3535
return rs_code;
3636
}
3737

@@ -72,7 +72,7 @@ std::string TranspileDir(std::string_view build_dir, Model model,
7272
FrontendActionFactory factory(rs_code, model, rules_dir);
7373
Tool.run(&factory);
7474
rs_code += Converter::EmitOpaqueRecords();
75-
rs_code += Converter::EmitDeferredImpls();
75+
rs_code += Converter::EmitMethodsOnPtr();
7676
return rs_code;
7777
}
7878
} // namespace cpp2rust
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
project(template_method_late_instantiation LANGUAGES CXX)
3+
add_executable(app a.cpp b.cpp)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <assert.h>
2+
3+
#include "s.h"
4+
5+
int main() {
6+
S<int> p;
7+
p.set(3);
8+
assert(f(&p) == 3);
9+
return 0;
10+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include "s.h"
2+
3+
int f(S<int> *p) { return p->get(); }
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
extern crate libcc2rs;
2+
use libcc2rs::*;
3+
use std::cell::RefCell;
4+
use std::collections::BTreeMap;
5+
use std::io::prelude::*;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::AsFd;
8+
use std::rc::{Rc, Weak};
9+
#[derive(Default)]
10+
pub struct S_int_ {
11+
pub x: Value<i32>,
12+
}
13+
impl Clone for S_int_ {
14+
fn clone(&self) -> Self {
15+
let __this: Value<S_int_> = Rc::new(RefCell::new(Self {
16+
x: Rc::new(RefCell::new((*self.x.borrow()))),
17+
}));
18+
let this: Ptr<S_int_> = __this.as_pointer();
19+
Rc::try_unwrap(__this).ok().unwrap().into_inner()
20+
}
21+
}
22+
impl ByteRepr for S_int_ {
23+
fn byte_size() -> usize {
24+
4
25+
}
26+
fn to_bytes(&self, buf: &mut [u8]) {
27+
(*self.x.borrow()).to_bytes(&mut buf[0..4]);
28+
}
29+
fn from_bytes(buf: &[u8]) -> Self {
30+
Self {
31+
x: Rc::new(RefCell::new(<i32>::from_bytes(&buf[0..4]))),
32+
}
33+
}
34+
}
35+
pub fn main() {
36+
std::process::exit(main_0());
37+
}
38+
fn main_0() -> i32 {
39+
let p: Value<S_int_> = Rc::new(RefCell::new(<S_int_>::default()));
40+
({ S_int_Impl::set(&p.as_pointer(), 3) });
41+
assert!((({ f_0((p.as_pointer()),) }) == 3));
42+
return 0;
43+
}
44+
pub fn f_0(p: Ptr<S_int_>) -> i32 {
45+
let p: Value<Ptr<S_int_>> = Rc::new(RefCell::new(p));
46+
return ({ S_int_Impl::get(&(*p.borrow())) });
47+
}
48+
pub trait S_int_Impl {
49+
fn set(&self, v: i32);
50+
fn get(&self) -> i32;
51+
}
52+
impl S_int_Impl for Ptr<S_int_> {
53+
fn set(&self, v: i32) {
54+
let v: Value<i32> = Rc::new(RefCell::new(v));
55+
(*(*(*self).upgrade().deref()).x.borrow_mut()) = (*v.borrow());
56+
}
57+
fn get(&self) -> i32 {
58+
return (*(*(*self).upgrade().deref()).x.borrow());
59+
}
60+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
extern crate libc;
2+
use libc::*;
3+
extern crate libcc2rs;
4+
use libcc2rs::*;
5+
use std::collections::BTreeMap;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
8+
use std::rc::Rc;
9+
#[repr(C)]
10+
#[derive(Copy, Clone, Default)]
11+
pub struct S_int_ {
12+
pub x: i32,
13+
}
14+
impl S_int_ {
15+
pub unsafe fn set(&mut self, mut v: i32) {
16+
self.x = v;
17+
}
18+
}
19+
pub fn main() {
20+
unsafe {
21+
std::process::exit(main_0() as i32);
22+
}
23+
}
24+
unsafe fn main_0() -> i32 {
25+
let mut p: S_int_ = <S_int_>::default();
26+
(unsafe { S_int_::set(&mut p, 3) });
27+
assert!(((unsafe { f_0((&mut p as *mut S_int_),) }) == (3)));
28+
return 0;
29+
}
30+
impl S_int_ {
31+
pub unsafe fn get(&mut self) -> i32 {
32+
return self.x;
33+
}
34+
}
35+
pub unsafe fn f_0(mut p: *mut S_int_) -> i32 {
36+
return (unsafe { S_int_::get(&mut (*p)) });
37+
}

0 commit comments

Comments
 (0)