Skip to content
Draft
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
21 changes: 19 additions & 2 deletions c2rust-ast-builder/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use syn::{__private::ToTokens, punctuated::Punctuated, *};

pub mod properties {
use proc_macro2::Span;
use syn::{StaticMutability, Token};
use syn::{PointerMutability, StaticMutability, Token};

pub trait ToToken {
type Token;
Expand All @@ -34,6 +34,13 @@ pub mod properties {
}

impl Mutability {
pub fn to_pointer_mutability(&self, span: Span) -> PointerMutability {
match self {
Mutability::Mutable => PointerMutability::Mut(Token![mut](span)),
Mutability::Immutable => PointerMutability::Const(Token![const](span)),
}
}

pub fn to_static_mutability(&self, span: Span) -> StaticMutability {
match self {
Mutability::Mutable => StaticMutability::Mut(Token![mut](span)),
Expand Down Expand Up @@ -826,7 +833,7 @@ impl Builder {
self.path_expr(vec![name])
}

pub fn addr_of_expr(self, e: Box<Expr>) -> Box<Expr> {
pub fn borrow_expr(self, e: Box<Expr>) -> Box<Expr> {
Box::new(parenthesize_if_necessary(Expr::Reference(ExprReference {
attrs: self.attrs,
and_token: Token![&](self.span),
Expand All @@ -835,6 +842,16 @@ impl Builder {
})))
}

pub fn raw_borrow_expr(self, e: Box<Expr>) -> Box<Expr> {
Box::new(parenthesize_if_necessary(Expr::RawAddr(ExprRawAddr {
attrs: self.attrs,
and_token: Token![&](self.span),
raw: Token![raw](self.span),
mutability: self.mutbl.to_pointer_mutability(self.span),
expr: e,
})))
}

pub fn mac_expr(self, mac: Macro) -> Box<Expr> {
Box::new(Expr::Macro(ExprMacro {
attrs: self.attrs,
Expand Down
1 change: 1 addition & 0 deletions c2rust-transpile/src/rust_ast/set_span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ impl SetSpan for Expr {
RangeLimits::Closed(mut r) => r.spans[0] = s,
RangeLimits::HalfOpen(mut r) => r.spans[0] = s,
},
Expr::RawAddr(e) => e.and_token.span = s,
Expr::Reference(e) => e.and_token.span = s,
Expr::Return(e) => e.return_token.span = s,
Expr::Try(e) => e.question_token.span = s,
Expand Down
6 changes: 3 additions & 3 deletions c2rust-transpile/src/translator/assembly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,7 @@ impl<'c> Translation<'c> {
// c2rust-ast-exporter added it (there's no gcc equivalent);
// in this case, we need to do what clang does and pass in
// the operand by-address instead of by-value
out_expr = mk().mutbl().addr_of_expr(out_expr);
out_expr = mk().mutbl().borrow_expr(out_expr);
}

if let Some(_tied_operand) = tied_operands.get(&(output_idx, true)) {
Expand All @@ -900,7 +900,7 @@ impl<'c> Translation<'c> {
let output_local = mk().local(
mk().ident_pat(&output_name),
None,
Some(mk().mutbl().addr_of_expr(out_expr)),
Some(mk().mutbl().borrow_expr(out_expr)),
);
stmts.push(mk().local_stmt(Box::new(output_local)));

Expand All @@ -924,7 +924,7 @@ impl<'c> Translation<'c> {
let mut in_expr = in_expr.into_value();

if operand.mem_only {
in_expr = mk().addr_of_expr(in_expr);
in_expr = mk().borrow_expr(in_expr);
}
if let Some(tied_operand) = tied_operands.get(&(input_idx, false)) {
self.use_crate(ExternCrate::C2RustAsmCasts);
Expand Down
105 changes: 56 additions & 49 deletions c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3032,7 +3032,7 @@ impl<'c> Translation<'c> {
}
}
_ => {
let addr_lhs = mk().set_mutbl(mutbl).addr_of_expr(lhs);
let addr_lhs = mk().set_mutbl(mutbl).borrow_expr(lhs);

let lhs_type = self.convert_type(lhs_type.ctype)?;
let ty = mk().set_mutbl(mutbl).ptr_ty(lhs_type);
Expand Down Expand Up @@ -4595,60 +4595,67 @@ impl<'c> Translation<'c> {
.is_some()
})
.unwrap_or(false);
match expr_kind {
Some(&CExprKind::Literal(_, CLiteral::String(ref bytes, 1)))
if is_const && !translate_as_macro =>
{
let target_ty = self.convert_type(target_cty.ctype)?;

let mut bytes = bytes.to_owned();
bytes.push(0);
let byte_literal = mk().lit_expr(bytes);
let val =
mk().cast_expr(byte_literal, mk().ptr_ty(mk().path_ty(vec!["u8"])));
let val = mk().cast_expr(val, target_ty);
Ok(WithStmts::new_val(val))
}
_ => {
// Variable length arrays are already represented as pointers.
if let CTypeKind::VariableArray(..) = source_ty_kind {
Ok(val)
} else {
let method = if is_const || ctx.is_static {
"as_ptr"
} else {
"as_mut_ptr"
};
if let (Some(&CExprKind::Literal(_, CLiteral::String(ref bytes, 1))), true, false) =
(expr_kind, is_const, translate_as_macro)
{
let target_ty = self.convert_type(target_cty.ctype)?;

let mut bytes = bytes.to_owned();
bytes.push(0);
let byte_literal = mk().lit_expr(bytes);
let val = mk().cast_expr(byte_literal, mk().ptr_ty(mk().path_ty(vec!["u8"])));
let val = mk().cast_expr(val, target_ty);
return Ok(WithStmts::new_val(val));
}

let call = val.map(|x| mk().method_call_expr(x, method, vec![]));
// Variable length arrays are already represented as pointers.
if let CTypeKind::VariableArray(..) = source_ty_kind {
return Ok(val);
}

// If the target pointee type is different from the source element type,
// then we need to cast the ptr type as well.
let call = match source_ty_kind.element_ty() {
None => call,
Some(source_element_ty) if source_element_ty == pointee.ctype => {
call
}
Some(_) => {
let target_ty = self.convert_type(target_cty.ctype)?;
call.map(|ptr| mk().cast_expr(ptr, target_ty))
}
};
let (mutbl, must_cast_mut) = if is_const {
(Mutability::Immutable, false)
} else if ctx.is_static {
// TODO: The currently used nightly doesn't allow `&raw mut` in
// static initialisers, but it's allowed since version 1.83.
// So we take a `&raw const` and then cast.
// Remove `must_cast_mut` variable when the version is updated.
(Mutability::Immutable, true)
} else {
(Mutability::Mutable, false)
};

// Static arrays can now use as_ptr. Can also cast that const ptr to a
// mutable pointer as we do here:
if ctx.is_static && !is_const {
return Ok(call.map(|val| {
let inferred_type = mk().infer_ty();
let ptr_type = mk().mutbl().ptr_ty(inferred_type);
mk().cast_expr(val, ptr_type)
}));
}
val.result_map(|mut val| {
if translate_as_macro {
// Values that translate into temporaries can't be raw-borrowed in Rust,
// and must be regular-borrowed first.
// Borrowing in a static/const context will extend the lifetime to static.
let method = match mutbl {
Mutability::Mutable => "as_mut_ptr",
Mutability::Immutable => "as_ptr",
};
val = mk().method_call_expr(val, method, vec![]);
} else {
self.use_feature("raw_ref_op");
val = mk().set_mutbl(mutbl).raw_borrow_expr(val);
// TODO: Add call to `ptr::as_[mut]_ptr` once that is available
// (`array_ptr_get` feature added to nightly in January 2024)
}

Ok(call)
}
// If the target pointee type is different from the source element type,
// then we need to cast the ptr type as well.
// TODO: Remove `!translate_as_macro` when `ptr::as_[mut]_ptr` is added above.
if source_ty_kind.element_ty() != Some(pointee.ctype)
|| must_cast_mut
|| !translate_as_macro
{
let target_element_ty = self.convert_type(target_cty.ctype)?;
val = mk().cast_expr(val, target_element_ty);
}
}

Ok(val)
})
}

CastKind::NullToPointer => {
Expand Down
107 changes: 66 additions & 41 deletions c2rust-transpile/src/translator/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -938,54 +938,79 @@ impl<'c> Translation<'c> {
// In this translation, there are only pointers to functions and
// & becomes a no-op when applied to a function.

let arg = self.convert_expr(ctx.used().set_needs_address(true), arg, None)?;
let val = self.convert_expr(ctx.used().set_needs_address(true), arg, None)?;

if self.ast_context.is_function_pointer(ctype) {
Ok(arg.map(|x| mk().call_expr(mk().ident_expr("Some"), vec![x])))
return Ok(val.map(|x| mk().call_expr(mk().ident_expr("Some"), vec![x])));
}

let pointee_ty =
self.ast_context
.get_pointee_qual_type(ctype)
.ok_or_else(|| {
TranslationError::generic("Address-of should return a pointer")
})?;

let translate_as_macro = self
.convert_const_macro_expansion(ctx, arg, None)
.ok()
.flatten()
.is_some();

let (mutbl, must_cast_mut) = if pointee_ty.qualifiers.is_const {
(Mutability::Immutable, false)
} else if ctx.is_static {
// TODO: The currently used nightly doesn't allow `&raw mut` in
// static initialisers, but it's allowed since version 1.83.
// So we take a `&raw const` and then cast.
// Remove `must_cast_mut` variable when the version is updated.
(Mutability::Immutable, true)
} else {
let pointee_ty =
self.ast_context
.get_pointee_qual_type(ctype)
.ok_or_else(|| {
TranslationError::generic("Address-of should return a pointer")
})?;

let mutbl = if pointee_ty.qualifiers.is_const {
Mutability::Immutable
} else {
Mutability::Mutable
};
(Mutability::Mutable, false)
};

arg.result_map(|a| {
let mut addr_of_arg: Box<Expr>;

if ctx.is_static {
// static variable initializers aren't able to use &mut,
// so we work around that by using & and an extra cast
// through & to *const to *mut
addr_of_arg = mk().addr_of_expr(a);
if let Mutability::Mutable = mutbl {
let mut qtype = pointee_ty;
qtype.qualifiers.is_const = true;
let ty_ = self
.type_converter
.borrow_mut()
.convert_pointer(&self.ast_context, qtype)?;
addr_of_arg = mk().cast_expr(addr_of_arg, ty_);
}
} else {
// Normal case is allowed to use &mut if needed
addr_of_arg = mk().set_mutbl(mutbl).addr_of_expr(a);
val.result_map(|mut val| {
if translate_as_macro
|| ctx.is_static
&& matches!(
arg_kind,
CExprKind::Literal(_, _) | CExprKind::CompoundLiteral(_, _)
)
{
// Values that translate into temporaries can't be raw-borrowed in Rust,
// and must be regular-borrowed first.
// Borrowing in a static/const context will extend the lifetime to static.
val = mk().set_mutbl(mutbl).borrow_expr(val);

// Immutable references can't be cast directly to mutable pointers, so
// add an intermediate cast to const pointer.
if must_cast_mut {
let mut const_pointee_ty = pointee_ty;
const_pointee_ty.qualifiers.is_const = true;
let const_ty = self
.type_converter
.borrow_mut()
.convert_pointer(&self.ast_context, const_pointee_ty)?;
val = mk().cast_expr(val, const_ty);
}

// Avoid unnecessary reference to pointer decay in fn call args:
if ctx.decay_ref.is_no() {
return Ok(addr_of_arg);
}
// Cast to the final pointer.
// Avoid unnecessary reference to pointer decay in fn call args.
// TODO: use `ptr::from_ref` and `ptr::from_mut` once stable (Rust 1.76).
if ctx.decay_ref.is_yes() || must_cast_mut {
val = mk().cast_expr(val, ty);
}
} else {
self.use_feature("raw_ref_op");
val = mk().set_mutbl(mutbl).raw_borrow_expr(val);

Ok(mk().cast_expr(addr_of_arg, ty))
})
}
if must_cast_mut {
val = mk().cast_expr(val, ty);
}
}

Ok(val)
})
}
c_ast::UnOp::PreIncrement => self.convert_pre_increment(ctx, cqual_type, true, arg),
c_ast::UnOp::PreDecrement => self.convert_pre_increment(ctx, cqual_type, false, arg),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ input_file: c2rust-transpile/tests/snapshots/arch-specific/vm_x86.c
unused_assignments,
unused_mut
)]
#![feature(raw_ref_op)]
#[derive(Copy, Clone)]
#[repr(C)]
pub struct vm_t {
Expand Down Expand Up @@ -42,17 +43,18 @@ pub unsafe extern "C" fn VM_CallCompiled(
programStack -= 8 as core::ffi::c_int + 4 as core::ffi::c_int * MAX_VMMAIN_ARGS;
arg = 0 as core::ffi::c_int;
while arg < MAX_VMMAIN_ARGS {
*(&mut *image
*(&raw mut *image
.offset((programStack + 8 as core::ffi::c_int + arg * 4 as core::ffi::c_int) as isize)
as *mut byte as *mut core::ffi::c_int) = *args.offset(arg as isize);
as *mut core::ffi::c_int) = *args.offset(arg as isize);
arg += 1;
}
*(&mut *image.offset((programStack + 4 as core::ffi::c_int) as isize) as *mut byte
*(&raw mut *image.offset((programStack + 4 as core::ffi::c_int) as isize)
as *mut core::ffi::c_int) = 0 as core::ffi::c_int;
*(&mut *image.offset(programStack as isize) as *mut byte as *mut core::ffi::c_int) =
*(&raw mut *image.offset(programStack as isize) as *mut core::ffi::c_int) =
-(1 as core::ffi::c_int);
entryPoint = ((*vm).codeBase).offset((*vm).entryOfs as isize);
opStack = (stack.as_mut_ptr() as *mut core::ffi::c_int).offset(16 as core::ffi::c_int as isize);
opStack = (&raw mut stack as *mut byte as *mut core::ffi::c_int)
.offset(16 as core::ffi::c_int as isize);
*opStack = 0 as core::ffi::c_int;
opStackOfs = 0 as core::ffi::c_int;
if opStackOfs != 1 as core::ffi::c_int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ input_file: c2rust-transpile/tests/snapshots/arch-specific/vm_x86.c
unused_assignments,
unused_mut
)]
#![feature(asm)]
#![feature(asm, raw_ref_op)]
use core::arch::asm;
#[derive(Copy, Clone)]
#[repr(C)]
Expand Down Expand Up @@ -44,17 +44,18 @@ pub unsafe extern "C" fn VM_CallCompiled(
programStack -= 8 as core::ffi::c_int + 4 as core::ffi::c_int * MAX_VMMAIN_ARGS;
arg = 0 as core::ffi::c_int;
while arg < MAX_VMMAIN_ARGS {
*(&mut *image
*(&raw mut *image
.offset((programStack + 8 as core::ffi::c_int + arg * 4 as core::ffi::c_int) as isize)
as *mut byte as *mut core::ffi::c_int) = *args.offset(arg as isize);
as *mut core::ffi::c_int) = *args.offset(arg as isize);
arg += 1;
}
*(&mut *image.offset((programStack + 4 as core::ffi::c_int) as isize) as *mut byte
*(&raw mut *image.offset((programStack + 4 as core::ffi::c_int) as isize)
as *mut core::ffi::c_int) = 0 as core::ffi::c_int;
*(&mut *image.offset(programStack as isize) as *mut byte as *mut core::ffi::c_int) =
*(&raw mut *image.offset(programStack as isize) as *mut core::ffi::c_int) =
-(1 as core::ffi::c_int);
entryPoint = ((*vm).codeBase).offset((*vm).entryOfs as isize);
opStack = (stack.as_mut_ptr() as *mut core::ffi::c_int).offset(16 as core::ffi::c_int as isize);
opStack = (&raw mut stack as *mut byte as *mut core::ffi::c_int)
.offset(16 as core::ffi::c_int as isize);
*opStack = 0 as core::ffi::c_int;
opStackOfs = 0 as core::ffi::c_int;
asm!(
Expand Down
Loading
Loading