Skip to content

Commit 07d7881

Browse files
committed
transpile: Factor out common function for address-of and array-decay
1 parent 6a902de commit 07d7881

File tree

7 files changed

+134
-180
lines changed

7 files changed

+134
-180
lines changed

c2rust-transpile/src/translator/mod.rs

Lines changed: 102 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -4605,84 +4605,12 @@ impl<'c> Translation<'c> {
46054605
return Ok(val);
46064606
}
46074607

4608-
let pointee = self
4609-
.ast_context
4610-
.get_pointee_qual_type(target_cty.ctype)
4611-
.unwrap_or_else(|| panic!("dereferencing a non-pointer"));
4612-
4613-
let is_const = pointee.qualifiers.is_const;
4614-
4615-
// Handle literals by looking at the next level of expr nesting. Avoid doing this
4616-
// for expressions that will be translated as const macros, because emitting the
4617-
// name of the const macro only occurs if we process the expr_id with a direct call
4618-
// to `convert_expr`.
4619-
let expr_kind = expr.map(|e| &self.ast_context.index(e).kind);
4620-
let translate_as_macro = expr
4621-
.map(|e| {
4622-
self.convert_const_macro_expansion(ctx, e, None)
4623-
.ok()
4624-
.flatten()
4625-
.is_some()
4626-
})
4627-
.unwrap_or(false);
4628-
match expr_kind {
4629-
Some(&CExprKind::Literal(
4630-
literal_cqual_type,
4631-
CLiteral::String(ref bytes, element_size @ 1),
4632-
)) if is_const && !translate_as_macro => {
4633-
let target_ty = self.convert_type(target_cty.ctype)?;
4634-
4635-
let bytes_padded = self.string_literal_bytes(
4636-
literal_cqual_type.ctype,
4637-
bytes,
4638-
element_size,
4639-
);
4640-
let element_ty = mk().ident_ty("u8");
4641-
let bytes_literal = mk().lit_expr(bytes_padded);
4642-
let val = mk().cast_expr(bytes_literal, mk().ptr_ty(element_ty));
4643-
let val = mk().cast_expr(val, target_ty);
4644-
Ok(WithStmts::new_val(val))
4645-
}
4646-
_ => {
4647-
// Variable length arrays are already represented as pointers.
4648-
if let CTypeKind::VariableArray(..) = source_ty_kind {
4649-
Ok(val)
4650-
} else {
4651-
let method = if is_const || ctx.is_static {
4652-
"as_ptr"
4653-
} else {
4654-
"as_mut_ptr"
4655-
};
4656-
4657-
let call = val.map(|x| mk().method_call_expr(x, method, vec![]));
4658-
4659-
// If the target pointee type is different from the source element type,
4660-
// then we need to cast the ptr type as well.
4661-
let call = match source_ty_kind.element_ty() {
4662-
None => call,
4663-
Some(source_element_ty) if source_element_ty == pointee.ctype => {
4664-
call
4665-
}
4666-
Some(_) => {
4667-
let target_ty = self.convert_type(target_cty.ctype)?;
4668-
call.map(|ptr| mk().cast_expr(ptr, target_ty))
4669-
}
4670-
};
4671-
4672-
// Static arrays can now use as_ptr. Can also cast that const ptr to a
4673-
// mutable pointer as we do here:
4674-
if ctx.is_static && !is_const {
4675-
return Ok(call.map(|val| {
4676-
let inferred_type = mk().infer_ty();
4677-
let ptr_type = mk().mutbl().ptr_ty(inferred_type);
4678-
mk().cast_expr(val, ptr_type)
4679-
}));
4680-
}
4681-
4682-
Ok(call)
4683-
}
4684-
}
4608+
// Variable length arrays are already represented as pointers.
4609+
if let CTypeKind::VariableArray(..) = source_ty_kind {
4610+
return Ok(val);
46854611
}
4612+
4613+
self.convert_address_of(ctx, expr, source_cty, target_cty, val, true)
46864614
}
46874615

