Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,25 @@ void Converter::ConvertVarDecl(clang::VarDecl *decl) {
}

HoistMaterializedTempBindings hoist_temps(*this);
if (decl->isLocalVarDecl() && !IsGlobalVar(decl) && decl->hasInit() &&
ReferencesVar(decl->getInit(), decl)) {
if (decl->getType()->isReferenceType()) {
llvm::report_fatal_error(
"self-referential initializer of a reference is not supported");
}
if (DefaultInitHasSideEffects(ctx_, decl->getType())) {
llvm::report_fatal_error("self-referential initializer of a variable "
"whose default initialization has side effects "
"is not supported");
}
hoisted_decls_.insert(decl);
if (ConvertVarDeclSkipInit(decl)) {
StrCat(token::kAssign, ConvertVarDefaultInit(decl), token::kSemiColon);
}
EmitHoistedInArmAssignment(decl);
return;
}

if (!ConvertVarDeclSkipInit(decl)) {
// Skip global variables declared extern
return;
Expand Down Expand Up @@ -591,6 +610,10 @@ bool Converter::VisitVarDecl(clang::VarDecl *decl) {
}

if (IsGlobalVar(decl)) {
if (decl->hasInit() && ReferencesVar(decl->getInit(), decl)) {
llvm::report_fatal_error(
"self-referential initializer of a static variable is not supported");
}
ConvertGlobalVarDecl(decl);
} else {
ConvertVarDecl(decl);
Expand Down
42 changes: 42 additions & 0 deletions cpp2rust/converter/converter_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1579,6 +1579,48 @@ GetAllVars(const clang::Stmt *stmt) {
return vars;
}

bool ReferencesVar(const clang::Stmt *stmt, const clang::VarDecl *var) {
if (!stmt) {
return false;
}
if (auto *decl_ref = clang::dyn_cast<clang::DeclRefExpr>(stmt)) {
return decl_ref->getDecl() == var;
}
if (clang::isa<clang::UnaryExprOrTypeTraitExpr>(stmt)) {
return false;
}
return std::ranges::any_of(stmt->children(), [var](const clang::Stmt *child) {
return ReferencesVar(child, var);
});
}

bool DefaultInitHasSideEffects(const clang::ASTContext &ctx,
clang::QualType type) {
auto *record = ctx.getBaseElementType(type)->getAsCXXRecordDecl();
if (!record || record->hasTrivialDefaultConstructor()) {
return false;
}
if (!record->hasDefaultConstructor() ||
record->hasUserProvidedDefaultConstructor()) {
return true;
}
for (const auto &base : record->bases()) {
if (DefaultInitHasSideEffects(ctx, base.getType())) {
return true;
}
}
for (auto *field : record->fields()) {
if (auto *init = field->getInClassInitializer()) {
if (init->HasSideEffects(ctx)) {
return true;
}
} else if (DefaultInitHasSideEffects(ctx, field->getType())) {
return true;
}
}
return false;
}

bool ReadsMemory(const clang::Stmt *stmt) {
if (auto *cast = clang::dyn_cast<clang::ImplicitCastExpr>(stmt);
cast && cast->getCastKind() == clang::CK_LValueToRValue) {
Expand Down
5 changes: 5 additions & 0 deletions cpp2rust/converter/converter_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,11 @@ clang::Expr *GetCallee(clang::CallExpr *expr);
std::unordered_set<const clang::ValueDecl *>
GetAllVars(const clang::Stmt *stmt);

bool ReferencesVar(const clang::Stmt *stmt, const clang::VarDecl *var);

bool DefaultInitHasSideEffects(const clang::ASTContext &ctx,
clang::QualType type);

bool ReferencesThis(const clang::Stmt *stmt);

// Whether evaluating `stmt` reads a value from memory, e.g., a pointer.
Expand Down
28 changes: 28 additions & 0 deletions tests/ub/out/refcount/self_referencing_initializer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
extern crate libcc2rs;
use libcc2rs::*;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::io::prelude::*;
use std::io::{Read, Seek, Write};
use std::os::fd::AsFd;
use std::rc::{Rc, Weak};
#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)]
#[byte_size(8)]
pub struct pair {
#[offset(0)]
pub a: i32,
#[offset(4)]
pub b: i32,
}
pub fn main() {
__cpp2rust_init_globals();
std::process::exit(main_0());
}
fn main_0() -> i32 {
let mut x: i32 = 0_i32;
x = (x + 1);
let mut p: pair = <pair>::default();
p = pair { a: 1, b: (p.a + 1) };
return (x + p.b);
}
pub fn __cpp2rust_init_globals() {}
31 changes: 31 additions & 0 deletions tests/ub/out/unsafe/self_referencing_initializer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
extern crate libc;
use libc::*;
extern crate libcc2rs;
use libcc2rs::*;
use std::collections::BTreeMap;
use std::io::{Read, Seek, Write};
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
use std::rc::Rc;
#[repr(C)]
#[derive(Copy, Clone, VaArg, FnPtrArg, Default)]
pub struct pair {
pub a: i32,
pub b: i32,
}
pub fn main() {
unsafe {
__cpp2rust_init_globals();
std::process::exit(main_0() as i32);
}
}
unsafe fn main_0() -> i32 {
let mut x: i32 = 0_i32;
x = ((x) + (1));
let mut p: pair = <pair>::default();
p = pair {
a: 1,
b: ((p.a) + (1)),
};
return ((x) + (p.b));
}
pub unsafe fn __cpp2rust_init_globals() {}
12 changes: 12 additions & 0 deletions tests/ub/self_referencing_initializer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// nondet-result

struct pair {
int a;
int b;
};

int main() {
int x = x + 1;
pair p = {1, p.a + 1};
return x + p.b;
}
103 changes: 103 additions & 0 deletions tests/unit/out/refcount/self_referencing_initializer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
extern crate libcc2rs;
use libcc2rs::*;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::io::prelude::*;
use std::io::{Read, Seek, Write};
use std::os::fd::AsFd;
use std::rc::{Rc, Weak};
#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)]
#[byte_size(8)]
pub struct list_head {
#[offset(0)]
#[byte_size(8)]
pub next: Ptr<list_head>,
}
#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)]
#[byte_size(16)]
pub struct node {
#[offset(0)]
pub value: i32,
#[offset(8)]
#[byte_size(8)]
pub value_ptr: Ptr<i32>,
}
#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg)]
#[byte_size(16)]
pub struct node_with_defaults {
#[offset(0)]
#[byte_size(8)]
pub next: Ptr<node_with_defaults>,
#[offset(8)]
pub value: i32,
}
impl Default for node_with_defaults {
fn default() -> Self {
node_with_defaults {
next: Ptr::<node_with_defaults>::null(),
value: 3,
}
}
}
pub fn init_0(mut l: Ptr<list_head>) -> Ptr<list_head> {
field!(l, next).write(Ptr::<list_head>::null());
return l;
}
pub fn main() {
__cpp2rust_init_globals();
std::process::exit(main_0());
}
fn main_0() -> i32 {
let list: Value<list_head> = <Value<list_head>>::default();
*list.borrow_mut() = list_head {
next: (list.as_pointer()),
};
assert!(({ { (*list.borrow()).next.clone() } } == { (list.as_pointer()) }));
let n: Value<node> = <Value<node>>::default();
*n.borrow_mut() = node {
value: 42,
value_ptr: (field_ptr!(n.as_pointer(), value)),
};
assert!(({ { (*n.borrow()).value_ptr.clone() } } == { (field_ptr!(n.as_pointer(), value)) }));
{ (*n.borrow()).value_ptr.clone() }.write(7);
assert!(({ (*n.borrow()).value } == 7));
let arr: Value<Box<[list_head]>> = Rc::new(RefCell::new(
(0..2)
.map(|_| <list_head>::default())
.collect::<Box<[list_head]>>(),
));
*arr.borrow_mut() = Box::new([
list_head {
next: ((arr.as_pointer() as Ptr<list_head>).offset(1)),
},
list_head {
next: ((arr.as_pointer() as Ptr<list_head>).offset(0)),
},
]);
assert!(
({ { (*arr.borrow())[(0) as usize].next.clone() } } == {
((arr.as_pointer() as Ptr<list_head>).offset(1))
})
);
assert!(
({ { (*arr.borrow())[(1) as usize].next.clone() } } == {
((arr.as_pointer() as Ptr<list_head>).offset(0))
})
);
let d: Value<node_with_defaults> = <Value<node_with_defaults>>::default();
*d.borrow_mut() = node_with_defaults {
next: (d.as_pointer()),
value: 3,
};
assert!(({ { (*d.borrow()).next.clone() } } == { (d.as_pointer()) }));
assert!(({ (*d.borrow()).value } == 3));
let called: Value<list_head> = <Value<list_head>>::default();
*called.borrow_mut() = list_head {
next: ({ init_0((called.as_pointer())) }),
};
assert!(({ { (*called.borrow()).next.clone() } } == { (called.as_pointer()) }));
let mut size: usize = ::std::mem::size_of::<usize>();
assert!((size == ::std::mem::size_of::<usize>()));
return 0;
}
pub fn __cpp2rust_init_globals() {}
85 changes: 85 additions & 0 deletions tests/unit/out/unsafe/self_referencing_initializer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
extern crate libc;
use libc::*;
extern crate libcc2rs;
use libcc2rs::*;
use std::collections::BTreeMap;
use std::io::{Read, Seek, Write};
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
use std::rc::Rc;
#[repr(C)]
#[derive(Copy, Clone, VaArg, FnPtrArg, Default)]
pub struct list_head {
pub next: *mut list_head,
}
#[repr(C)]
#[derive(Copy, Clone, VaArg, FnPtrArg, Default)]
pub struct node {
pub value: i32,
pub value_ptr: *mut i32,
}
#[repr(C)]
#[derive(Copy, Clone, VaArg, FnPtrArg)]
pub struct node_with_defaults {
pub next: *mut node_with_defaults,
pub value: i32,
}
impl Default for node_with_defaults {
fn default() -> Self {
node_with_defaults {
next: std::ptr::null_mut(),
value: 3,
}
}
}
pub unsafe fn init_0(mut l: *mut list_head) -> *mut list_head {
(*l).next = std::ptr::null_mut();
return l;
}
pub fn main() {
unsafe {
__cpp2rust_init_globals();
std::process::exit(main_0() as i32);
}
}
unsafe fn main_0() -> i32 {
let mut list: list_head = <list_head>::default();
list = list_head {
next: (&mut list as *mut list_head),
};
assert!(((list.next) == (&mut list as *mut list_head)));
let mut n: node = <node>::default();
n = node {
value: 42,
value_ptr: (&mut n.value as *mut i32),
};
assert!(((n.value_ptr) == (&mut n.value as *mut i32)));
(*n.value_ptr) = 7;
assert!(((n.value) == (7)));
let mut arr: [list_head; 2] = [<list_head>::default(); 2];
arr = [
list_head {
next: (&mut arr[(1) as usize] as *mut list_head),
},
list_head {
next: (&mut arr[(0) as usize] as *mut list_head),
},
];
assert!(((arr[(0) as usize].next) == (&mut arr[(1) as usize] as *mut list_head)));
assert!(((arr[(1) as usize].next) == (&mut arr[(0) as usize] as *mut list_head)));
let mut d: node_with_defaults = <node_with_defaults>::default();
d = node_with_defaults {
next: (&mut d as *mut node_with_defaults),
value: 3,
};
assert!(((d.next) == (&mut d as *mut node_with_defaults)));
assert!(((d.value) == (3)));
let mut called: list_head = <list_head>::default();
called = list_head {
next: (unsafe { init_0((&mut called as *mut list_head)) }),
};
assert!(((called.next) == (&mut called as *mut list_head)));
let mut size: usize = ::std::mem::size_of::<usize>();
assert!(((size) == (::std::mem::size_of::<usize>())));
return 0;
}
pub unsafe fn __cpp2rust_init_globals() {}
Loading
Loading