Skip to content
Closed
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
159 changes: 136 additions & 23 deletions cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1986,15 +1986,25 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
StrCat(keyword::kAs, dest_pointee_const ? "*const u8" : "*mut u8");
break;
}
Convert(sub_expr);
if (clang::isa<clang::StringLiteral>(sub_expr) ||
clang::isa<clang::PredefinedExpr>(sub_expr)) {
StrCat(".as_ptr()");
if (!dest_pointee_const) {
StrCat(".cast_mut()");
if (IsGlobalVar(sub_expr)) {
{
PushParen paren(*this);
StrCat("&raw", dest_pointee_const ? keyword::kConst : keyword_mut_);
Convert(sub_expr);
}
StrCat(".cast::<",
GetUnsafeTypeAsString(expr->getType()->getPointeeType()), ">()");
} else {
StrCat(dest_pointee_const ? ".as_ptr()" : ".as_mut_ptr()");
Convert(sub_expr);
if (clang::isa<clang::StringLiteral>(sub_expr) ||
clang::isa<clang::PredefinedExpr>(sub_expr)) {
StrCat(".as_ptr()");
if (!dest_pointee_const) {
StrCat(".cast_mut()");
}
} else {
StrCat(dest_pointee_const ? ".as_ptr()" : ".as_mut_ptr()");
}
}
break;
}
Expand Down Expand Up @@ -2322,31 +2332,40 @@ bool Converter::IsReferenceType(const clang::Expr *expr) const {
bool Converter::ConvertIncAndDec(clang::UnaryOperator *expr) {
auto opcode = expr->getOpcode();
auto *sub_expr = expr->getSubExpr();
auto emit_target = [&]() {
if (IsGlobalVar(sub_expr)) {
StrCat("(*(&raw", keyword_mut_);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use PushParen instead

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot comply with this review.

Convert(sub_expr);
StrCat("))");
} else {
Convert(sub_expr);
}
};
switch (opcode) {
case clang::UO_PostInc: {
PushExprKind push(*this, ExprKind::RValue);
Convert(sub_expr);
emit_target();
StrCat(".postfix_inc()");
SetFresh();
return true;
}
case clang::UO_PostDec: {
PushExprKind push(*this, ExprKind::RValue);
Convert(sub_expr);
emit_target();
StrCat(".postfix_dec()");
SetFresh();
return true;
}
case clang::UO_PreInc: {
PushExprKind push(*this, ExprKind::RValue);
Convert(sub_expr);
emit_target();
StrCat(".prefix_inc()");
SetFresh();
return true;
}
case clang::UO_PreDec: {
PushExprKind push(*this, ExprKind::RValue);
Convert(sub_expr);
emit_target();
StrCat(".prefix_dec()");
SetFresh();
return true;
Expand Down Expand Up @@ -2539,8 +2558,15 @@ bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) {
}

if (!decl->getType()->getAs<clang::ReferenceType>() && isAddrOf()) {
StrCat(token::kRef, decl->getType().isConstQualified() ? "" : keyword_mut_,
str);
if (IsGlobalVar(expr)) {
StrCat("&raw",
decl->getType().isConstQualified() ? keyword::kConst
: keyword_mut_,
str);
} else {
StrCat(token::kRef,
decl->getType().isConstQualified() ? "" : keyword_mut_, str);
}
return false;
}

Expand Down Expand Up @@ -3509,14 +3535,78 @@ void Converter::ConvertUnsignedArithOperand(clang::Expr *expr,
}
}

void Converter::ConvertGlobalVarBaseSuffix(clang::Expr *expr) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not good. The logic here should go into VisitMemberExpr and VisitArraySubscriptExpr

expr = expr->IgnoreImplicit();
// Base case: this expression IS the global var root — no suffix to emit.
if (IsGlobalVar(expr)) {
return;
}
if (auto *member = clang::dyn_cast<clang::MemberExpr>(expr)) {
ConvertGlobalVarBaseSuffix(member->getBase());
StrCat(token::kDot);
StrCat(GetNamedDeclAsString(member->getMemberDecl()));
return;
}
if (auto *subscript = clang::dyn_cast<clang::ArraySubscriptExpr>(expr)) {
ConvertGlobalVarBaseSuffix(subscript->getBase());
PushBracket bracket(*this);
{
PushParen paren(*this);
Convert(subscript->getIdx());
}
StrCat(keyword::kAs, "usize");
return;
}
}

void Converter::ConvertEqualsNullPtr(clang::Expr *expr) {
StrCat('(');
Convert(expr);
if (IsUniquePtr(expr->getType()) ||
expr->getType()->isFunctionPointerType()) {
StrCat(").is_none()");
// Walk up through member accesses and array subscripts to find the root
// global var (file-scope or static-local). Any fn-ptr field access on a

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the right place to check against global variables. Prefer to propagate a flag from here to VisitVarDecl

// static mut struct requires the raw-const pattern to avoid Rust 2024's
// shared-reference-to-static-mut restriction.
clang::Expr *root = expr;
while (root) {
if (IsGlobalVar(root)) {
break;
}
auto *r = root->IgnoreImplicit();
if (auto *member = clang::dyn_cast<clang::MemberExpr>(r)) {
root = member->getBase();
continue;
}
if (auto *subscript = clang::dyn_cast<clang::ArraySubscriptExpr>(r)) {
root = subscript->getBase();
continue;
}
root = nullptr;
break;
}
if (root != nullptr) {
StrCat(keyword_unsafe_);
PushBrace unsafe_brace(*this);
{
PushParen paren(*this);
StrCat("&raw", keyword::kConst);
Convert(root->IgnoreImplicit());
}
StrCat(".as_ref().unwrap()");
ConvertGlobalVarBaseSuffix(expr);
StrCat(".is_none()");
return;
}
}

{
PushParen paren(*this);
Convert(expr);
}
if (IsUniquePtr(expr->getType()) ||
expr->getType()->isFunctionPointerType()) {
StrCat(".is_none()");
} else {
StrCat(").is_null()");
StrCat(".is_null()");
}
}

Expand Down Expand Up @@ -4084,12 +4174,35 @@ std::string Converter::ConvertIRFragment(
auto all_args = BuildUnifiedArgs(expr, args, num_args);

std::string result;
for (auto &frag : fragments) {
if (auto *t = std::get_if<TextFragment>(&frag)) {
result += t->text;
} else if (auto *g = std::get_if<GenericFragment>(&frag)) {
for (size_t i = 0; i < fragments.size(); ++i) {
const auto &frag = fragments[i];
if (const auto *t = std::get_if<TextFragment>(&frag)) {
std::string text = t->text;
// If this text fragment ends with a lone '&' (reference operator, not

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing string manipulation on the text fragments is fragile. Prefer adding new attributes in the IR if this change is necessary.

// '&&' or '&mut') and the immediately following fragment is a placeholder
// for a global variable, upgrade '&' to '&raw const' or '&raw mut' to
// comply with Rust 2024's ban on shared references to mutable statics.
if (!text.empty() && text.back() == '&' &&
(text.size() < 2 || text[text.size() - 2] != '&') &&
i + 1 < fragments.size()) {
if (const auto *next_ph =
std::get_if<PlaceholderFragment>(&fragments[i + 1])) {
auto next_idx = next_ph->n;
if (next_idx < all_args.size() && IsGlobalVar(all_args[next_idx])) {
const auto *decl_ref = clang::dyn_cast<clang::DeclRefExpr>(
all_args[next_idx]->IgnoreImplicit());
bool is_const =
decl_ref &&
decl_ref->getDecl()->getType().isConstQualified();
text.pop_back(); // remove '&'
text += is_const ? "&raw const " : std::string("&raw") + keyword_mut_;
}
}
}
result += text;
} else if (const auto *g = std::get_if<GenericFragment>(&frag)) {
result += Mapper::InstantiateTemplate(GetCalleeOrExpr(expr), g->n);
} else if (auto *ph = std::get_if<PlaceholderFragment>(&frag)) {
} else if (const auto *ph = std::get_if<PlaceholderFragment>(&frag)) {
auto arg_idx = ph->n;
assert(arg_idx < all_args.size());
auto *arg = all_args[arg_idx];
Expand All @@ -4109,7 +4222,7 @@ std::string Converter::ConvertIRFragment(
.is_index_base = ph->is_index_base,
};
result += ConvertPlaceholder(expr, arg, ph_ctx);
} else if (auto *mc =
} else if (const auto *mc =
std::get_if<std::unique_ptr<MethodCallFragment>>(&frag)) {
result += ConvertMappedMethodCall(expr, **mc, args, num_args, ctx);
}
Expand Down
2 changes: 2 additions & 0 deletions cpp2rust/converter/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

virtual void ConvertEqualsNullPtr(clang::Expr *expr);

void ConvertGlobalVarBaseSuffix(clang::Expr *expr);

virtual void ConvertPointerSubscript(clang::ArraySubscriptExpr *expr);

virtual void ConvertPointerOffset(clang::Expr *base, clang::Expr *idx,
Expand Down
2 changes: 0 additions & 2 deletions tests/lit/lit/formats/Cpp2RustTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,6 @@ def build_rust(self):
"strip=symbols",
"-C",
"panic=abort",
"-W", ## TODO: remove this once we fix the errors in the generated code
"static_mut_refs",
"--out-dir",
str(self.tmp_dir),
"-L",
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/out/unsafe/bool_condition_logical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl From<i32> for Code {
libcc2rs::impl_enum_inc_dec!(Code);
pub static mut side_effect_0: i32 = unsafe { 0 };
pub unsafe fn observe_1(mut v: i32) -> i32 {
side_effect_0.prefix_inc();
(*(&raw mut side_effect_0)).prefix_inc();
return v;
}
pub unsafe fn returns_one_2() -> i32 {
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/out/unsafe/bool_condition_logical_c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl From<i32> for Code {
libcc2rs::impl_enum_inc_dec!(Code);
pub static mut side_effect_0: i32 = unsafe { 0 };
pub unsafe fn observe_1(mut v: i32) -> i32 {
side_effect_0.prefix_inc();
(*(&raw mut side_effect_0)).prefix_inc();
return v;
}
pub unsafe fn returns_one_2() -> i32 {
Expand Down
24 changes: 16 additions & 8 deletions tests/unit/out/unsafe/default_in_statics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ pub unsafe fn check_local_static_5() {
static mut local_fn_7: Option<unsafe fn(i32) -> i32> = unsafe { None };;
static mut local_p_8: *mut i32 = unsafe { std::ptr::null_mut() };;
assert!((local_outer_6.p1).is_null());
assert!((local_outer_6.fn_).is_none());
assert!((local_fn_7).is_none());
assert!(unsafe { (&raw const local_outer_6).as_ref().unwrap().fn_.is_none() });
assert!(unsafe { (&raw const local_fn_7).as_ref().unwrap().is_none() });
assert!((local_p_8).is_null());
}
pub fn main() {
Expand All @@ -136,12 +136,12 @@ pub fn main() {
}
}
unsafe fn main_0() -> i32 {
assert!((static_fn_0).is_none());
assert!(unsafe { (&raw const static_fn_0).as_ref().unwrap().is_none() });
assert!((static_outer_1.p1).is_null());
assert!((static_outer_1.p2).is_null());
assert!((static_outer_1.cp).is_null());
assert!((static_outer_1.pp).is_null());
assert!((static_outer_1.fn_).is_none());
assert!(unsafe { (&raw const static_outer_1).as_ref().unwrap().fn_.is_none() });
let mut i: i32 = 0;
'loop_: while ((i) < (3)) {
assert!((static_outer_1.arr[(i) as usize]).is_null());
Expand All @@ -154,14 +154,22 @@ unsafe fn main_0() -> i32 {
i.prefix_inc();
}
assert!((static_foo_3.s2).is_null());
assert!((static_foo_3.fn1).is_none());
assert!((static_foo_3.fn2).is_none());
assert!(unsafe { (&raw const static_foo_3).as_ref().unwrap().fn1.is_none() });
assert!(unsafe { (&raw const static_foo_3).as_ref().unwrap().fn2.is_none() });
assert!(((static_foo_3.n) == (42)));
let mut i: i32 = 0;
'loop_: while ((i) < (2)) {
assert!((static_foo_array_4[(i) as usize].s2).is_null());
assert!((static_foo_array_4[(i) as usize].fn1).is_none());
assert!((static_foo_array_4[(i) as usize].fn2).is_none());
assert!(unsafe {
(&raw const static_foo_array_4).as_ref().unwrap()[(i) as usize]
.fn1
.is_none()
});
assert!(unsafe {
(&raw const static_foo_array_4).as_ref().unwrap()[(i) as usize]
.fn2
.is_none()
});
i.prefix_inc();
}
(unsafe { check_local_static_5() });
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/out/unsafe/fn_ptr_global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub unsafe fn set_op_3(mut fn_: Option<unsafe fn(i32) -> i32>) {
g_op_2 = fn_;
}
pub unsafe fn call_op_4(mut x: i32) -> i32 {
if !(g_op_2).is_none() {
if !unsafe { (&raw const g_op_2).as_ref().unwrap().is_none() } {
return (unsafe {
let _arg0: i32 = x;
(g_op_2).unwrap()(_arg0)
Expand All @@ -36,7 +36,7 @@ unsafe fn main_0() -> i32 {
let _fn: Option<unsafe fn(i32) -> i32> = Some(double_it_0);
set_op_3(_fn)
});
assert!(!((g_op_2).is_none()));
assert!(!(unsafe { (&raw const g_op_2).as_ref().unwrap().is_none() }));
assert!(((g_op_2) == (Some(double_it_0))));
assert!(((unsafe { call_op_4(5,) }) == (10)));
(unsafe {
Expand All @@ -49,7 +49,7 @@ unsafe fn main_0() -> i32 {
let _fn: Option<unsafe fn(i32) -> i32> = None;
set_op_3(_fn)
});
assert!((g_op_2).is_none());
assert!(unsafe { (&raw const g_op_2).as_ref().unwrap().is_none() });
assert!(((unsafe { call_op_4(5,) }) == (5)));
return 0;
}
2 changes: 1 addition & 1 deletion tests/unit/out/unsafe/union_tagged_struct_arms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ unsafe fn main_0() -> i32 {
let mut p_list: Branch = <Branch>::default();
p_list.choice = Choice_enum::C_LIST;
p_list.index = 0;
p_list.v.list.items = items_4.as_mut_ptr();
p_list.v.list.items = (&raw mut items_4).cast::<*mut u8>();
p_list.v.list.count = 3_i64;
p_list.v.list.cursor = 1_i64;
assert!(((((p_list.v.list.count) == (3_i64)) as i32) != 0));
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/out/unsafe/void_cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub unsafe fn unused_ptr_param_2(mut p: *const NonTrivial) {
}
pub static mut side_effect_counter_3: i32 = unsafe { 0 };
pub unsafe fn bump_and_return_4() -> i32 {
side_effect_counter_3.prefix_inc();
(*(&raw mut side_effect_counter_3)).prefix_inc();
return side_effect_counter_3;
}
#[repr(C)]
Expand Down
Loading