46884616
CastKind::NullToPointer => {
@@ -4842,6 +4770,103 @@ impl<'c> Translation<'c> {
48424770
val.map(|x| mk().cast_expr(x, target_ty))
48434771
}
48444772

4773+
pub fn convert_address_of(
4774+
&self,
4775+
ctx: ExprContext,
4776+
arg: Option<CExprId>,
4777+
arg_cty: CQualTypeId,
4778+
pointer_cty: CQualTypeId,
4779+
mut val: WithStmts<Box<Expr>>,
4780+
is_array_decay: bool,
4781+
) -> TranslationResult<WithStmts<Box<Expr>>> {
4782+
let arg_expr_kind = arg.map(|arg| &self.ast_context.index(arg).kind);
4783+
let pointee_cty = self
4784+
.ast_context
4785+
.get_pointee_qual_type(pointer_cty.ctype)
4786+
.ok_or_else(|| TranslationError::generic("Address-of should return a pointer"))?;
4787+
let arg_is_macro = arg.map_or(false, |arg| {
4788+
matches!(
4789+
self.convert_const_macro_expansion(ctx, arg, None),
4790+
Ok(Some(_))
4791+
)
4792+
});
4793+
4794+
let mut needs_cast = false;
4795+
let mut ref_cast_pointee_ty = None;
4796+
let mutbl = if pointee_cty.qualifiers.is_const {
4797+
Mutability::Immutable
4798+
} else if ctx.is_static {
4799+
// static variable initializers aren't able to use &mut, so we work around that
4800+
// by using & and an extra cast through & to *const to *mut
4801+
// TODO: Rust 1.83: Allowed, so this can be removed.
4802+
needs_cast = true;
4803+
Mutability::Immutable
4804+
} else {
4805+
Mutability::Mutable
4806+
};
4807+
4808+
// String literals are translated with a transmute, which produces a temporary.
4809+
// Taking the address of a temporary leaves a dangling pointer. So instead,
4810+
// cast the string literal directly so that its 'static lifetime is preserved.
4811+
if let (
4812+
Some(&CExprKind::Literal(literal_cty, CLiteral::String(ref bytes, element_size @ 1))),
4813+
false,
4814+
) = (arg_expr_kind, arg_is_macro)
4815+
{
4816+
let bytes_padded = self.string_literal_bytes(literal_cty.ctype, bytes, element_size);
4817+
let len = bytes_padded.len();
4818+
val = WithStmts::new_val(mk().lit_expr(bytes_padded));
4819+
4820+
ref_cast_pointee_ty =
4821+
Some(mk().array_ty(mk().ident_ty("u8"), mk().lit_expr(len as u128)));
4822+
needs_cast = true;
4823+
} else {
4824+
let arg_cty_kind = &self.ast_context.resolve_type(arg_cty.ctype).kind;
4825+
4826+
if is_array_decay {
4827+
let method = match mutbl {
4828+
Mutability::Mutable => "as_mut_ptr",
4829+
Mutability::Immutable => "as_ptr",
4830+
};
4831+
val = val.map(|val| mk().method_call_expr(val, method, vec![]));
4832+
4833+
// If the target pointee type is different from the source element type,
4834+
// then we need to cast the ptr type as well.
4835+
if arg_cty_kind.element_ty().map_or(false, |arg_element_cty| {
4836+
arg_element_cty != pointee_cty.ctype
4837+
}) {
4838+
needs_cast = true;
4839+
}
4840+
} else {
4841+
val = val.map(|val| mk().set_mutbl(mutbl).addr_of_expr(val));
4842+
4843+
// Add an intermediate reference-to-pointer cast if the context needs
4844+
// reference-to-pointer decay, or if another cast follows.
4845+
if ctx.decay_ref.is_yes() || needs_cast {
4846+
ref_cast_pointee_ty = Some(
4847+
self.type_converter
4848+
.borrow_mut()
4849+
.convert_pointee(&self.ast_context, arg_cty.ctype)?,
4850+
);
4851+
}
4852+
}
4853+
}
4854+
4855+
// Perform an intermediate reference-to-pointer cast if needed.
4856+
// TODO: Rust 1.76: Use `ptr::from_ref`.
4857+
if let Some(pointee_ty) = ref_cast_pointee_ty {
4858+
val = val.map(|val| mk().cast_expr(val, mk().set_mutbl(mutbl).ptr_ty(pointee_ty)));
4859+
}
4860+
4861+
// Perform a final cast to the target type if needed.
4862+
if needs_cast {
4863+
let pointer_ty = self.convert_type(pointer_cty.ctype)?;
4864+
val = val.map(|val| mk().cast_expr(val, pointer_ty));
4865+
}
4866+
4867+
Ok(val)
4868+
}
4869+
48454870
pub fn implicit_default_expr(
48464871
&self,
48474872
ty_id: CTypeId,

c2rust-transpile/src/translator/operators.rs

Lines changed: 12 additions & 85 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,99 +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 mut qtype = pointee_ty;
1003-
qtype.qualifiers.is_const = true;
1004-
let ty_ = self
1005-
.type_converter
1006-
.borrow_mut()
1007-
.convert_pointer(&self.ast_context, qtype)?;
1008-
addr_of_arg = mk().cast_expr(addr_of_arg, ty_);
1009-
}
1010-
} else {
1011-
// Normal case is allowed to use &mut if needed
1012-
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+
}
1013944

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

