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
47 changes: 29 additions & 18 deletions c2rust-transpile/src/translator/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,31 +151,42 @@ impl<'c> Translation<'c> {
Ok(WithStmts::new_val(val))
}

CLiteral::String(ref val, width) => {
let mut val = val.to_owned();
let num_elems = match self.ast_context.resolve_type(ty.ctype).kind {
CTypeKind::ConstantArray(_elem_ty, num_elems) => num_elems,
ref kind => {
panic!("String literal with unknown size: {val:?}, kind = {kind:?}")
}
};

// Match the literal size to the expected size padding with zeros as needed
let size = num_elems * (width as usize);
val.resize(size, 0);
CLiteral::String(ref bytes, element_size) => {
let bytes_padded = self.string_literal_bytes(ty.ctype, bytes, element_size);

// std::mem::transmute::<[u8; size], ctype>(*b"xxxx")
let u8_ty = mk().path_ty(vec!["u8"]);
let width_lit = mk().lit_expr(mk().int_unsuffixed_lit(val.len() as u128));
Ok(WithStmts::new_unsafe_val(transmute_expr(
mk().array_ty(u8_ty, width_lit),
let array_ty = mk().array_ty(
mk().ident_ty("u8"),
mk().lit_expr(bytes_padded.len() as u128),
);
let val = transmute_expr(
array_ty,
self.convert_type(ty.ctype)?,
mk().unary_expr(UnOp::Deref(Default::default()), mk().lit_expr(val)),
)))
mk().unary_expr(UnOp::Deref(Default::default()), mk().lit_expr(bytes_padded)),
);
Ok(WithStmts::new_unsafe_val(val))
}
}
}

/// Returns the bytes of a string literal, including any additional zero bytes to pad the
/// literal to the expected size.
pub fn string_literal_bytes(&self, ctype: CTypeId, bytes: &[u8], element_size: u8) -> Vec<u8> {
let num_elems = match self.ast_context.resolve_type(ctype).kind {
CTypeKind::ConstantArray(_, num_elems) => num_elems,
ref kind => {
panic!("String literal with unknown size: {bytes:?}, kind = {kind:?}")
}
};

let size = num_elems * (element_size as usize);
let mut bytes_padded = Vec::with_capacity(size);
bytes_padded.extend(bytes);
bytes_padded.resize(size, 0);

bytes_padded
}

/// Convert an initialization list into an expression. These initialization lists can be
/// used as array literals, struct literals, and union literals in code.
pub fn convert_init_list(
Expand Down
175 changes: 102 additions & 73 deletions c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4708,80 +4708,12 @@ impl<'c> Translation<'c> {
return Ok(val);
}

let pointee = self
.ast_context
.get_pointee_qual_type(target_cty.ctype)
.unwrap_or_else(|| panic!("dereferencing a non-pointer"));

let is_const = pointee.qualifiers.is_const;

// Handle literals by looking at the next level of expr nesting. Avoid doing this
// for expressions that will be translated as const macros, because emitting the
// name of the const macro only occurs if we process the expr_id with a direct call
// to `convert_expr`.
let expr_kind = expr.map(|e| &self.ast_context.index(e).kind);
let translate_as_macro = expr
.map(|e| {
self.convert_const_macro_expansion(ctx, e, None)
.ok()
.flatten()
.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"
};

let call = val.map(|x| mk().method_call_expr(x, method, vec![]));

// 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))
}
};

// 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)
}));
}

Ok(call)
}
}
// Variable length arrays are already represented as pointers.
if let CTypeKind::VariableArray(..) = source_ty_kind {
return Ok(val);
}

self.convert_address_of(ctx, expr, source_cty, target_cty, val, true)
}

CastKind::NullToPointer => {
Expand Down Expand Up @@ -4941,6 +4873,103 @@ impl<'c> Translation<'c> {
val.map(|x| mk().cast_expr(x, target_ty))
}

pub fn convert_address_of(
&self,
ctx: ExprContext,
arg: Option<CExprId>,
arg_cty: CQualTypeId,
pointer_cty: CQualTypeId,
mut val: WithStmts<Box<Expr>>,
is_array_decay: bool,
) -> TranslationResult<WithStmts<Box<Expr>>> {
let arg_expr_kind = arg.map(|arg| &self.ast_context.index(arg).kind);
let pointee_cty = self
.ast_context
.get_pointee_qual_type(pointer_cty.ctype)
.ok_or_else(|| TranslationError::generic("Address-of should return a pointer"))?;
let arg_is_macro = arg.map_or(false, |arg| {
matches!(
self.convert_const_macro_expansion(ctx, arg, None),
Ok(Some(_))
)
});

let mut needs_cast = false;
let mut ref_cast_pointee_ty = None;
let mutbl = if pointee_cty.qualifiers.is_const {
Mutability::Immutable
} else 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
// TODO: Rust 1.83: Allowed, so this can be removed.
needs_cast = true;
Mutability::Immutable
} else {
Mutability::Mutable
};

// String literals are translated with a transmute, which produces a temporary.
// Taking the address of a temporary leaves a dangling pointer. So instead,
// cast the string literal directly so that its 'static lifetime is preserved.
if let (
Some(&CExprKind::Literal(literal_cty, CLiteral::String(ref bytes, element_size @ 1))),
false,
) = (arg_expr_kind, arg_is_macro)
{
let bytes_padded = self.string_literal_bytes(literal_cty.ctype, bytes, element_size);
let len = bytes_padded.len();
val = WithStmts::new_val(mk().lit_expr(bytes_padded));

if is_array_decay {
ref_cast_pointee_ty = Some(mk().ident_ty("u8"));
} else {
ref_cast_pointee_ty =
Some(mk().array_ty(mk().ident_ty("u8"), mk().lit_expr(len as u128)));
}
needs_cast = true;
} else {
let arg_cty_kind = &self.ast_context.resolve_type(arg_cty.ctype).kind;

if is_array_decay {
let method = match mutbl {
Mutability::Mutable => "as_mut_ptr",
Mutability::Immutable => "as_ptr",
};
val = val.map(|val| mk().method_call_expr(val, method, vec![]));

// If the target pointee type is different from the source element type,
// then we need to cast the ptr type as well.
if arg_cty_kind.element_ty().map_or(false, |arg_element_cty| {
arg_element_cty != pointee_cty.ctype
}) {
needs_cast = true;
}
} else {
val = val.map(|val| mk().set_mutbl(mutbl).addr_of_expr(val));

// Add an intermediate reference-to-pointer cast if the context needs
// reference-to-pointer decay, or if another cast follows.
if ctx.decay_ref.is_yes() || needs_cast {
ref_cast_pointee_ty = Some(self.convert_pointee_type(arg_cty.ctype)?);
}
}
}

// Perform an intermediate reference-to-pointer cast if needed.
// TODO: Rust 1.76: Use `ptr::from_ref`.
if let Some(pointee_ty) = ref_cast_pointee_ty {
val = val.map(|val| mk().cast_expr(val, mk().set_mutbl(mutbl).ptr_ty(pointee_ty)));
}

// Perform a final cast to the target type if needed.
if needs_cast {
let pointer_ty = self.convert_type(pointer_cty.ctype)?;
val = val.map(|val| mk().cast_expr(val, pointer_ty));
}

Ok(val)
}

