Skip to content

Commit 5a55ba9

Browse files
committed
transpile: Factor out common function for address-of and array-decay
1 parent d7adfc4 commit 5a55ba9

File tree

5 files changed

+124
-163
lines changed

5 files changed

+124
-163
lines changed

c2rust-transpile/src/translator/mod.rs

Lines changed: 106 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -4708,84 +4708,12 @@ impl<'c> Translation<'c> {
47084708
return Ok(val);
47094709
}
47104710

4711-
let pointee = self
4712-
.ast_context
4713-
.get_pointee_qual_type(target_cty.ctype)
4714-
.unwrap_or_else(|| panic!("dereferencing a non-pointer"));
4715-
4716-
let is_const = pointee.qualifiers.is_const;
4717-
4718-
// Handle literals by looking at the next level of expr nesting. Avoid doing this
4719-
// for expressions that will be translated as const macros, because emitting the
4720-
// name of the const macro only occurs if we process the expr_id with a direct call
4721-
// to `convert_expr`.
4722-
let expr_kind = expr.map(|e| &self.ast_context.index(e).kind);
4723-
let translate_as_macro = expr
4724-
.map(|e| {
4725-
self.convert_const_macro_expansion(ctx, e, None)
4726-
.ok()
4727-
.flatten()
4728-
.is_some()
4729-
})
4730-
.unwrap_or(false);
4731-
match expr_kind {
4732-
Some(&CExprKind::Literal(
4733-
literal_cqual_type,
4734-
CLiteral::String(ref bytes, element_size @ 1),
4735-
)) if is_const && !translate_as_macro => {
4736-
let target_ty = self.convert_type(target_cty.ctype)?;
4737-
4738-
let bytes_padded = self.string_literal_bytes(
4739-
literal_cqual_type.ctype,
4740-
bytes,
4741-
element_size,
4742-
);
4743-
let element_ty = mk().ident_ty("u8");
4744-
let bytes_literal = mk().lit_expr(bytes_padded);
4745-
let val = mk().cast_expr(bytes_literal, mk().ptr_ty(element_ty));
4746-
let val = mk().cast_expr(val, target_ty);
4747-
Ok(WithStmts::new_val(val))
4748-
}
4749-
_ => {
4750-
// Variable length arrays are already represented as pointers.
4751-
if let CTypeKind::VariableArray(..) = source_ty_kind {
4752-
Ok(val)
4753-
} else {
4754-
let method = if is_const || ctx.is_static {
4755-
"as_ptr"
4756-
} else {
4757-
"as_mut_ptr"
4758-
};
4759-
4760-
let call = val.map(|x| mk().method_call_expr(x, method, vec![]));
4761-
4762-
// If the target pointee type is different from the source element type,
4763-
// then we need to cast the ptr type as well.
4764-
let call = match source_ty_kind.element_ty() {
4765-
None => call,
4766-
Some(source_element_ty) if source_element_ty == pointee.ctype => {
4767-
call
4768-
}
4769-
Some(_) => {
4770-
let target_ty = self.convert_type(target_cty.ctype)?;
4771-
call.map(|ptr| mk().cast_expr(ptr, target_ty))
4772-
}
4773-
};
4774-
4775-
// Static arrays can now use as_ptr. Can also cast that const ptr to a
4776-
// mutable pointer as we do here:
4777-
if ctx.is_static && !is_const {
4778-
return Ok(call.map(|val| {
4779-
let inferred_type = mk().infer_ty();
4780-
let ptr_type = mk().mutbl().ptr_ty(inferred_type);
4781-
mk().cast_expr(val, ptr_type)
4782-
}));
4783-
}
4784-
4785-
Ok(call)
4786-
}
4787-
}
4711+
// Variable length arrays are already represented as pointers.
4712+
if let CTypeKind::VariableArray(..) = source_ty_kind {
4713+
return Ok(val);
47884714
}
4715+
4716+
self.convert_address_of(ctx, expr, source_cty, target_cty, val, true)
47894717
}
47904718