1020-
Ok(mk().cast_expr(addr_of_arg, ty))
1021-
})
1022-
}
949+
self.convert_address_of(ctx, Some(arg), arg_cty, cqual_type, val, false)
1023950
}
1024951
c_ast::UnOp::PreIncrement => self.convert_pre_increment(ctx, cqual_type, true, arg),
1025952
c_ast::UnOp::PreDecrement => self.convert_pre_increment(ctx, cqual_type, false, arg),

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,10 @@ pub static mut static_char_array: [::core::ffi::c_char; 9] =
3939
unsafe { ::core::mem::transmute::<[u8; 9], [::core::ffi::c_char; 9]>(*b"mystring\0") };
4040
#[no_mangle]
4141
pub static mut static_char_ptr: *mut ::core::ffi::c_char =
42-
b"mystring\0" as *const u8 as *const ::core::ffi::c_char as *mut ::core::ffi::c_char;
42+
b"mystring\0" as *const [u8; 9] 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]];
@@ -90,11 +89,11 @@ pub unsafe extern "C" fn entry() {
9089
let mut char_var_ptr: *mut ::core::ffi::c_char = char_with_string.as_mut_ptr();
9190
let mut char_var_array_ptr: *mut [::core::ffi::c_char; 4] = &mut char_with_string;
9291
let mut const_char_lit_ptr: *const ::core::ffi::c_char =
93-
b"abc\0" as *const u8 as *const ::core::ffi::c_char;
92+
b"abc\0" as *const [u8; 4] as *const ::core::ffi::c_char;
9493
let mut const_char_lit_array_ptr: *const [::core::ffi::c_char; 4] =
9594
b"abc\0" as *const [u8; 4] as *const [::core::ffi::c_char; 4];
9695
let mut char_lit_ptr: *mut ::core::ffi::c_char =
97-
b"abc\0" as *const u8 as *const ::core::ffi::c_char as *mut ::core::ffi::c_char;
96+
b"abc\0" as *const [u8; 4] as *const ::core::ffi::c_char as *mut ::core::ffi::c_char;
9897
let mut char_lit_array_ptr: *mut [::core::ffi::c_char; 4] = b"abc\0" as *const [u8; 4]
9998
as *const [::core::ffi::c_char; 4]
10099
as *mut [::core::ffi::c_char; 4];

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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub const C: C2RustUnnamed = 2;
1919
pub const B: C2RustUnnamed = 1;
2020
pub const A: C2RustUnnamed = 0;
2121
unsafe extern "C" fn side_effect() -> ::core::ffi::c_int {
22-
puts(b"the return of side effect\0" as *const u8 as *const ::core::ffi::c_char);
22+
puts(b"the return of side effect\0" as *const [u8; 26] as *const ::core::ffi::c_char);
2323
return 10 as ::core::ffi::c_int;
2424
}
2525
#[no_mangle]
@@ -43,7 +43,7 @@ pub unsafe extern "C" fn unary_with_side_effect() {
4343
side_effect();
4444
!side_effect();
4545
(side_effect() == 0) as ::core::ffi::c_int;
46-
&*(b"\0" as *const u8 as *const ::core::ffi::c_char).offset(::core::mem::transmute::<
46+
&*(b"\0" as *const [u8; 1] as *const ::core::ffi::c_char).offset(::core::mem::transmute::<
4747
unsafe extern "C" fn() -> ::core::ffi::c_int,
4848
unsafe extern "C" fn() -> ::core::ffi::c_int,
4949
>(side_effect)() as isize) as *const ::core::ffi::c_char;

0 commit comments

Comments
 (0)