Skip to content

Commit 07779b3

Browse files
committed
transpile: Factor out common function for address-of and array-decay
1 parent 712cfa1 commit 07779b3

File tree

5 files changed

+124
-166
lines changed

5 files changed

+124
-166
lines changed

c2rust-transpile/src/translator/mod.rs

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

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

47864714
CastKind::NullToPointer => {
@@ -4940,6 +4868,107 @@ impl<'c> Translation<'c> {
49404868
val.map(|x| mk().cast_expr(x, target_ty))
49414869
}
49424870

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

c2rust-transpile/src/translator/operators.rs

Lines changed: 12 additions & 83 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,97 +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
1003-
.type_converter
1004-
.borrow_mut()
1005-
.convert_pointee(&self.ast_context, pointee_ty.ctype)?;
1006-
addr_of_arg = mk().cast_expr(addr_of_arg, mk().ptr_ty(ty_));
1007-
}
1008-
} else {
1009-
// Normal case is allowed to use &mut if needed
1010-
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+
}
1011944

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

1018-
Ok(mk().cast_expr(addr_of_arg, ty))
1019-
})
1020-
}
949+
self.convert_address_of(ctx, Some(arg), arg_cty, cqual_type, val, false)
1021950
}
1022951
c_ast::UnOp::PreIncrement => self.convert_pre_increment(ctx, cqual_type, true, arg),
1023952
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)