diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index fde1092b..e43025c5 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -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(sub_expr) || - clang::isa(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(sub_expr) || + clang::isa(sub_expr)) { + StrCat(".as_ptr()"); + if (!dest_pointee_const) { + StrCat(".cast_mut()"); + } + } else { + StrCat(dest_pointee_const ? ".as_ptr()" : ".as_mut_ptr()"); + } } break; } @@ -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_); + 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; @@ -2539,8 +2558,15 @@ bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) { } if (!decl->getType()->getAs() && 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; } @@ -3509,14 +3535,78 @@ void Converter::ConvertUnsignedArithOperand(clang::Expr *expr, } } +void Converter::ConvertGlobalVarBaseSuffix(clang::Expr *expr) { + 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(expr)) { + ConvertGlobalVarBaseSuffix(member->getBase()); + StrCat(token::kDot); + StrCat(GetNamedDeclAsString(member->getMemberDecl())); + return; + } + if (auto *subscript = clang::dyn_cast(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 + // 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(r)) { + root = member->getBase(); + continue; + } + if (auto *subscript = clang::dyn_cast(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()"); } } @@ -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(&frag)) { - result += t->text; - } else if (auto *g = std::get_if(&frag)) { + for (size_t i = 0; i < fragments.size(); ++i) { + const auto &frag = fragments[i]; + if (const auto *t = std::get_if(&frag)) { + std::string text = t->text; + // If this text fragment ends with a lone '&' (reference operator, not + // '&&' 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(&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( + 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(&frag)) { result += Mapper::InstantiateTemplate(GetCalleeOrExpr(expr), g->n); - } else if (auto *ph = std::get_if(&frag)) { + } else if (const auto *ph = std::get_if(&frag)) { auto arg_idx = ph->n; assert(arg_idx < all_args.size()); auto *arg = all_args[arg_idx]; @@ -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>(&frag)) { result += ConvertMappedMethodCall(expr, **mc, args, num_args, ctx); } diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 9d64ef22..e502faeb 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -476,6 +476,8 @@ class Converter : public clang::RecursiveASTVisitor { 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, diff --git a/tests/lit/lit/formats/Cpp2RustTest.py b/tests/lit/lit/formats/Cpp2RustTest.py index f13cff6b..63a07cf2 100644 --- a/tests/lit/lit/formats/Cpp2RustTest.py +++ b/tests/lit/lit/formats/Cpp2RustTest.py @@ -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", diff --git a/tests/unit/out/unsafe/bool_condition_logical.rs b/tests/unit/out/unsafe/bool_condition_logical.rs index d48fc729..b373704a 100644 --- a/tests/unit/out/unsafe/bool_condition_logical.rs +++ b/tests/unit/out/unsafe/bool_condition_logical.rs @@ -26,7 +26,7 @@ impl From 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 { diff --git a/tests/unit/out/unsafe/bool_condition_logical_c.rs b/tests/unit/out/unsafe/bool_condition_logical_c.rs index ef45ccdb..0932487e 100644 --- a/tests/unit/out/unsafe/bool_condition_logical_c.rs +++ b/tests/unit/out/unsafe/bool_condition_logical_c.rs @@ -26,7 +26,7 @@ impl From 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 { diff --git a/tests/unit/out/unsafe/default_in_statics.rs b/tests/unit/out/unsafe/default_in_statics.rs index 4694da33..8d6c3391 100644 --- a/tests/unit/out/unsafe/default_in_statics.rs +++ b/tests/unit/out/unsafe/default_in_statics.rs @@ -126,8 +126,8 @@ pub unsafe fn check_local_static_5() { static mut local_fn_7: Option 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() { @@ -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()); @@ -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() }); diff --git a/tests/unit/out/unsafe/fn_ptr_global.rs b/tests/unit/out/unsafe/fn_ptr_global.rs index e3b897b4..3e277bbf 100644 --- a/tests/unit/out/unsafe/fn_ptr_global.rs +++ b/tests/unit/out/unsafe/fn_ptr_global.rs @@ -17,7 +17,7 @@ pub unsafe fn set_op_3(mut fn_: Option 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) @@ -36,7 +36,7 @@ unsafe fn main_0() -> i32 { let _fn: Option 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 { @@ -49,7 +49,7 @@ unsafe fn main_0() -> i32 { let _fn: Option 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; } diff --git a/tests/unit/out/unsafe/union_tagged_struct_arms.rs b/tests/unit/out/unsafe/union_tagged_struct_arms.rs index 7c578e28..49dbd4d4 100644 --- a/tests/unit/out/unsafe/union_tagged_struct_arms.rs +++ b/tests/unit/out/unsafe/union_tagged_struct_arms.rs @@ -83,7 +83,7 @@ unsafe fn main_0() -> i32 { let mut p_list: 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)); diff --git a/tests/unit/out/unsafe/void_cast.rs b/tests/unit/out/unsafe/void_cast.rs index 226850b4..f1a8128f 100644 --- a/tests/unit/out/unsafe/void_cast.rs +++ b/tests/unit/out/unsafe/void_cast.rs @@ -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)]