Skip to content

Commit 7853c4f

Browse files
authored
Translate records that contain declared but not defined methods (#394)
```cpp struct S { int a; void declared_but_never_defined(); ]; int main() { S s{1}; assert(s.a == 1); } ``` S is a valid record that can be translated to Rust. This PR translates it like this: ```rs struct S { a: i32 } trait SImpl { fn declared_but_never_defined(&self) { unimplemented!() } } ``` The function gets a default `unimplemented` implementation. Other TU might offer a definition for the declared-only method.
1 parent e6e3bd6 commit 7853c4f

32 files changed

Lines changed: 442 additions & 171 deletions

‎cpp2rust/converter/converter.cpp‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,9 @@ bool Converter::ConvertCXXMethodDecl(clang::CXXMethodDecl *decl) {
11141114
ConvertFunctionReturnType(decl);
11151115
if (decl->isPureVirtual() || method_target_ == MethodTarget::TraitDecl) {
11161116
StrCat(token::kSemiColon);
1117+
} else if (method_target_ == MethodTarget::TraitDefault) {
1118+
PushBrace body(*this);
1119+
StrCat("unimplemented!()");
11171120
} else {
11181121
PushBrace body(*this);
11191122
EmitFunctionPreamble(decl);

‎cpp2rust/converter/converter.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
691691
enum class MethodTarget : uint8_t {
692692
ValueImpl,
693693
TraitDecl,
694+
TraitDefault,
694695
PtrImpl,
695696
};
696697
MethodTarget method_target_ = MethodTarget::ValueImpl;

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,10 @@ bool HasDefaultedCopyConstructor(const clang::RecordDecl *decl) {
357357
return !cxx->defaultedCopyConstructorIsDeleted();
358358
}
359359

360+
bool RecordDerivesByteRepr(const clang::RecordDecl *decl) {
361+
return !decl->isUnion() && decl->field_empty();
362+
}
363+
360364
bool RecordHasOnlyReferenceFields(const clang::RecordDecl *decl) {
361365
for (auto *field : decl->fields()) {
362366
if (!field->getType()->isReferenceType()) {
@@ -418,17 +422,7 @@ bool IsPassThroughConstructor(const clang::CXXConstructorDecl *ctor) {
418422
}
419423

420424
bool IsConvertibleCXXRecordDecl(const clang::CXXRecordDecl *decl) {
421-
return decl->isThisDeclarationADefinition() &&
422-
std::all_of(
423-
decl->method_begin(), decl->method_end(), [](auto *method) {
424-
auto *ctor = clang::dyn_cast<clang::CXXConstructorDecl>(method);
425-
return method->getDefinition() || method->isPureVirtual() ||
426-
method->getTemplateInstantiationPattern() ||
427-
method->getDescribedFunctionTemplate() ||
428-
(ctor ? ctor->isCopyOrMoveConstructor()
429-
: method->isCopyAssignmentOperator() ||
430-
method->isMoveAssignmentOperator());
431-
});
425+
return decl->isThisDeclarationADefinition() && !decl->isDependentContext();
432426
}
433427

434428
bool IsConvertibleCXXMethodDecl(const clang::CXXMethodDecl *decl) {
@@ -1039,8 +1033,8 @@ bool IsMethodOnPtr(const clang::CXXMethodDecl *method) {
10391033
!IsComparisonOperator(method)) {
10401034
return false;
10411035
}
1042-
if (clang::isa<clang::CXXDestructorDecl>(method)) {
1043-
return GetUserDefinedDestructor(method->getParent()) != nullptr;
1036+
if (auto *dtor = clang::dyn_cast<clang::CXXDestructorDecl>(method)) {
1037+
return !dtor->isImplicit() && !dtor->isDefaulted();
10441038
}
10451039
return true;
10461040
}

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ bool HasDefaultedCopyConstructor(const clang::RecordDecl *decl);
9191

9292
bool RecordHasOnlyReferenceFields(const clang::RecordDecl *decl);
9393

94+
bool RecordDerivesByteRepr(const clang::RecordDecl *decl);
95+
9496
bool HasDefaultedCopyAssignment(const clang::RecordDecl *decl);
9597

9698
bool IsRValueConvertingConstructor(const clang::CXXConstructorDecl *ctor);

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,10 @@ void ConverterRefCount::EmitRustUnion(clang::RecordDecl *decl) {
574574
}
575575

576576
void ConverterRefCount::AddByteReprTrait(const clang::RecordDecl *decl) {
577+
if (RecordDerivesByteRepr(decl)) {
578+
return;
579+
}
580+
577581
auto struct_name = GetRecordName(decl);
578582

579583
if (!TypeImplementsByteRepr(ctx_.getCanonicalTagType(decl))) {
@@ -2152,6 +2156,10 @@ ConverterRefCount::GetStructAttributes(const clang::RecordDecl *decl) {
21522156
attrs.emplace_back("Clone");
21532157
}
21542158

2159+
if (RecordDerivesByteRepr(decl)) {
2160+
attrs.emplace_back("ByteRepr");
2161+
}
2162+
21552163
if (RecordDerivesDefault(decl)) {
21562164
attrs.emplace_back("Default");
21572165
}
@@ -2855,17 +2863,20 @@ bool ConverterRefCount::ConvertOutOfLineMethod(clang::CXXMethodDecl *decl) {
28552863
return false;
28562864
}
28572865

2858-
void ConverterRefCount::ConvertMethodOnPtr(clang::CXXMethodDecl *method) {
2859-
auto *record = method->getParent();
2866+
void ConverterRefCount::ConvertMethodOnPtrTraitDecl(
2867+
clang::CXXMethodDecl *method) {
2868+
Buffer buf(*this);
28602869
{
2861-
Buffer buf(*this);
2862-
{
2863-
PushCurrFunction push_fn(*this, method);
2864-
PushMethodTarget push(*this, MethodTarget::TraitDecl);
2865-
ConvertCXXMethodDecl(method);
2866-
}
2867-
MethodsOnPtrFor(record).trait_body += std::move(buf).str();
2870+
PushCurrFunction push_fn(*this, method);
2871+
PushMethodTarget push(*this, method->getDefinition()
2872+
? MethodTarget::TraitDecl
2873+
: MethodTarget::TraitDefault);
2874+
ConvertCXXMethodDecl(method);
28682875
}
2876+
MethodsOnPtrFor(method->getParent()).trait_body += std::move(buf).str();
2877+
}
2878+
2879+
void ConverterRefCount::ConvertMethodOnPtr(clang::CXXMethodDecl *method) {
28692880
if (!method->isThisDeclarationADefinition()) {
28702881
return;
28712882
}
@@ -2874,7 +2885,7 @@ void ConverterRefCount::ConvertMethodOnPtr(clang::CXXMethodDecl *method) {
28742885
PushMethodTarget push(*this, MethodTarget::PtrImpl);
28752886
VisitCXXMethodDecl(method);
28762887
}
2877-
MethodsOnPtrFor(record).impl_body += std::move(buf).str();
2888+
MethodsOnPtrFor(method->getParent()).impl_body += std::move(buf).str();
28782889
}
28792890

28802891
void ConverterRefCount::ConvertLateInstantiatedMethods(
@@ -2908,7 +2919,8 @@ void ConverterRefCount::ConvertCXXRecordMethods(clang::CXXRecordDecl *decl) {
29082919
});
29092920

29102921
auto convert_method = [&](clang::CXXMethodDecl *method) {
2911-
if (IsMethodOnPtr(method) && method->getDefinition()) {
2922+
if (IsMethodOnPtr(method)) {
2923+
ConvertMethodOnPtrTraitDecl(method);
29122924
ConvertMethodOnPtr(method);
29132925
}
29142926
};

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class ConverterRefCount final : public Converter {
6666

6767
void ConvertLateInstantiatedMethods(clang::CXXRecordDecl *decl) override;
6868

69+
void ConvertMethodOnPtrTraitDecl(clang::CXXMethodDecl *method);
6970
void ConvertMethodOnPtr(clang::CXXMethodDecl *method);
7071

7172
bool VisitCXXThisExpr(clang::CXXThisExpr *expr) override;
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(out_of_line_methods_cross_tu LANGUAGES CXX)
3+
add_executable(app a.cpp b.cpp)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <assert.h>
2+
3+
#include "s.h"
4+
5+
int main() {
6+
S s(1);
7+
assert(s.get() == 1);
8+
s.set(4);
9+
assert(s.get() == 4);
10+
assert(s.add(2) == 6);
11+
return 0;
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include "s.h"
2+
3+
S::S(int x) : v(x) {}
4+
5+
S::~S() {}
6+
7+
void S::set(int x) { v = x; }
8+
9+
int S::add(int x) {
10+
v += x;
11+
return v;
12+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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 {
11+
pub v: Value<i32>,
12+
}
13+
impl S {}
14+
impl Clone for S {
15+
fn clone(&self) -> Self {
16+
let __this: Value<S> = Rc::new(RefCell::new(Self {
17+
v: Rc::new(RefCell::new((*self.v.borrow()))),
18+
}));
19+
let this: Ptr<S> = __this.as_pointer();
20+
Rc::try_unwrap(__this).ok().unwrap().into_inner()
21+
}
22+
}
23+
impl ByteRepr for S {
24+
fn byte_size() -> usize {
25+
4
26+
}
27+
fn to_bytes(&self, buf: &mut [u8]) {
28+
(*self.v.borrow()).to_bytes(&mut buf[0..4]);
29+
}
30+
fn from_bytes(buf: &[u8]) -> Self {
31+
Self {
32+
v: Rc::new(RefCell::new(<i32>::from_bytes(&buf[0..4]))),
33+
}
34+
}
35+
}
36+
pub fn main() {
37+
__cpp2rust_init_globals();
38+
std::process::exit(main_0());
39+
}
40+
fn main_0() -> i32 {
41+
let s: Value<S> = Rc::new(RefCell::new(S::S({ 1 })));
42+
assert!((({ SImpl::get(&s.as_pointer(),) }) == 1));
43+
({ SImpl::set(&s.as_pointer(), 4) });
44+
assert!((({ SImpl::get(&s.as_pointer(),) }) == 4));
45+
assert!((({ SImpl::add(&s.as_pointer(), 2,) }) == 6));
46+
return 0;
47+
}
48+
impl S {
49+
pub fn S(x: i32) -> Self {
50+
let x: Value<i32> = Rc::new(RefCell::new(x));
51+
let __this: Value<S> = Rc::new(RefCell::new(Self {
52+
v: Rc::new(RefCell::new((*x.borrow()))),
53+
}));
54+
let this: Ptr<S> = __this.as_pointer();
55+
Rc::try_unwrap(__this).ok().unwrap().into_inner()
56+
}
57+
}
58+
impl S {}
59+
pub trait SImpl {
60+
fn destructor(&self) {
61+
unimplemented!()
62+
}
63+
fn get(&self) -> i32;
64+
fn set(&self, x: i32) {
65+
unimplemented!()
66+
}
67+
fn add(&self, x: i32) -> i32 {
68+
unimplemented!()
69+
}
70+
}
71+
impl SImpl for Ptr<S> {
72+
fn get(&self) -> i32 {
73+
return (*(*(*self).upgrade().deref()).v.borrow());
74+
}
75+
fn destructor(&self) {}
76+
fn set(&self, x: i32) {
77+
let x: Value<i32> = Rc::new(RefCell::new(x));
78+
(*(*(*self).upgrade().deref()).v.borrow_mut()) = (*x.borrow());
79+
}
80+
fn add(&self, x: i32) -> i32 {
81+
let x: Value<i32> = Rc::new(RefCell::new(x));
82+
(*(*(*self).upgrade().deref()).v.borrow_mut()) += (*x.borrow());
83+
return (*(*(*self).upgrade().deref()).v.borrow());
84+
}
85+
}
86+
pub fn __cpp2rust_init_globals() {}

0 commit comments

Comments
 (0)