Skip to content

Commit 8f79a34

Browse files
authored
Use from_fn for non-Copyable Rust types (#192)
In Rust, a struct is Copyable if it explicitly derives or implements Copy. All fields being Copy is not enough. Add the `derives` attribute in the IR that lists all traits that a mapped Rust type derives. For example rustls_slice_bytes, a struct with Copyable fields, doesn't derive Copy. So `derives` contains only Default. User defined types and builtin types also have their `derives` field filled. The entire PR is built around the following check: ```cpp if (auto *rec = element_type->getAsRecordDecl()) { if (!RecordDerivesCopy(rec)) { return std::format("std::array::from_fn::<_, {}, _>(|_| {})", size_as_string.c_str(), element_type_as_string); } } ``` Arrays of non-copyable types cannot be initialized with `[non_copyable_default, N]`, they have to use the `std::array:from_fn` format.
1 parent 6849e2c commit 8f79a34

17 files changed

Lines changed: 215 additions & 15 deletions

cpp2rust/converter/converter.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,12 @@ bool Converter::RecordDerivesDefault(const clang::RecordDecl *decl) {
665665
}
666666

667667
bool Converter::RecordDerivesCopy(const clang::RecordDecl *decl) {
668+
auto *derives = Mapper::MappedDerives(ctx_.getCanonicalTagType(decl));
669+
return derives &&
670+
std::find(derives->begin(), derives->end(), "Copy") != derives->end();
671+
}
672+
673+
bool Converter::RecordHasCopyableFields(const clang::RecordDecl *decl) {
668674
for (auto f : decl->fields()) {
669675
// Records that contain std::vector, std::array, std::string or anything
670676
// that is translated to Vec<>, do not derive Copy
@@ -751,8 +757,11 @@ void Converter::EmitRustStructOrUnion(clang::RecordDecl *decl) {
751757
if (EmitsReprCForRecords()) {
752758
StrCat("#[repr(C)]");
753759
}
760+
auto attrs = GetStructAttributes(decl);
761+
Mapper::SetDerives(ctx_.getCanonicalTagType(decl),
762+
std::vector<std::string>(attrs.begin(), attrs.end()));
754763
StrCat("#[derive(");
755-
for (auto *attr : GetStructAttributes(decl)) {
764+
for (auto *attr : attrs) {
756765
StrCat(attr, ',');
757766
}
758767
StrCat(")]");
@@ -3107,6 +3116,8 @@ bool Converter::VisitEnumDecl(clang::EnumDecl *decl) {
31073116
return false;
31083117
}
31093118
Mapper::AddRuleForUserDefinedType(decl);
3119+
Mapper::SetDerives(ctx_.getCanonicalTagType(decl),
3120+
{"Clone", "Copy", "PartialEq", "Debug", "Default"});
31103121
StrCat("#[derive(Clone, Copy, PartialEq, Debug, Default)]");
31113122
StrCat(std::format("enum {}", GetRecordName(decl)));
31123123
StrCat('{');
@@ -3324,6 +3335,12 @@ std::string Converter::GetArrayDefaultAsString(clang::QualType qual_type) {
33243335
auto size_as_string = GetNumAsString(array_type->getSize());
33253336
auto element_type = array_type->getElementType();
33263337
auto element_type_as_string = GetDefaultAsString(element_type);
3338+
if (auto *rec = element_type->getAsRecordDecl()) {
3339+
if (!RecordDerivesCopy(rec)) {
3340+
return std::format("std::array::from_fn::<_, {}, _>(|_| {})",
3341+
size_as_string.c_str(), element_type_as_string);
3342+
}
3343+
}
33273344
return std::format("[{}; {}]", element_type_as_string,
33283345
size_as_string.c_str());
33293346
}
@@ -3487,7 +3504,7 @@ Converter::GetStructAttributes(const clang::RecordDecl *decl) {
34873504

34883505
std::vector<const char *> struct_attrs;
34893506

3490-
if (RecordDerivesCopy(decl)) {
3507+
if (RecordHasCopyableFields(decl)) {
34913508
struct_attrs.emplace_back("Copy");
34923509
}
34933510

cpp2rust/converter/converter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
587587

588588
bool RecordDerivesCopy(const clang::RecordDecl *decl);
589589

590+
bool RecordHasCopyableFields(const clang::RecordDecl *decl);
591+
590592
bool ShouldReplaceWithMappedBody(clang::DeclRefExpr *expr) const;
591593

592594
std::string *rs_code_;

cpp2rust/converter/mapper.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,12 @@ void addBuiltinTypes(Model model) {
460460
const std::string &initializer = {}) {
461461
auto plain = TranslationRule::TypeRule::Plain(rust);
462462
plain.initializer = initializer;
463+
std::vector<std::string> derives = {"Copy", "Clone", "Default",
464+
"Debug", "PartialEq", "PartialOrd"};
465+
if (!(rust == "f32" || rust == "f64")) {
466+
derives.insert(derives.end(), {"Eq", "Ord", "Hash"});
467+
}
468+
plain.type_info.derives = std::move(derives);
463469
AddTypeRule(cxx, TranslationRule::TypeRule(plain));
464470
AddTypeRule("const " + cxx, std::move(plain));
465471

@@ -696,6 +702,16 @@ bool MapsToRefcountPointer(clang::QualType qual_type) {
696702
return rule && rule->type_info.is_refcount_pointer;
697703
}
698704

705+
const std::vector<std::string> *MappedDerives(clang::QualType qual_type) {
706+
auto rule = search(qual_type).first;
707+
return rule ? &rule->type_info.derives : nullptr;
708+
}
709+
710+
void SetDerives(clang::QualType qual_type, std::vector<std::string> derives) {
711+
if (auto *rule = search(qual_type).first) {
712+
rule->type_info.derives = std::move(derives);
713+
}
714+
}
699715
bool ReturnsPointer(const clang::Expr *expr) {
700716
auto rule = search(expr);
701717
return rule && rule->return_type.is_pointer();

cpp2rust/converter/mapper.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ std::string GetParamType(const clang::Expr *expr, unsigned index);
3737
bool ParamIsPointer(const clang::Expr *expr, unsigned index);
3838
bool MapsToPointer(clang::QualType qual_type);
3939
bool MapsToRefcountPointer(clang::QualType qual_type);
40+
const std::vector<std::string> *MappedDerives(clang::QualType qual_type);
41+
void SetDerives(clang::QualType qual_type, std::vector<std::string> derives);
4042

4143
enum class ScalarSugar {
4244
kDesugar,

cpp2rust/converter/translation_rule.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ TypeInfo ParseTypeInfoJSON(const llvm::json::Object &obj) {
2525
if (auto v = obj.getBoolean("is_unsafe_pointer"))
2626
info.is_unsafe_pointer = *v;
2727
assert(!(info.is_refcount_pointer && info.is_unsafe_pointer));
28+
if (auto *arr = obj.getArray("derives")) {
29+
for (const auto &elem : *arr) {
30+
if (auto s = elem.getAsString())
31+
info.derives.emplace_back(s->str());
32+
}
33+
}
2834
return info;
2935
}
3036

@@ -329,6 +335,8 @@ void TypeInfo::dump() const {
329335
log() << " [rc_ptr]";
330336
if (is_unsafe_pointer)
331337
log() << " [unsafe_ptr]";
338+
for (const auto &d : derives)
339+
log() << " +" << d;
332340
}
333341

334342
void TypeRule::dump() const {

cpp2rust/converter/translation_rule.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ struct MethodCallFragment {
5454
};
5555

5656
struct TypeInfo {
57+
std::vector<std::string> derives;
5758
std::string type;
5859
bool is_refcount_pointer = false;
5960
bool is_unsafe_pointer = false;
@@ -83,13 +84,13 @@ struct TypeRule {
8384
void dump() const;
8485

8586
static TypeRule Plain(std::string type) {
86-
return {{}, {}, {std::move(type), false, false}};
87+
return {{}, {}, {{}, std::move(type), false, false}};
8788
}
8889
static TypeRule RefcountPtr(std::string type) {
89-
return {{}, {}, {std::move(type), true, false}};
90+
return {{}, {}, {{}, std::move(type), true, false}};
9091
}
9192
static TypeRule UnsafePtr(std::string type) {
92-
return {{}, {}, {std::move(type), false, true}};
93+
return {{}, {}, {{}, std::move(type), false, true}};
9394
}
9495
};
9596

rule-preprocessor/src/ir.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ pub struct TypeInfo {
4444
pub is_refcount_pointer: bool,
4545
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
4646
pub is_unsafe_pointer: bool,
47+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
48+
pub derives: Vec<String>,
4749
}
4850

4951
#[derive(Debug, Clone, Serialize, Deserialize)]

rule-preprocessor/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55

66
extern crate rustc_driver;
77
extern crate rustc_hir;
8+
extern crate rustc_infer;
89
extern crate rustc_interface;
910
extern crate rustc_middle;
1011
extern crate rustc_span;
12+
extern crate rustc_trait_selection;
1113

1214
mod ir;
1315
mod semantic;

rule-preprocessor/src/semantic.rs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ fn find_rlib(deps_dir: &Path, crate_name: &str) -> Option<PathBuf> {
9090
struct FnDecl<'tcx> {
9191
source_file: String,
9292
name: String,
93+
def_id: rustc_span::def_id::DefId,
9394
body: &'tcx rustc_hir::Body<'tcx>,
9495
}
9596

@@ -124,11 +125,17 @@ struct MethodResolver {
124125
}
125126

126127
impl MethodResolver {
127-
fn resolve_fn_decl<'tcx>(&mut self, tcx: rustc_middle::ty::TyCtxt<'tcx>, f: &FnDecl<'tcx>) {
128-
if let Some(file_ir) = self.ir.all_ir.get_mut(&f.source_file)
129-
&& let Some(RuleIr::Fn(fn_ir)) = file_ir.get_mut(&f.name)
130-
{
131-
f.resolve_unknowns(tcx, fn_ir);
128+
fn resolve_rule<'tcx>(&mut self, tcx: rustc_middle::ty::TyCtxt<'tcx>, f: &FnDecl<'tcx>) {
129+
let Some(file_ir) = self.ir.all_ir.get_mut(&f.source_file) else {
130+
return;
131+
};
132+
match file_ir.get_mut(&f.name) {
133+
Some(RuleIr::Fn(fn_ir)) => f.resolve_unknowns(tcx, fn_ir),
134+
Some(RuleIr::Type(type_ir)) => {
135+
let ret_ty = tcx.fn_sig(f.def_id).skip_binder().output().skip_binder();
136+
type_ir.type_info.derives = type_derives(tcx, ret_ty);
137+
}
138+
None => {}
132139
}
133140
}
134141

@@ -154,7 +161,7 @@ impl rustc_driver::Callbacks for MethodResolver {
154161
tcx: rustc_middle::ty::TyCtxt<'_>,
155162
) -> rustc_driver::Compilation {
156163
for f in iter_fn_decls(tcx) {
157-
self.resolve_fn_decl(tcx, &f);
164+
self.resolve_rule(tcx, &f);
158165
}
159166

160167
rustc_driver::Compilation::Stop
@@ -181,6 +188,7 @@ fn iter_fn_decls<'tcx>(tcx: rustc_middle::ty::TyCtxt<'tcx>) -> Vec<FnDecl<'tcx>>
181188
result.push(FnDecl {
182189
source_file,
183190
name: ident.name.as_str().to_string(),
191+
def_id: decl_id.owner_id.to_def_id(),
184192
body: tcx.hir_body(body_id),
185193
});
186194
}
@@ -205,6 +213,44 @@ fn decl_source_file(
205213
)
206214
}
207215

216+
fn type_derives<'tcx>(
217+
tcx: rustc_middle::ty::TyCtxt<'tcx>,
218+
ty: rustc_middle::ty::Ty<'tcx>,
219+
) -> Vec<String> {
220+
use rustc_infer::infer::TyCtxtInferExt;
221+
use rustc_span::sym;
222+
use rustc_trait_selection::infer::InferCtxtExt;
223+
224+
let lang = tcx.lang_items();
225+
let derivable = [
226+
lang.copy_trait(),
227+
lang.clone_trait(),
228+
tcx.get_diagnostic_item(sym::Debug),
229+
tcx.get_diagnostic_item(sym::Default),
230+
tcx.get_diagnostic_item(sym::PartialEq),
231+
tcx.get_diagnostic_item(sym::Eq),
232+
tcx.get_diagnostic_item(sym::PartialOrd),
233+
tcx.get_diagnostic_item(sym::Ord),
234+
tcx.get_diagnostic_item(sym::Hash),
235+
];
236+
237+
let infcx = tcx
238+
.infer_ctxt()
239+
.build(rustc_middle::ty::TypingMode::non_body_analysis());
240+
241+
derivable
242+
.into_iter()
243+
.flatten()
244+
.filter(|&trait_def_id| {
245+
let args = vec![ty; tcx.generics_of(trait_def_id).count()];
246+
infcx
247+
.type_implements_trait(trait_def_id, args, rustc_middle::ty::ParamEnv::empty())
248+
.must_apply_modulo_regions()
249+
})
250+
.map(|trait_def_id| tcx.item_name(trait_def_id).to_string())
251+
.collect()
252+
}
253+
208254
struct AstVisitor<'a, 'tcx> {
209255
tcx: rustc_middle::ty::TyCtxt<'tcx>,
210256
param_names: Vec<String>,

rule-preprocessor/src/syntactic.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ impl<'a> FnIrBuilder<'a> {
322322
ty: ty_str,
323323
is_refcount_pointer,
324324
is_unsafe_pointer,
325+
derives: Vec::new(),
325326
})
326327
}
327328
}
@@ -482,6 +483,7 @@ impl<'a> FnIrBuilder<'a> {
482483
ty: p.ty.clone(),
483484
is_refcount_pointer: p.is_refcount_pointer,
484485
is_unsafe_pointer: p.is_unsafe_pointer,
486+
derives: Vec::new(),
485487
},
486488
)
487489
})
@@ -560,6 +562,7 @@ impl<'a> TypeIrBuilder<'a> {
560562
ty: ty.syntax().text().to_string(),
561563
is_refcount_pointer,
562564
is_unsafe_pointer,
565+
derives: Vec::new(),
563566
},
564567
}
565568
}

0 commit comments

Comments
 (0)