Skip to content

Commit c11f9aa

Browse files
authored
transpile: Factor out more pointer-related functions (#1521)
This factors out functions for two things: casts involving pointers, and null checking. In the case of `==` and `!=` operators in a non-conditional context (in operators.rs), this also adds special handling for regular pointers, where only function pointers were specially handled previously. This means that ```c int b = ptr == NULL; ``` is now translated with the `is_null` method as well, instead of with the `==` operator and `core::ptr::null`.
2 parents 2ea5c96 + 03e6439 commit c11f9aa

3 files changed

Lines changed: 195 additions & 177 deletions

File tree

c2rust-transpile/src/translator/mod.rs

Lines changed: 31 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -2715,62 +2715,39 @@ impl<'c> Translation<'c> {
27152715
.ok_or_else(|| format_err!("bad condition type"))?;
27162716

27172717
let null_pointer_case =
2718-
|negated: bool, ptr: CExprId| -> TranslationResult<WithStmts<Box<Expr>>> {
2718+
|ptr: CExprId, is_null: bool| -> TranslationResult<WithStmts<Box<Expr>>> {
27192719
let val = self.convert_expr(ctx.used().decay_ref(), ptr, None)?;
27202720
let ptr_type = self.ast_context[ptr]
27212721
.kind
27222722
.get_type()
27232723
.ok_or_else(|| format_err!("bad pointer type for condition"))?;
2724-
val.and_then(|e| {
2725-
Ok(WithStmts::new_val(
2726-
if self.ast_context.is_function_pointer(ptr_type) {
2727-
if negated {
2728-
mk().method_call_expr(e, "is_some", vec![])
2729-
} else {
2730-
mk().method_call_expr(e, "is_none", vec![])
2731-
}
2732-
} else {
2733-
// TODO: `pointer::is_null` becomes stably const in Rust 1.84.
2734-
if ctx.is_const {
2735-
return Err(format_translation_err!(
2736-
None,
2737-
"cannot check nullity of pointer in `const` context",
2738-
));
2739-
}
2740-
let is_null = mk().method_call_expr(e, "is_null", vec![]);
2741-
if negated {
2742-
mk().unary_expr(UnOp::Not(Default::default()), is_null)
2743-
} else {
2744-
is_null
2745-
}
2746-
},
2747-
))
2748-
})
2724+
2725+
val.result_map(|val| self.convert_pointer_is_null(ctx, ptr_type, val, is_null))
27492726
};
27502727

27512728
match self.ast_context[cond_id].kind {
27522729
CExprKind::Binary(_, c_ast::BinOp::EqualEqual, null_expr, ptr, _, _)
27532730
if self.ast_context.is_null_expr(null_expr) =>
27542731
{
2755-
null_pointer_case(!target, ptr)
2732+
null_pointer_case(ptr, target)
27562733
}
27572734

27582735
CExprKind::Binary(_, c_ast::BinOp::EqualEqual, ptr, null_expr, _, _)
27592736
if self.ast_context.is_null_expr(null_expr) =>
27602737
{
2761-
null_pointer_case(!target, ptr)
2738+
null_pointer_case(ptr, target)
27622739
}
27632740

27642741
CExprKind::Binary(_, c_ast::BinOp::NotEqual, null_expr, ptr, _, _)
27652742
if self.ast_context.is_null_expr(null_expr) =>
27662743
{
2767-
null_pointer_case(target, ptr)
2744+
null_pointer_case(ptr, !target)
27682745
}
27692746

27702747
CExprKind::Binary(_, c_ast::BinOp::NotEqual, ptr, null_expr, _, _)
27712748
if self.ast_context.is_null_expr(null_expr) =>
27722749
{
2773-
null_pointer_case(target, ptr)
2750+
null_pointer_case(ptr, !target)
27742751
}
27752752

27762753
CExprKind::Unary(_, c_ast::UnOp::Not, subexpr_id, _) => {
@@ -4480,79 +4457,35 @@ impl<'c> Translation<'c> {
44804457

44814458
match kind {
44824459
CastKind::BitCast | CastKind::NoOp => {
4483-
if self.ast_context.is_function_pointer(target_cty.ctype)
4484-
|| self.ast_context.is_function_pointer(source_cty.ctype)
4485-
{
4486-
let source_ty = self
4487-
.type_converter
4488-
.borrow_mut()
4489-
.convert(&self.ast_context, source_cty.ctype)?;
4490-
let target_ty = self
4491-
.type_converter
4492-
.borrow_mut()
4493-
.convert(&self.ast_context, target_cty.ctype)?;
4494-
4495-
if source_ty == target_ty {
4496-
return Ok(val);
4497-
}
4498-
4499-
self.import_type(source_cty.ctype);
4500-
self.import_type(target_cty.ctype);
4501-
4502-
val.and_then(|val| {
4503-
Ok(WithStmts::new_unsafe_val(transmute_expr(
4504-
source_ty, target_ty, val,
4505-
)))
4506-
})
4507-
} else {
4508-
// Normal case
4509-
let target_ty = self.convert_type(target_cty.ctype)?;
4510-
Ok(val.map(|val| mk().cast_expr(val, target_ty)))
4511-
}
4460+
self.convert_pointer_to_pointer_cast(source_cty.ctype, target_cty.ctype, val)
45124461
}
45134462

4514-
CastKind::IntegralToPointer
4515-
if self.ast_context.is_function_pointer(target_cty.ctype) =>
4516-
{
4517-
let target_ty = self.convert_type(target_cty.ctype)?;
4518-
val.and_then(|x| {
4519-
self.use_crate(ExternCrate::Libc);
4520-
let intptr_t = mk().abs_path_ty(vec!["libc", "intptr_t"]);
4521-
let intptr = mk().cast_expr(x, intptr_t.clone());
4522-
if ctx.is_const {
4523-
return Err(format_translation_err!(
4524-
None,
4525-
"cannot transmute integers to Option<fn ...> in `const` context",
4526-
));
4527-
}
4528-
Ok(WithStmts::new_unsafe_val(transmute_expr(
4529-
intptr_t, target_ty, intptr,
4530-
)))
4531-
})
4463+
CastKind::IntegralToPointer => {
4464+
self.convert_integral_to_pointer_cast(ctx, source_cty.ctype, target_cty.ctype, val)
45324465
}
45334466

4534-
CastKind::IntegralToPointer
4535-
| CastKind::PointerToIntegral
4536-
| CastKind::IntegralCast
4467+
CastKind::PointerToIntegral => self.convert_pointer_to_integral_cast(
4468+
ctx,
4469+
source_cty.ctype,
4470+
target_cty.ctype,
4471+
val,
4472+
expr,
4473+
),
4474+
4475+
CastKind::IntegralCast
45374476
| CastKind::FloatingCast
45384477
| CastKind::FloatingToIntegral
45394478
| CastKind::IntegralToFloating
45404479
| CastKind::BooleanToSignedIntegral => {
4541-
if kind == CastKind::PointerToIntegral && ctx.is_const {
4542-
return Err(format_translation_err!(
4543-
None,
4544-
"cannot observe pointer values in `const` context",
4545-
));
4546-
}
45474480
let target_ty = self.convert_type(target_cty.ctype)?;
4548-
let source_ty = self.convert_type(source_cty.ctype)?;
45494481

45504482
if let CTypeKind::LongDouble = target_ty_kind {
45514483
if ctx.is_const {
45524484
return Err(format_translation_err!(
4553-
None,
4554-
"f128 cannot be used in constants because `f128::f128::new` is not `const`",
4555-
));
4485+
None,
4486+
"f128 cannot be used in constants because \
4487+
`f128::f128::new` is not `const`",
4488+
));
45564489
}
45574490

45584491
self.use_crate(ExternCrate::F128);
@@ -4575,35 +4508,13 @@ impl<'c> Translation<'c> {
45754508
)
45764509
})
45774510
} else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() {
4578-
val.and_then(|x| {
4579-
Ok(WithStmts::new_val(mk().cast_expr(
4580-
mk().cast_expr(x, mk().path_ty(vec!["u8"])),
4581-
target_ty,
4582-
)))
4583-
})
4584-
} else if target_ty_kind.is_pointer() && source_ty_kind.is_bool() {
4585-
val.and_then(|x| {
4586-
self.use_crate(ExternCrate::Libc);
4587-
Ok(WithStmts::new_val(mk().cast_expr(
4588-
mk().cast_expr(x, mk().abs_path_ty(vec!["libc", "size_t"])),
4589-
target_ty,
4590-
)))
4591-
})
4511+
Ok(val.map(|val| {
4512+
mk().cast_expr(mk().cast_expr(val, mk().path_ty(vec!["u8"])), target_ty)
4513+
}))
4514+
} else if let &CTypeKind::Enum(..) = source_ty_kind {
4515+
val.result_map(|val| self.convert_cast_from_enum(target_cty.ctype, val))
45924516
} else {
4593-
// Other numeric casts translate to Rust `as` casts,
4594-
// unless the cast is to a function pointer then use `transmute`.
4595-
val.and_then(|x| {
4596-
if self.ast_context.is_function_pointer(source_cty.ctype) {
4597-
Ok(WithStmts::new_unsafe_val(transmute_expr(
4598-
source_ty, target_ty, x,
4599-
)))
4600-
} else if let &CTypeKind::Enum(..) = source_ty_kind {
4601-
self.convert_cast_from_enum(target_cty.ctype, x)
4602-
.map(WithStmts::new_val)
4603-
} else {
4604-
Ok(WithStmts::new_val(mk().cast_expr(x, target_ty)))
4605-
}
4606-
})
4517+
Ok(val.map(|val| mk().cast_expr(val, target_ty)))
46074518
}
46084519
}
46094520

@@ -4917,25 +4828,8 @@ impl<'c> Translation<'c> {
49174828
) -> TranslationResult<Box<Expr>> {
49184829
let ty = &self.ast_context.resolve_type(ty_id).kind;
49194830

4920-
Ok(if self.ast_context.is_function_pointer(ty_id) {
4921-
if target {
4922-
mk().method_call_expr(val, "is_some", vec![])
4923-
} else {
4924-
mk().method_call_expr(val, "is_none", vec![])
4925-
}
4926-
} else if ty.is_pointer() {
4927-
// TODO: `pointer::is_null` becomes stably const in Rust 1.84.
4928-
if ctx.is_const {
4929-
return Err(format_translation_err!(
4930-
None,
4931-
"cannot check nullity of pointer in `const` context",
4932-
));
4933-
}
4934-
let mut res = mk().method_call_expr(val, "is_null", vec![]);
4935-
if target {
4936-
res = mk().unary_expr(UnOp::Not(Default::default()), res)
4937-
}
4938-
res
4831+
Ok(if ty.is_pointer() {
4832+
self.convert_pointer_is_null(ctx, ty_id, val, !target)?
49394833
} else if ty.is_bool() {
49404834
if target {
49414835
val

c2rust-transpile/src/translator/operators.rs

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ impl<'c> Translation<'c> {
181181
.and_then(|rhs_val| {
182182
let expr_ids = Some((lhs, rhs));
183183
self.convert_binary_operator(
184+
ctx,
184185
op,
185186
ty,
186187
expr_type_id.ctype,
@@ -235,6 +236,7 @@ impl<'c> Translation<'c> {
235236
};
236237
let ty = self.convert_type(compute_res_type_id.ctype)?;
237238
let mut val = self.convert_binary_operator(
239+
ctx,
238240
bin_op,
239241
ty,
240242
compute_res_type_id.ctype,
@@ -456,6 +458,7 @@ impl<'c> Translation<'c> {
456458

457459
let val = if expr_or_comp_type_id.ctype == initial_lhs_type_id.ctype {
458460
self.convert_binary_operator(
461+
ctx,
459462
op,
460463
ty,
461464
expr_type_id.ctype,
@@ -472,6 +475,7 @@ impl<'c> Translation<'c> {
472475
let lhs = mk().cast_expr(read.clone(), lhs_type);
473476
let ty = self.convert_type(result_type_id.ctype)?;
474477
let mut val = self.convert_binary_operator(
478+
ctx,
475479
op,
476480
ty,
477481
result_type_id.ctype,
@@ -570,6 +574,7 @@ impl<'c> Translation<'c> {
570574
/// arguments be usable as rvalues.
571575
fn convert_binary_operator(
572576
&self,
577+
ctx: ExprContext,
573578
op: c_ast::BinOp,
574579
ty: Box<Type>,
575580
ctype: CTypeId,
@@ -611,46 +616,23 @@ impl<'c> Translation<'c> {
611616
c_ast::BinOp::ShiftRight => mk().binary_expr(BinOp::Shr(Default::default()), lhs, rhs),
612617
c_ast::BinOp::ShiftLeft => mk().binary_expr(BinOp::Shl(Default::default()), lhs, rhs),
613618

614-
c_ast::BinOp::EqualEqual => {
615-
// Using is_none method for null comparison means we don't have to
616-
// rely on the PartialEq trait as much and is also more idiomatic
617-
let expr = if let Some((lhs_expr_id, rhs_expr_id)) = lhs_rhs_ids {
618-
let fn_eq_null = self.ast_context.is_function_pointer(lhs_type.ctype)
619-
&& self.ast_context.is_null_expr(rhs_expr_id);
620-
let null_eq_fn = self.ast_context.is_function_pointer(rhs_type.ctype)
621-
&& self.ast_context.is_null_expr(lhs_expr_id);
622-
623-
if fn_eq_null {
624-
mk().method_call_expr(lhs, "is_none", vec![])
625-
} else if null_eq_fn {
626-
mk().method_call_expr(rhs, "is_none", vec![])
627-
} else {
628-
mk().binary_expr(BinOp::Eq(Default::default()), lhs, rhs)
629-
}
630-
} else {
631-
mk().binary_expr(BinOp::Eq(Default::default()), lhs, rhs)
619+
c_ast::BinOp::EqualEqual | c_ast::BinOp::NotEqual => {
620+
// Using `.is_none()` and `.is_some()` for null comparison means
621+
// we don't have to rely on `trait PartialEq` as much
622+
// and it is also more idiomatic.
623+
let (is_null, bin_op) = match op {
624+
c_ast::BinOp::EqualEqual => (true, BinOp::Eq(Default::default())),
625+
c_ast::BinOp::NotEqual => (false, BinOp::Ne(Default::default())),
626+
_ => unreachable!(),
632627
};
633-
634-
bool_to_int(expr)
635-
}
636-
c_ast::BinOp::NotEqual => {
637-
// Using is_some method for null comparison means we don't have to
638-
// rely on the PartialEq trait as much and is also more idiomatic
639-
let expr = if let Some((lhs_expr_id, rhs_expr_id)) = lhs_rhs_ids {
640-
let fn_eq_null = self.ast_context.is_function_pointer(lhs_type.ctype)
641-
&& self.ast_context.is_null_expr(rhs_expr_id);
642-
let null_eq_fn = self.ast_context.is_function_pointer(rhs_type.ctype)
643-
&& self.ast_context.is_null_expr(lhs_expr_id);
644-
645-
if fn_eq_null {
646-
mk().method_call_expr(lhs, "is_some", vec![])
647-
} else if null_eq_fn {
648-
mk().method_call_expr(rhs, "is_some", vec![])
649-
} else {
650-
mk().binary_expr(BinOp::Ne(Default::default()), lhs, rhs)
628+
let expr = match lhs_rhs_ids {
629+
Some((lhs_expr_id, _)) if self.ast_context.is_null_expr(lhs_expr_id) => {
630+
self.convert_pointer_is_null(ctx, rhs_type.ctype, rhs, is_null)?
651631
}
652-
} else {
653-
mk().binary_expr(BinOp::Ne(Default::default()), lhs, rhs)
632+
Some((_, rhs_expr_id)) if self.ast_context.is_null_expr(rhs_expr_id) => {
633+
self.convert_pointer_is_null(ctx, lhs_type.ctype, lhs, is_null)?
634+
}
635+
_ => mk().binary_expr(bin_op, lhs, rhs),
654636
};
655637

656638
bool_to_int(expr)

0 commit comments

Comments
 (0)