47914719
CastKind::NullToPointer => {
@@ -4945,6 +4873,107 @@ impl<'c> Translation<'c> {
49454873
val.map(|x| mk().cast_expr(x, target_ty))
49464874
}
49474875

4876+
pub fn convert_address_of(
4877+
&self,
4878+
ctx: ExprContext,
4879+
arg: Option<CExprId>,
4880+
arg_cty: CQualTypeId,
4881+
pointer_cty: CQualTypeId,
4882+
mut val: WithStmts<Box<Expr>>,
4883+
is_array_decay: bool,
4884+
) -> TranslationResult<WithStmts<Box<Expr>>> {
4885+
let arg_expr_kind = arg.map(|arg| &self.ast_context.index(arg).kind);
4886+
let pointee_cty = self
4887+
.ast_context
4888+
.get_pointee_qual_type(pointer_cty.ctype)
4889+
.ok_or_else(|| TranslationError::generic("Address-of should return a pointer"))?;
4890+
let arg_is_macro = arg.map_or(false, |arg| {
4891+
matches!(
4892+
self.convert_const_macro_expansion(ctx, arg, None),
4893+
Ok(Some(_))
4894+
)
4895+
});
4896+
4897+
let mut needs_cast = false;
4898+
let mut ref_cast_pointee_ty = None;
4899+
let mutbl = if pointee_cty.qualifiers.is_const {
4900+
Mutability::Immutable
4901+
} else if ctx.is_static {
4902+
// static variable initializers aren't able to use &mut, so we work around that
4903+
// by using & and an extra cast through & to *const to *mut
4904+
// TODO: Rust 1.83: Allowed, so this can be removed.
4905+
needs_cast = true;
4906+
Mutability::Immutable
4907+
} else {
4908+
Mutability::Mutable
4909+
};
4910+
4911+
// String literals are translated with a transmute, which produces a temporary.
4912+
// Taking the address of a temporary leaves a dangling pointer. So instead,
4913+
// cast the string literal directly so that its 'static lifetime is preserved.
4914+
if let (
4915+
Some(&CExprKind::Literal(literal_cty, CLiteral::String(ref bytes, element_size @ 1))),
4916+
false,
4917+
) = (arg_expr_kind, arg_is_macro)
4918+
{
4919+
let bytes_padded = self.string_literal_bytes(literal_cty.ctype, bytes, element_size);
4920+
let len = bytes_padded.len();
4921+
val = WithStmts::new_val(mk().lit_expr(bytes_padded));
4922+
4923+
if is_array_decay {
4924+
ref_cast_pointee_ty = Some(mk().ident_ty("u8"));
4925+
} else {
4926+
ref_cast_pointee_ty =
4927+
Some(mk().array_ty(mk().ident_ty("u8"), mk().lit_expr(len as u128)));
4928+
}
4929+
needs_cast = true;
4930+
} else {
4931+
let arg_cty_kind = &self.ast_context.resolve_type(arg_cty.ctype).kind;
4932+
4933+
if is_array_decay {
4934+
let method = match mutbl {
4935+
Mutability::Mutable => "as_mut_ptr",
4936+
Mutability::Immutable => "as_ptr",
4937+
};
4938+
val = val.map(|val| mk().method_call_expr(val, method, vec![]));
4939+
4940+
// If the target pointee type is different from the source element type,
4941+
// then we need to cast the ptr type as well.
4942+
if arg_cty_kind.element_ty().map_or(false, |arg_element_cty| {
4943+
arg_element_cty != pointee_cty.ctype
4944+
}) {
4945+
needs_cast = true;
4946+
}
4947+
} else {
4948+
val = val.map(|val| mk().set_mutbl(mutbl).addr_of_expr(val));
4949+
4950+
// Add an intermediate reference-to-pointer cast if the context needs
4951+
// reference-to-pointer decay, or if another cast follows.
4952+
if ctx.decay_ref.is_yes() || needs_cast {
4953+
ref_cast_pointee_ty = Some(
4954+
self.type_converter
4955+
.borrow_mut()
4956+
.convert_pointee(&self.ast_context, arg_cty.ctype)?,
4957+
);
4958+
}
4959+
}
4960+
}
4961+
4962+
// Perform an intermediate reference-to-pointer cast if needed.
4963+
// TODO: Rust 1.76: Use `ptr::from_ref`.
4964+
if let Some(pointee_ty) = ref_cast_pointee_ty {
4965+
val = val.map(|val| mk().cast_expr(val, mk().set_mutbl(mutbl).ptr_ty(pointee_ty)));
4966+
}
4967+
4968+
// Perform a final cast to the target type if needed.
4969+
if needs_cast {
4970+
let pointer_ty = self.convert_type(pointer_cty.ctype)?;
4971+
val = val.map(|val| mk().cast_expr(val, pointer_ty));
4972+
}
4973+
4974+
Ok(val)
4975+
}
4976+
49484977
pub fn implicit_default_expr(
49494978
&self,
49504979
ty_id: CTypeId,

c2rust-transpile/src/translator/operators.rs

Lines changed: 12 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,6 @@ impl<'c> Translation<'c> {
915915
lrvalue: LRValue,
916916
) -> TranslationResult<WithStmts<Box<Expr>>> {
917917
let CQualTypeId { ctype, .. } = cqual_type;
918-
let ty = self.convert_type(ctype)?;
919918
let resolved_ctype = self.ast_context.resolve_type(ctype);
920919

921920
let mut unary = match name {
@@ -927,94 +926,27 @@ impl<'c> Translation<'c> {
927926
CExprKind::Unary(_, c_ast::UnOp::Deref, target, _) => {
928927
return self.convert_expr(ctx, *target, None)
929928
}
930-
// An AddrOf DeclRef/Member is safe to not decay if the translator isn't already giving a hard
931-
// yes to decaying (ie, BitCasts). So we only convert default to no decay.
929+
// An AddrOf DeclRef/Member is safe to not decay
930+
// if the translator isn't already giving a hard yes to decaying (ie, BitCasts).
931+
// So we only convert default to no decay.
932932
CExprKind::DeclRef(..) | CExprKind::Member(..) => {
933933
ctx.decay_ref.set_default_to_no()
934934
}
935935
_ => (),
936-
};
937-
938-
// In this translation, there are only pointers to functions and
939-
// & becomes a no-op when applied to a function.
936+
}
940937

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

943-
if self.ast_context.is_function_pointer(ctype) {
944-
Ok(val.map(|x| mk().call_expr(mk().ident_expr("Some"), vec![x])))
945-
} else {
946-
let pointee_ty =
947-
self.ast_context
948-
.get_pointee_qual_type(ctype)
949-
.ok_or_else(|| {
950-
TranslationError::generic("Address-of should return a pointer")
951-
})?;
952-
953-
let expr_kind = &self.ast_context.index(arg).kind;
954-
let translate_as_macro = self
955-
.convert_const_macro_expansion(ctx, arg, None)
956-
.ok()
957-
.flatten()
958-
.is_some();
959-
960-
// String literals are translated with a transmute, which produces a temporary.
961-
// Taking the address of a temporary leaves a dangling pointer. So instead,
962-
// cast the string literal directly so that its 'static lifetime is preserved.
963-
if let (
964-
&CExprKind::Literal(
965-
literal_cqual_type,
966-
CLiteral::String(ref bytes, element_size @ 1),
967-
),
968-
false,
969-
) = (expr_kind, translate_as_macro)
970-
{
971-
let bytes_padded = self.string_literal_bytes(
972-
literal_cqual_type.ctype,
973-
bytes,
974-
element_size,
975-
);
976-
977-
let array_ty = mk().array_ty(
978-
mk().ident_ty("u8"),
979-
mk().lit_expr(bytes_padded.len() as u128),
980-
);
981-
let bytes_literal = mk().lit_expr(bytes_padded);
982-
let val = mk().cast_expr(bytes_literal, mk().ptr_ty(array_ty));
983-
let val = mk().cast_expr(val, ty);
984-
return Ok(WithStmts::new_val(val));
985-
}
986-
987-
let mutbl = if pointee_ty.qualifiers.is_const {
988-
Mutability::Immutable
989-
} else {
990-
Mutability::Mutable
991-
};
992-
993-
val.result_map(|a| {
994-
let mut addr_of_arg: Box<Expr>;
995-
996-
if ctx.is_static {
997-
// static variable initializers aren't able to use &mut,
998-
// so we work around that by using & and an extra cast
999-
// through & to *const to *mut
1000-
addr_of_arg = mk().addr_of_expr(a);
1001-
if let Mutability::Mutable = mutbl {
1002-
let ty_ = self.convert_pointee_type(pointee_ty.ctype)?;
1003-
addr_of_arg = mk().cast_expr(addr_of_arg, mk().ptr_ty(ty_));
1004-
}
1005-
} else {
1006-
// Normal case is allowed to use &mut if needed
1007-
addr_of_arg = mk().set_mutbl(mutbl).addr_of_expr(a);
940+
// & becomes a no-op when applied to a function.
941+
if self.ast_context.is_function_pointer(cqual_type.ctype) {
942+
return Ok(val.map(|x| mk().call_expr(mk().ident_expr("Some"), vec![x])));
943+
}
1008944

1009-
// Avoid unnecessary reference to pointer decay in fn call args:
1010-
if ctx.decay_ref.is_no() {
1011-
return Ok(addr_of_arg);
1012-
}
1013-
}
945+
let arg_cty = arg_kind
946+
.get_qual_type()
947+
.ok_or_else(|| format_err!("bad source type"))?;
1014948

1015-
Ok(mk().cast_expr(addr_of_arg, ty))
1016-
})
1017-
}
949+
self.convert_address_of(ctx, Some(arg), arg_cty, cqual_type, val, false)
1018950
}
1019951
c_ast::UnOp::PreIncrement => self.convert_pre_increment(ctx, cqual_type, true, arg),
1020952
c_ast::UnOp::PreDecrement => self.convert_pre_increment(ctx, cqual_type, false, arg),

c2rust-transpile/tests/snapshots/[email protected]

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,8 @@ pub static mut static_char_array: [::core::ffi::c_char; 9] =
4141
pub static mut static_char_ptr: *mut ::core::ffi::c_char =
4242
b"mystring\0" as *const u8 as *const ::core::ffi::c_char as *mut ::core::ffi::c_char;
4343
#[no_mangle]
44-
pub static mut static_void_ptr: *mut ::core::ffi::c_void = unsafe {
45-
static_char_array.as_ptr() as *mut _ as *mut ::core::ffi::c_void
46-
};
44+
pub static mut static_void_ptr: *mut ::core::ffi::c_void =
45+
unsafe { static_char_array.as_ptr() as *mut ::core::ffi::c_char as *mut ::core::ffi::c_void };
4746
#[no_mangle]
4847
pub unsafe extern "C" fn entry() {
4948
let mut int_2d: [[::core::ffi::c_int; 1]; 1] = [[1 as ::core::ffi::c_int]];

c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.snap

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ pub static mut static_single_int: ::core::ffi::c_int = 42;
1717
pub static mut static_single_int_ptr: *mut ::core::ffi::c_int =
1818
&42 as *const ::core::ffi::c_int as *mut ::core::ffi::c_int;
1919
#[no_mangle]
20-
pub static mut static_int_ptr_to_array: *mut ::core::ffi::c_int = [42, 9001].as_ptr() as *mut _;
20+
pub static mut static_int_ptr_to_array: *mut ::core::ffi::c_int =
21+
[42, 9001].as_ptr() as *mut ::core::ffi::c_int;
2122
#[no_mangle]
2223
pub static mut static_volatile_int_ptr_to_array: *mut ::core::ffi::c_int =
23-
[42, 9001].as_ptr() as *mut _ as *mut ::core::ffi::c_int;
24+
[42, 9001].as_ptr() as *mut ::core::ffi::c_int as *mut ::core::ffi::c_int;
2425
#[no_mangle]
2526
pub static mut static_int_array_ptr: *mut [::core::ffi::c_int; 2] =
2627
&[42, 9001] as *const [::core::ffi::c_int; 2] as *mut [::core::ffi::c_int; 2];

c2rust-transpile/tests/snapshots/[email protected]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ pub static mut fns: fn_ptrs = {
370370
init
371371
};
372372
#[no_mangle]
373-
pub static mut p: *const fn_ptrs = unsafe { &fns as *const fn_ptrs };
373+
pub static mut p: *const fn_ptrs = unsafe { &fns };
374374
pub const ZSTD_WINDOWLOG_MAX_32: ::core::ffi::c_int = 30 as ::core::ffi::c_int;
375375
pub const ZSTD_WINDOWLOG_MAX_64: ::core::ffi::c_int = 31 as ::core::ffi::c_int;
376376
#[no_mangle]

0 commit comments

Comments
 (0)