pub fn implicit_default_expr(
&self,
ty_id: CTypeId,
Expand Down
102 changes: 12 additions & 90 deletions c2rust-transpile/src/translator/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,6 @@ impl<'c> Translation<'c> {
lrvalue: LRValue,
) -> TranslationResult<WithStmts<Box<Expr>>> {
let CQualTypeId { ctype, .. } = cqual_type;
let ty = self.convert_type(ctype)?;
let resolved_ctype = self.ast_context.resolve_type(ctype);

let mut unary = match name {
Expand All @@ -927,104 +926,27 @@ impl<'c> Translation<'c> {
CExprKind::Unary(_, c_ast::UnOp::Deref, target, _) => {
return self.convert_expr(ctx, *target, None)
}
// An AddrOf DeclRef/Member is safe to not decay if the translator isn't already giving a hard
// yes to decaying (ie, BitCasts). So we only convert default to no decay.
// An AddrOf DeclRef/Member is safe to not decay
// if the translator isn't already giving a hard yes to decaying (ie, BitCasts).
// So we only convert default to no decay.
CExprKind::DeclRef(..) | CExprKind::Member(..) => {
ctx.decay_ref.set_default_to_no()
}
_ => (),
};

// In this translation, there are only pointers to functions and
// & becomes a no-op when applied to a function.
}

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

if self.ast_context.is_function_pointer(ctype) {
Ok(val.map(|x| mk().call_expr(mk().ident_expr("Some"), vec![x])))
} else {
let pointee_ty =
self.ast_context
.get_pointee_qual_type(ctype)
.ok_or_else(|| {
TranslationError::generic("Address-of should return a pointer")
})?;

let expr_kind = &self.ast_context.index(arg).kind;
let translate_as_macro = self
.convert_const_macro_expansion(ctx, arg, None)
.ok()
.flatten()
.is_some();

// String literals are translated with a transmute, which produces a temporary.
// Taking the address of a temporary leaves a dangling pointer. So instead,
// cast the string literal directly so that its 'static lifetime is preserved.
if let (
&CExprKind::Literal(
literal_cqual_type,
CLiteral::String(ref bytes, element_size @ 1),
),
false,
) = (expr_kind, translate_as_macro)
{
let num_elems =
match self.ast_context.resolve_type(literal_cqual_type.ctype).kind {
CTypeKind::ConstantArray(_, num_elems) => num_elems,
ref kind => {
panic!(
"String literal with unknown size: {bytes:?}, kind = {kind:?}"
)
}
};

// Match the literal size to the expected size padding with zeros as needed
let size = num_elems * (element_size as usize);
let mut bytes_padded = Vec::with_capacity(size);
bytes_padded.extend(bytes);
bytes_padded.resize(size, 0);

let array_ty = mk().array_ty(
mk().ident_ty("u8"),
mk().lit_expr(bytes_padded.len() as u128),
);
let bytes_literal = mk().lit_expr(bytes_padded);
let val = mk().cast_expr(bytes_literal, mk().ptr_ty(array_ty));
let val = mk().cast_expr(val, ty);
return Ok(WithStmts::new_val(val));
}

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

val.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 ty_ = self.convert_pointee_type(pointee_ty.ctype)?;
addr_of_arg = mk().cast_expr(addr_of_arg, mk().ptr_ty(ty_));
}
} else {
// Normal case is allowed to use &mut if needed
addr_of_arg = mk().set_mutbl(mutbl).addr_of_expr(a);
// & becomes a no-op when applied to a function.
if self.ast_context.is_function_pointer(cqual_type.ctype) {
return Ok(val.map(|x| mk().call_expr(mk().ident_expr("Some"), vec![x])));
}

// Avoid unnecessary reference to pointer decay in fn call args:
if ctx.decay_ref.is_no() {
return Ok(addr_of_arg);
}
}
let arg_cty = arg_kind
.get_qual_type()
.ok_or_else(|| format_err!("bad source type"))?;

Ok(mk().cast_expr(addr_of_arg, ty))
})
}
self.convert_address_of(ctx, Some(arg), arg_cty, cqual_type, val, false)
}
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
Loading