Skip to content

Commit 2bb3063

Browse files
authored
Translate self referencing initialiser (#498)
Fixes the original issue reported in #248. Self-refrencing in constructors is not handled here. It translates: ```cpp struct list_head list = { &list }; ``` to: ```rs let list: Value<list_head> = <Value<list_head>>::default(); *list.borrow_mut() = list_head { next: (list.as_pointer()), }; ``` That is, first create a default value, then assign it the self-referencing initialiser. Types that have defaults with side-effects are not supported.
1 parent bf89150 commit 2bb3063

9 files changed

Lines changed: 376 additions & 0 deletions

‎cpp2rust/converter/converter.cpp‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,25 @@ void Converter::ConvertVarDecl(clang::VarDecl *decl) {
559559
}
560560

561561
HoistMaterializedTempBindings hoist_temps(*this);
562+
if (decl->isLocalVarDecl() && !IsGlobalVar(decl) && decl->hasInit() &&
563+
ReferencesVar(decl->getInit(), decl)) {
564+
if (decl->getType()->isReferenceType()) {
565+
llvm::report_fatal_error(
566+
"self-referential initializer of a reference is not supported");
567+
}
568+
if (DefaultInitHasSideEffects(ctx_, decl->getType())) {
569+
llvm::report_fatal_error("self-referential initializer of a variable "
570+
"whose default initialization has side effects "
571+
"is not supported");
572+
}
573+
hoisted_decls_.insert(decl);
574+
if (ConvertVarDeclSkipInit(decl)) {
575+
StrCat(token::kAssign, ConvertVarDefaultInit(decl), token::kSemiColon);
576+
}
577+
EmitHoistedInArmAssignment(decl);
578+
return;
579+
}
580+
562581
if (!ConvertVarDeclSkipInit(decl)) {
563582
// Skip global variables declared extern
564583
return;
@@ -595,6 +614,10 @@ bool Converter::VisitVarDecl(clang::VarDecl *decl) {
595614
}
596615

597616
if (IsGlobalVar(decl)) {
617+
if (decl->hasInit() && ReferencesVar(decl->getInit(), decl)) {
618+
llvm::report_fatal_error(
619+
"self-referential initializer of a static variable is not supported");
620+
}
598621
ConvertGlobalVarDecl(decl);
599622
} else {
600623
ConvertVarDecl(decl);

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,48 @@ GetAllVars(const clang::Stmt *stmt) {
16581658
return vars;
16591659
}
16601660

1661+
bool ReferencesVar(const clang::Stmt *stmt, const clang::VarDecl *var) {
1662+
if (!stmt) {
1663+
return false;
1664+
}
1665+
if (auto *decl_ref = clang::dyn_cast<clang::DeclRefExpr>(stmt)) {
1666+
return decl_ref->getDecl() == var;
1667+
}
1668+
if (clang::isa<clang::UnaryExprOrTypeTraitExpr>(stmt)) {
1669+
return false;
1670+
}
1671+
return std::ranges::any_of(stmt->children(), [var](const clang::Stmt *child) {
1672+
return ReferencesVar(child, var);
1673+
});
1674+
}
1675+
1676+
bool DefaultInitHasSideEffects(const clang::ASTContext &ctx,
1677+
clang::QualType type) {
1678+
auto *record = ctx.getBaseElementType(type)->getAsCXXRecordDecl();
1679+
if (!record || record->hasTrivialDefaultConstructor()) {
1680+
return false;
1681+
}
1682+
if (!record->hasDefaultConstructor() ||
1683+
record->hasUserProvidedDefaultConstructor()) {
1684+
return true;
1685+
}
1686+
for (const auto &base : record->bases()) {
1687+
if (DefaultInitHasSideEffects(ctx, base.getType())) {
1688+
return true;
1689+
}
1690+
}
1691+
for (auto *field : record->fields()) {
1692+
if (auto *init = field->getInClassInitializer()) {
1693+
if (init->HasSideEffects(ctx)) {
1694+
return true;
1695+
}
1696+
} else if (DefaultInitHasSideEffects(ctx, field->getType())) {
1697+
return true;
1698+
}
1699+
}
1700+
return false;
1701+
}
1702+
16611703
bool ReadsMemory(const clang::Stmt *stmt) {
16621704
if (auto *cast = clang::dyn_cast<clang::ImplicitCastExpr>(stmt);
16631705
cast && cast->getCastKind() == clang::CK_LValueToRValue) {

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,11 @@ clang::Expr *GetCallee(clang::CallExpr *expr);
291291
std::unordered_set<const clang::ValueDecl *>
292292
GetAllVars(const clang::Stmt *stmt);
293293

294+
bool ReferencesVar(const clang::Stmt *stmt, const clang::VarDecl *var);
295+
296+
bool DefaultInitHasSideEffects(const clang::ASTContext &ctx,
297+
clang::QualType type);
298+
294299
bool ReferencesThis(const clang::Stmt *stmt);
295300

296301
// Whether evaluating `stmt` reads a value from memory, e.g., a pointer.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)]
10+
#[byte_size(8)]
11+
pub struct pair {
12+
#[offset(0)]
13+
pub a: i32,
14+
#[offset(4)]
15+
pub b: i32,
16+
}
17+
pub fn main() {
18+
__cpp2rust_init_globals();
19+
std::process::exit(main_0());
20+
}
21+
fn main_0() -> i32 {
22+
let mut x: i32 = 0_i32;
23+
x = (x + 1);
24+
let mut p: pair = <pair>::default();
25+
p = pair { a: 1, b: (p.a + 1) };
26+
return (x + p.b);
27+
}
28+
pub fn __cpp2rust_init_globals() {}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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, VaArg, FnPtrArg, Default)]
11+
pub struct pair {
12+
pub a: i32,
13+
pub b: i32,
14+
}
15+
pub fn main() {
16+
unsafe {
17+
__cpp2rust_init_globals();
18+
std::process::exit(main_0() as i32);
19+
}
20+
}
21+
unsafe fn main_0() -> i32 {
22+
let mut x: i32 = 0_i32;
23+
x = ((x) + (1));
24+
let mut p: pair = <pair>::default();
25+
p = pair {
26+
a: 1,
27+
b: ((p.a) + (1)),
28+
};
29+
return ((x) + (p.b));
30+
}
31+
pub unsafe fn __cpp2rust_init_globals() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// nondet-result
2+
3+
struct pair {
4+
int a;
5+
int b;
6+
};
7+
8+
int main() {
9+
int x = x + 1;
10+
pair p = {1, p.a + 1};
11+
return x + p.b;
12+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)]
10+
#[byte_size(8)]
11+
pub struct list_head {
12+
#[offset(0)]
13+
#[byte_size(8)]
14+
pub next: Ptr<list_head>,
15+
}
16+
#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)]
17+
#[byte_size(16)]
18+
pub struct node {
19+
#[offset(0)]
20+
pub value: i32,
21+
#[offset(8)]
22+
#[byte_size(8)]
23+
pub value_ptr: Ptr<i32>,
24+
}
25+
#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg)]
26+
#[byte_size(16)]
27+
pub struct node_with_defaults {
28+
#[offset(0)]
29+
#[byte_size(8)]
30+
pub next: Ptr<node_with_defaults>,
31+
#[offset(8)]
32+
pub value: i32,
33+
}
34+
impl Default for node_with_defaults {
35+
fn default() -> Self {
36+
node_with_defaults {
37+
next: Ptr::<node_with_defaults>::null(),
38+
value: 3,
39+
}
40+
}
41+
}
42+
pub fn init_0(mut l: Ptr<list_head>) -> Ptr<list_head> {
43+
field!(l, next).write(Ptr::<list_head>::null());
44+
return l;
45+
}
46+
pub fn main() {
47+
__cpp2rust_init_globals();
48+
std::process::exit(main_0());
49+
}
50+
fn main_0() -> i32 {
51+
let list: Value<list_head> = <Value<list_head>>::default();
52+
*list.borrow_mut() = list_head {
53+
next: (list.as_pointer()),
54+
};
55+
assert!(({ { (*list.borrow()).next.clone() } } == { (list.as_pointer()) }));
56+
let n: Value<node> = <Value<node>>::default();
57+
*n.borrow_mut() = node {
58+
value: 42,
59+
value_ptr: (field_ptr!(n.as_pointer(), value)),
60+
};
61+
assert!(({ { (*n.borrow()).value_ptr.clone() } } == { (field_ptr!(n.as_pointer(), value)) }));
62+
{ (*n.borrow()).value_ptr.clone() }.write(7);
63+
assert!(({ (*n.borrow()).value } == 7));
64+
let arr: Value<Box<[list_head]>> = Rc::new(RefCell::new(
65+
(0..2)
66+
.map(|_| <list_head>::default())
67+
.collect::<Box<[list_head]>>(),
68+
));
69+
*arr.borrow_mut() = Box::new([
70+
list_head {
71+
next: ((arr.as_pointer() as Ptr<list_head>).offset(1)),
72+
},
73+
list_head {
74+
next: ((arr.as_pointer() as Ptr<list_head>).offset(0)),
75+
},
76+
]);
77+
assert!(
78+
({ { (*arr.borrow())[(0) as usize].next.clone() } } == {
79+
((arr.as_pointer() as Ptr<list_head>).offset(1))
80+
})
81+
);
82+
assert!(
83+
({ { (*arr.borrow())[(1) as usize].next.clone() } } == {
84+
((arr.as_pointer() as Ptr<list_head>).offset(0))
85+
})
86+
);
87+
let d: Value<node_with_defaults> = <Value<node_with_defaults>>::default();
88+
*d.borrow_mut() = node_with_defaults {
89+
next: (d.as_pointer()),
90+
value: 3,
91+
};
92+
assert!(({ { (*d.borrow()).next.clone() } } == { (d.as_pointer()) }));
93+
assert!(({ (*d.borrow()).value } == 3));
94+
let called: Value<list_head> = <Value<list_head>>::default();
95+
*called.borrow_mut() = list_head {
96+
next: ({ init_0((called.as_pointer())) }),
97+
};
98+
assert!(({ { (*called.borrow()).next.clone() } } == { (called.as_pointer()) }));
99+
let mut size: usize = ::std::mem::size_of::<usize>();
100+
assert!((size == ::std::mem::size_of::<usize>()));
101+
return 0;
102+
}
103+
pub fn __cpp2rust_init_globals() {}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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, VaArg, FnPtrArg, Default)]
11+
pub struct list_head {
12+
pub next: *mut list_head,
13+
}
14+
#[repr(C)]
15+
#[derive(Copy, Clone, VaArg, FnPtrArg, Default)]
16+
pub struct node {
17+
pub value: i32,
18+
pub value_ptr: *mut i32,
19+
}
20+
#[repr(C)]
21+
#[derive(Copy, Clone, VaArg, FnPtrArg)]
22+
pub struct node_with_defaults {
23+
pub next: *mut node_with_defaults,
24+
pub value: i32,
25+
}
26+
impl Default for node_with_defaults {
27+
fn default() -> Self {
28+
node_with_defaults {
29+
next: std::ptr::null_mut(),
30+
value: 3,
31+
}
32+
}
33+
}
34+
pub unsafe fn init_0(mut l: *mut list_head) -> *mut list_head {
35+
(*l).next = std::ptr::null_mut();
36+
return l;
37+
}
38+
pub fn main() {
39+
unsafe {
40+
__cpp2rust_init_globals();
41+
std::process::exit(main_0() as i32);
42+
}
43+
}
44+
unsafe fn main_0() -> i32 {
45+
let mut list: list_head = <list_head>::default();
46+
list = list_head {
47+
next: (&mut list as *mut list_head),
48+
};
49+
assert!(((list.next) == (&mut list as *mut list_head)));
50+
let mut n: node = <node>::default();
51+
n = node {
52+
value: 42,
53+
value_ptr: (&mut n.value as *mut i32),
54+
};
55+
assert!(((n.value_ptr) == (&mut n.value as *mut i32)));
56+
(*n.value_ptr) = 7;
57+
assert!(((n.value) == (7)));
58+
let mut arr: [list_head; 2] = [<list_head>::default(); 2];
59+
arr = [
60+
list_head {
61+
next: (&mut arr[(1) as usize] as *mut list_head),
62+
},
63+
list_head {
64+
next: (&mut arr[(0) as usize] as *mut list_head),
65+
},
66+
];
67+
assert!(((arr[(0) as usize].next) == (&mut arr[(1) as usize] as *mut list_head)));
68+
assert!(((arr[(1) as usize].next) == (&mut arr[(0) as usize] as *mut list_head)));
69+
let mut d: node_with_defaults = <node_with_defaults>::default();
70+
d = node_with_defaults {
71+
next: (&mut d as *mut node_with_defaults),
72+
value: 3,
73+
};
74+
assert!(((d.next) == (&mut d as *mut node_with_defaults)));
75+
assert!(((d.value) == (3)));
76+
let mut called: list_head = <list_head>::default();
77+
called = list_head {
78+
next: (unsafe { init_0((&mut called as *mut list_head)) }),
79+
};
80+
assert!(((called.next) == (&mut called as *mut list_head)));
81+
let mut size: usize = ::std::mem::size_of::<usize>();
82+
assert!(((size) == (::std::mem::size_of::<usize>())));
83+
return 0;
84+
}
85+
pub unsafe fn __cpp2rust_init_globals() {}

0 commit comments

Comments
 (0)