Skip to content

Commit 42f0ad8

Browse files
authored
Fix class template translations (#195)
Fixes how class templates are translated. Class templates are currently monomorphized: https://github.com/Cpp2Rust/cpp2rust/blob/8f79a34db82d97f1b41ac2576b8a202458d029ea/cpp2rust/converter/converter.cpp#L3316-L3321 However, the name of each translated Rust struct was the plain C++ name, leading to collisions. Additionally, the C++ name used in the translation rule inserted into Mapper was also the plain C++ name. This name does not match how the type itself is printed, which led to internal cpp2rust assertion failures due to missing translation rules.
1 parent 0dfb5ba commit 42f0ad8

6 files changed

Lines changed: 373 additions & 3 deletions

File tree

cpp2rust/converter/converter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3493,7 +3493,7 @@ std::string Converter::GetRecordName(const clang::NamedDecl *decl) const {
34933493
if (auto it = inner_structs_.find(ID); it != inner_structs_.end()) {
34943494
return it->second;
34953495
}
3496-
return ReplaceAll(Mapper::ToString(decl), "::", "_");
3496+
return Mapper::ToRustName(Mapper::ToString(Mapper::GetTypeForDecl(decl)));
34973497
}
34983498

34993499
std::vector<const char *>

cpp2rust/converter/mapper.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -739,9 +739,30 @@ bool ParamIsPointer(const clang::Expr *expr, unsigned index) {
739739
return GetParamInfo(expr, index).is_pointer();
740740
}
741741

742+
clang::QualType GetTypeForDecl(const clang::NamedDecl *decl) {
743+
if (const auto *spec =
744+
llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(decl)) {
745+
llvm::ArrayRef<clang::TemplateArgument> args =
746+
spec->getTemplateArgs().asArray();
747+
llvm::SmallVector<clang::TemplateArgument, 4> canon(args.begin(),
748+
args.end());
749+
ctx_->canonicalizeTemplateArguments(canon);
750+
751+
return ctx_->getTemplateSpecializationType(
752+
clang::ElaboratedTypeKeyword::None,
753+
clang::TemplateName(spec->getSpecializedTemplate()), args, canon);
754+
}
755+
756+
const auto *rdecl = llvm::dyn_cast<clang::TagDecl>(decl);
757+
assert(rdecl && "Unsupported decl type");
758+
759+
return ctx_->getTagType(clang::ElaboratedTypeKeyword::None,
760+
rdecl->getQualifier(), rdecl, /*OwnsTag*/ false);
761+
}
762+
742763
void AddRuleForUserDefinedType(clang::NamedDecl *decl) {
743-
auto cpp_name = ToString(decl);
744-
auto rs_name = ReplaceAll(cpp_name, "::", "_");
764+
auto cpp_name = ToString(GetTypeForDecl(decl));
765+
auto rs_name = ToRustName(cpp_name);
745766

746767
AddTypeRule(cpp_name, TranslationRule::TypeRule::Plain(rs_name));
747768

@@ -783,6 +804,15 @@ void AddRuleForUserDefinedType(clang::NamedDecl *decl) {
783804
}
784805
}
785806

807+
std::string ToRustName(std::string name) {
808+
size_t pos = 0;
809+
while ((pos = name.find_first_of("<>, ", pos)) != std::string::npos) {
810+
name[pos] = '_';
811+
++pos;
812+
}
813+
return ReplaceAll(name, "::", "_");
814+
}
815+
786816
std::string ToString(clang::QualType qual_type, ScalarSugar sugar) {
787817
assert(ctx_);
788818

cpp2rust/converter/mapper.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ enum class ScalarSugar {
4545
kPreserve,
4646
};
4747

48+
clang::QualType GetTypeForDecl(const clang::NamedDecl *decl);
4849
std::string ToString(clang::QualType qual_type,
4950
ScalarSugar sugar = ScalarSugar::kDesugar);
5051
std::string ToString(const clang::Expr *expr);
5152
std::string ToString(const clang::NamedDecl *decl);
53+
std::string ToRustName(std::string name);
5254

5355
void LoadTranslationRules(Model model, clang::ASTContext &ctx,
5456
const std::string &rules_dir);

tests/unit/class_templates.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include <cassert>
2+
#include <vector>
3+
4+
template <typename T> class MyContainer {
5+
std::vector<T> vec;
6+
7+
public:
8+
using value_type = T;
9+
using reference = T &;
10+
using const_reference = const T &;
11+
using size_type = std::size_t;
12+
13+
bool empty() const { return vec.empty(); }
14+
size_type size() const { return vec.size(); }
15+
const_reference back() const { return vec.back(); }
16+
reference back() { return vec.back(); }
17+
void pop_back() { return vec.pop_back(); }
18+
void push_back(const_reference item) { vec.push_back(item); }
19+
};
20+
21+
int main() {
22+
MyContainer<int> imc;
23+
assert(imc.empty());
24+
imc.push_back(1);
25+
assert(imc.size() == 1 && imc.back() == 1);
26+
imc.pop_back();
27+
assert(imc.empty());
28+
29+
MyContainer<char> cmc;
30+
assert(cmc.empty());
31+
cmc.push_back('a');
32+
assert(cmc.size() == 1 && cmc.back() == 'a');
33+
cmc.pop_back();
34+
assert(cmc.empty());
35+
36+
MyContainer<float> fmc;
37+
assert(fmc.empty());
38+
fmc.push_back(1.0);
39+
assert(fmc.size() == 1 && fmc.back() == 1.0);
40+
fmc.pop_back();
41+
assert(fmc.empty());
42+
return 0;
43+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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 MyContainer_int_ {
11+
vec_: Value<Vec<i32>>,
12+
}
13+
impl MyContainer_int_ {
14+
pub fn empty(&self) -> bool {
15+
return (*self.vec_.borrow()).is_empty();
16+
}
17+
pub fn size(&self) -> usize {
18+
return (*self.vec_.borrow()).len();
19+
}
20+
pub fn back_const(&self) -> Ptr<i32> {
21+
return (self.vec_.as_pointer() as Ptr<i32>).to_last();
22+
}
23+
pub fn back(&self) -> Ptr<i32> {
24+
return (self.vec_.as_pointer() as Ptr<i32>).to_last();
25+
}
26+
pub fn pop_back(&self) {
27+
(*self.vec_.borrow_mut()).pop();
28+
return;
29+
}
30+
pub fn push_back(&self, item: Ptr<i32>) {
31+
{
32+
let a0_clone = (item.read()).clone();
33+
(*self.vec_.borrow_mut()).push(a0_clone)
34+
};
35+
}
36+
}
37+
impl Clone for MyContainer_int_ {
38+
fn clone(&self) -> Self {
39+
let mut this = Self {
40+
vec_: Rc::new(RefCell::new((*self.vec_.borrow()).clone())),
41+
};
42+
this
43+
}
44+
}
45+
impl ByteRepr for MyContainer_int_ {}
46+
#[derive(Default)]
47+
pub struct MyContainer_char_ {
48+
vec_: Value<Vec<u8>>,
49+
}
50+
impl MyContainer_char_ {
51+
pub fn empty(&self) -> bool {
52+
return (*self.vec_.borrow()).is_empty();
53+
}
54+
pub fn size(&self) -> usize {
55+
return (*self.vec_.borrow()).len();
56+
}
57+
pub fn back_const(&self) -> Ptr<u8> {
58+
return (self.vec_.as_pointer() as Ptr<u8>).to_last();
59+
}
60+
pub fn back(&self) -> Ptr<u8> {
61+
return (self.vec_.as_pointer() as Ptr<u8>).to_last();
62+
}
63+
pub fn pop_back(&self) {
64+
(*self.vec_.borrow_mut()).pop();
65+
return;
66+
}
67+
pub fn push_back(&self, item: Ptr<u8>) {
68+
{
69+
let a0_clone = (item.read()).clone();
70+
(*self.vec_.borrow_mut()).push(a0_clone)
71+
};
72+
}
73+
}
74+
impl Clone for MyContainer_char_ {
75+
fn clone(&self) -> Self {
76+
let mut this = Self {
77+
vec_: Rc::new(RefCell::new((*self.vec_.borrow()).clone())),
78+
};
79+
this
80+
}
81+
}
82+
impl ByteRepr for MyContainer_char_ {}
83+
#[derive(Default)]
84+
pub struct MyContainer_float_ {
85+
vec_: Value<Vec<f32>>,
86+
}
87+
impl MyContainer_float_ {
88+
pub fn empty(&self) -> bool {
89+
return (*self.vec_.borrow()).is_empty();
90+
}
91+
pub fn size(&self) -> usize {
92+
return (*self.vec_.borrow()).len();
93+
}
94+
pub fn back_const(&self) -> Ptr<f32> {
95+
return (self.vec_.as_pointer() as Ptr<f32>).to_last();
96+
}
97+
pub fn back(&self) -> Ptr<f32> {
98+
return (self.vec_.as_pointer() as Ptr<f32>).to_last();
99+
}
100+
pub fn pop_back(&self) {
101+
(*self.vec_.borrow_mut()).pop();
102+
return;
103+
}
104+
pub fn push_back(&self, item: Ptr<f32>) {
105+
{
106+
let a0_clone = (item.read()).clone();
107+
(*self.vec_.borrow_mut()).push(a0_clone)
108+
};
109+
}
110+
}
111+
impl Clone for MyContainer_float_ {
112+
fn clone(&self) -> Self {
113+
let mut this = Self {
114+
vec_: Rc::new(RefCell::new((*self.vec_.borrow()).clone())),
115+
};
116+
this
117+
}
118+
}
119+
impl ByteRepr for MyContainer_float_ {}
120+
pub fn main() {
121+
std::process::exit(main_0());
122+
}
123+
fn main_0() -> i32 {
124+
let imc: Value<MyContainer_int_> = Rc::new(RefCell::new(<MyContainer_int_>::default()));
125+
assert!(({ (*imc.borrow()).empty() }));
126+
({
127+
let _item: Value<i32> = Rc::new(RefCell::new(1));
128+
(*imc.borrow()).push_back(_item.as_pointer())
129+
});
130+
assert!(
131+
(({ (*imc.borrow()).size() }) == 1_usize) && ((({ (*imc.borrow()).back() }).read()) == 1)
132+
);
133+
({ (*imc.borrow()).pop_back() });
134+
assert!(({ (*imc.borrow()).empty() }));
135+
let cmc: Value<MyContainer_char_> = Rc::new(RefCell::new(<MyContainer_char_>::default()));
136+
assert!(({ (*cmc.borrow()).empty() }));
137+
({
138+
let _item: Value<u8> = Rc::new(RefCell::new(('a' as u8)));
139+
(*cmc.borrow()).push_back(_item.as_pointer())
140+
});
141+
assert!(
142+
(({ (*cmc.borrow()).size() }) == 1_usize)
143+
&& (((({ (*cmc.borrow()).back() }).read()) as i32) == (('a' as u8) as i32))
144+
);
145+
({ (*cmc.borrow()).pop_back() });
146+
assert!(({ (*cmc.borrow()).empty() }));
147+
let fmc: Value<MyContainer_float_> = Rc::new(RefCell::new(<MyContainer_float_>::default()));
148+
assert!(({ (*fmc.borrow()).empty() }));
149+
({
150+
let _item: Value<f32> = Rc::new(RefCell::new((1.0E+0 as f32)));
151+
(*fmc.borrow()).push_back(_item.as_pointer())
152+
});
153+
assert!(
154+
(({ (*fmc.borrow()).size() }) == 1_usize)
155+
&& (((({ (*fmc.borrow()).back() }).read()) as f64) == 1.0E+0)
156+
);
157+
({ (*fmc.borrow()).pop_back() });
158+
assert!(({ (*fmc.borrow()).empty() }));
159+
return 0;
160+
}

0 commit comments

Comments
 (0)