Skip to content

Commit 9098794

Browse files
committed
transpile: Translate enums into newtype structs
1 parent c11f9aa commit 9098794

12 files changed

Lines changed: 138 additions & 88 deletions

c2rust-transpile/src/translator/enums.rs

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,18 @@ impl<'c> Translation<'c> {
2323
.borrow()
2424
.resolve_decl_name(enum_id)
2525
.expect("Enums should already be renamed");
26-
let ty = self.convert_type(integral_type.ctype)?;
27-
Ok(ConvertedDecl::Item(
28-
mk().span(span).pub_().type_item(enum_name, ty),
29-
))
26+
27+
let field = mk()
28+
.pub_()
29+
.enum_field(self.convert_type(integral_type.ctype)?);
30+
let item = mk()
31+
.span(span)
32+
.call_attr("derive", vec!["Clone", "Copy"])
33+
.call_attr("repr", vec!["transparent"])
34+
.pub_()
35+
.struct_item(enum_name, vec![field], true);
36+
37+
Ok(ConvertedDecl::Item(item))
3038
}
3139

3240
pub fn convert_enum_constant(
@@ -46,47 +54,51 @@ impl<'c> Translation<'c> {
4654
.borrow()
4755
.resolve_decl_name(enum_id)
4856
.expect("Enums should already be renamed");
49-
self.add_import(enum_id, &enum_name);
5057

51-
let ty = mk().ident_ty(enum_name);
58+
let ty = mk().ident_ty(enum_name.clone());
5259
let val = match value {
5360
ConstIntExpr::I(value) => signed_int_expr(value),
5461
ConstIntExpr::U(value) => mk().lit_expr(mk().int_unsuffixed_lit(value as u128)),
5562
};
63+
let init = self.enum_constructor_expr(enum_id, val);
5664

5765
Ok(ConvertedDecl::Item(
58-
mk().span(span).pub_().const_item(name, ty, val),
66+
mk().span(span).pub_().const_item(name, ty, init),
5967
))
6068
}
6169

62-
pub fn convert_enum_zero_initializer(&self, type_id: CTypeId) -> WithStmts<Box<Expr>> {
63-
WithStmts::new_val(self.enum_for_i64(type_id, 0))
70+
pub fn convert_enum_zero_initializer(&self, enum_id: CEnumId) -> WithStmts<Box<Expr>> {
71+
WithStmts::new_val(self.enum_for_i64(enum_id, 0))
6472
}
6573

6674
/// Translate a cast where the source type, but not the target type, is an `enum` type.
6775
pub fn convert_cast_from_enum(
6876
&self,
6977
target_cty: CTypeId,
70-
val: Box<Expr>,
78+
mut val: Box<Expr>,
7179
) -> TranslationResult<Box<Expr>> {
7280
// Convert it to the expected integral type.
81+
val = self.integer_from_enum(val);
82+
7383
let ty = self.convert_type(target_cty)?;
74-
Ok(mk().cast_expr(val, ty))
84+
val = mk().cast_expr(val, ty);
85+
86+
Ok(val)
7587
}
7688

77-
/// Translate a cast where the target type is an `enum` type.
78-
///
79-
/// When translating variable references to `EnumConstant`s, we always insert casts to the
80-
/// expected type. In C, `EnumConstant`s have some integral type, _not_ the enum type. However,
81-
/// if we then immediately have a cast to convert this variable back into an enum type, we would
82-
/// like to produce Rust with _no_ casts. This function handles this simplification.
89+
/// Gets the inner integral value of an enum value.
90+
pub fn integer_from_enum(&self, val: Box<Expr>) -> Box<Expr> {
91+
mk().anon_field_expr(val, 0)
92+
}
93+
94+
/// Translates a cast where the target type is an `enum` type.
8395
pub fn convert_cast_to_enum(
8496
&self,
8597
ctx: ExprContext,
86-
enum_type_id: CTypeId,
8798
enum_id: CEnumId,
8899
expr: Option<CExprId>,
89-
val: Box<Expr>,
100+
source_cty: CTypeId,
101+
mut val: Box<Expr>,
90102
) -> TranslationResult<Box<Expr>> {
91103
if let Some(expr) = expr {
92104
match self.ast_context[expr].kind {
@@ -110,33 +122,42 @@ impl<'c> Translation<'c> {
110122
}
111123

112124
CExprKind::Literal(_, CLiteral::Integer(i, _)) => {
113-
return Ok(self.enum_for_i64(enum_type_id, i as i64));
125+
return Ok(self.enum_for_i64(enum_id, i as i64));
114126
}
115127

116128
CExprKind::Unary(_, c_ast::UnOp::Negate, subexpr_id, _) => {
117129
if let &CExprKind::Literal(_, CLiteral::Integer(i, _)) =
118130
&self.ast_context[subexpr_id].kind
119131
{
120-
return Ok(self.enum_for_i64(enum_type_id, -(i as i64)));
132+
return Ok(self.enum_for_i64(enum_id, -(i as i64)));
121133
}
122134
}
123135

124136
_ => {}
125137
}
126138
}
127139

128-
let target_ty = self.convert_type(enum_type_id)?;
129-
Ok(mk().cast_expr(val, target_ty))
140+
let integral_type = self.enum_integral_type(enum_id);
141+
let mut needs_cast = true;
142+
143+
// C allows directly casting from enum to enum, but in Rust we need to use
144+
// the inner integer value as an intermediate.
145+
if let CTypeKind::Enum(source_enum_id) = self.ast_context.resolve_type(source_cty).kind {
146+
val = self.integer_from_enum(val);
147+
needs_cast = integral_type.ctype != self.enum_integral_type(source_enum_id).ctype;
148+
}
149+
150+
if needs_cast {
151+
let ty = self.convert_type(integral_type.ctype)?;
152+
val = mk().cast_expr(val, ty);
153+
}
154+
155+
Ok(self.enum_constructor_expr(enum_id, val))
130156
}
131157

132158
/// Given an integer value this attempts to either generate the corresponding enum
133159
/// variant directly, otherwise it converts a number to the enum type.
134-
fn enum_for_i64(&self, enum_type_id: CTypeId, value: i64) -> Box<Expr> {
135-
let enum_id = match self.ast_context.resolve_type(enum_type_id).kind {
136-
CTypeKind::Enum(enum_id) => enum_id,
137-
_ => panic!("{:?} does not point to an `enum` type", enum_type_id),
138-
};
139-
160+
fn enum_for_i64(&self, enum_id: CEnumId, value: i64) -> Box<Expr> {
140161
if let Some(enum_constant_id) = self.enum_variant_for_i64(enum_id, value) {
141162
return self.enum_constant_expr(enum_constant_id);
142163
}
@@ -148,8 +169,7 @@ impl<'c> Translation<'c> {
148169
_ => signed_int_expr(value),
149170
};
150171

151-
let target_ty = self.convert_type(enum_type_id).unwrap();
152-
mk().cast_expr(value, target_ty)
172+
self.enum_constructor_expr(enum_id, value)
153173
}
154174

155175
/// Returns the id of the variant of `enum_id` whose value matches `value`, if any.
@@ -170,6 +190,16 @@ impl<'c> Translation<'c> {
170190
})
171191
}
172192

193+
fn enum_constructor_expr(&self, enum_id: CEnumId, value: Box<Expr>) -> Box<Expr> {
194+
let enum_name = self
195+
.type_converter
196+
.borrow()
197+
.resolve_decl_name(enum_id)
198+
.unwrap();
199+
self.add_import(enum_id, &enum_name);
200+
mk().call_expr(mk().ident_expr(enum_name), vec![value])
201+
}
202+
173203
fn enum_constant_expr(&self, enum_constant_id: CEnumConstantId) -> Box<Expr> {
174204
let name = self.renamer.borrow().get(&enum_constant_id).unwrap();
175205
self.add_import(enum_constant_id, &name);

c2rust-transpile/src/translator/mod.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4494,18 +4494,12 @@ impl<'c> Translation<'c> {
44944494
Ok(val.map(|val| mk().call_expr(fn_path, vec![val])))
44954495
} else if let CTypeKind::LongDouble = self.ast_context[source_cty.ctype].kind {
44964496
self.f128_cast_to(val, target_ty_kind)
4497-
} else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind {
4497+
} else if let &CTypeKind::Enum(enum_id) = target_ty_kind {
44984498
// Casts targeting `enum` types...
44994499
let expr =
45004500
expr.ok_or_else(|| format_err!("Casts to enums require a C ExprId"))?;
45014501
val.result_map(|val| {
4502-
self.convert_cast_to_enum(
4503-
ctx,
4504-
target_cty.ctype,
4505-
enum_decl_id,
4506-
Some(expr),
4507-
val,
4508-
)
4502+
self.convert_cast_to_enum(ctx, enum_id, Some(expr), source_cty.ctype, val)
45094503
})
45104504
} else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() {
45114505
Ok(val.map(|val| {
@@ -4764,7 +4758,7 @@ impl<'c> Translation<'c> {
47644758
}
47654759

47664760
// Transmute the number `0` into the enum type
4767-
CDeclKind::Enum { .. } => self.convert_enum_zero_initializer(type_id),
4761+
CDeclKind::Enum { .. } => self.convert_enum_zero_initializer(decl_id),
47684762

47694763
_ => {
47704764
return Err(TranslationError::generic(
@@ -4868,7 +4862,7 @@ impl<'c> Translation<'c> {
48684862
}
48694863

48704864
let val = if ty.is_enum() {
4871-
mk().cast_expr(val, mk().path_ty(vec!["u64"]))
4865+
mk().anon_field_expr(val, 0)
48724866
} else {
48734867
val
48744868
};

c2rust-transpile/src/translator/operators.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,12 @@ impl<'c> Translation<'c> {
222222
} else {
223223
let compute_lhs_resolved_ty = &self.ast_context.resolve_type(compute_lhs_type_id.ctype);
224224
let lhs_type = self.convert_type(compute_lhs_type_id.ctype)?;
225+
let resolve_lhs_kind = &self.ast_context.resolve_type(lhs_type_id.ctype).kind;
225226

226-
// We can't simply as-cast into a non primitive like f128
227-
let lhs = if compute_lhs_resolved_ty.kind == CTypeKind::LongDouble {
227+
let lhs = if let CTypeKind::Enum(..) = resolve_lhs_kind {
228+
self.convert_cast_from_enum(compute_lhs_type_id.ctype, read)?
229+
} else if compute_lhs_resolved_ty.kind == CTypeKind::LongDouble {
230+
// We can't simply as-cast into a non primitive like f128
228231
self.use_crate(ExternCrate::F128);
229232

230233
let fn_path = mk().abs_path_expr(vec!["f128", "f128", "from"]);
@@ -247,11 +250,9 @@ impl<'c> Translation<'c> {
247250
None,
248251
)?;
249252

250-
let resolve_lhs_kind = &self.ast_context.resolve_type(lhs_type_id.ctype).kind;
251-
252253
val = if let &CTypeKind::Enum(enum_id) = resolve_lhs_kind {
253254
val.result_map(|val| {
254-
self.convert_cast_to_enum(ctx, lhs_type_id.ctype, enum_id, None, val)
255+
self.convert_cast_to_enum(ctx, enum_id, None, compute_lhs_type_id.ctype, val)
255256
})?
256257
} else if compute_lhs_resolved_ty.kind == CTypeKind::LongDouble {
257258
// We can't as-cast from a non primitive like f128 back to the result_type
@@ -491,9 +492,9 @@ impl<'c> Translation<'c> {
491492
val = if let &CTypeKind::Enum(enum_id) = expr_resolved_ty_kind {
492493
val.result_map(|val| self.convert_cast_to_enum(
493494
ctx,
494-
expr_type_id.ctype,
495495
enum_id,
496496
None,
497+
expr_type_id.ctype,
497498
val,
498499
))?
499500
} else {

c2rust-transpile/src/translator/pointers.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ impl<'c> Translation<'c> {
262262
let lhs_node_type = lhs_node
263263
.get_type()
264264
.ok_or_else(|| format_err!("lhs node bad type"))?;
265+
let rhs_node_type = rhs_node
266+
.get_type()
267+
.ok_or_else(|| format_err!("rhs node bad type"))?;
268+
265269
if self
266270
.ast_context
267271
.resolve_type(lhs_node_type)
@@ -275,7 +279,12 @@ impl<'c> Translation<'c> {
275279
}
276280

277281
let rhs = self.convert_expr(ctx.used(), rhs, None)?;
278-
rhs.and_then(|rhs| {
282+
rhs.and_then(|mut rhs| {
283+
// C allows enums to index arrays directly without inserting a numeric cast.
284+
if let CTypeKind::Enum(..) = self.ast_context.resolve_type(rhs_node_type).kind {
285+
rhs = self.integer_from_enum(rhs);
286+
}
287+
279288
let simple_index_array = if ctx.needs_address() {
280289
// We can't necessarily index into an array if we're using
281290
// that element to compute an address.
@@ -558,7 +567,7 @@ impl<'c> Translation<'c> {
558567
} else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind {
559568
let expr = expr.ok_or_else(|| format_err!("Casts to enums require a C ExprId"))?;
560569
val.result_map(|val| {
561-
self.convert_cast_to_enum(ctx, target_cty, enum_decl_id, Some(expr), val)
570+
self.convert_cast_to_enum(ctx, enum_decl_id, Some(expr), source_cty, val)
562571
})
563572
} else {
564573
Ok(val.map(|val| mk().cast_expr(val, target_ty)))

c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.snap

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ expression: cat tests/snapshots/exprs.2021.rs
1515
extern "C" {
1616
fn puts(str: *const ::core::ffi::c_char) -> ::core::ffi::c_int;
1717
}
18-
pub type C2Rust_Unnamed = ::core::ffi::c_uint;
19-
pub const C: C2Rust_Unnamed = 2;
20-
pub const B: C2Rust_Unnamed = 1;
21-
pub const A: C2Rust_Unnamed = 0;
18+
#[derive(Clone, Copy)]
19+
#[repr(transparent)]
20+
pub struct C2Rust_Unnamed(pub ::core::ffi::c_uint);
21+
pub const C: C2Rust_Unnamed = C2Rust_Unnamed(2);
22+
pub const B: C2Rust_Unnamed = C2Rust_Unnamed(1);
23+
pub const A: C2Rust_Unnamed = C2Rust_Unnamed(0);
2224
unsafe extern "C" fn side_effect() -> ::core::ffi::c_int {
2325
unsafe {
2426
puts(b"the return of side effect\0".as_ptr() as *const ::core::ffi::c_char);
@@ -62,7 +64,7 @@ pub unsafe extern "C" fn unary_with_side_effect() {
6264
#[no_mangle]
6365
pub unsafe extern "C" fn compound_literal() {
6466
unsafe {
65-
let mut i: ::core::ffi::c_int = B as ::core::ffi::c_int;
67+
let mut i: ::core::ffi::c_int = B.0 as ::core::ffi::c_int;
6668
}
6769
}
6870
#[no_mangle]

c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.snap

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ expression: cat tests/snapshots/exprs.2024.rs
1414
unsafe extern "C" {
1515
unsafe fn puts(str: *const ::core::ffi::c_char) -> ::core::ffi::c_int;
1616
}
17-
pub type C2Rust_Unnamed = ::core::ffi::c_uint;
18-
pub const C: C2Rust_Unnamed = 2;
19-
pub const B: C2Rust_Unnamed = 1;
20-
pub const A: C2Rust_Unnamed = 0;
17+
#[derive(Clone, Copy)]
18+
#[repr(transparent)]
19+
pub struct C2Rust_Unnamed(pub ::core::ffi::c_uint);
20+
pub const C: C2Rust_Unnamed = C2Rust_Unnamed(2);
21+
pub const B: C2Rust_Unnamed = C2Rust_Unnamed(1);
22+
pub const A: C2Rust_Unnamed = C2Rust_Unnamed(0);
2123
unsafe extern "C" fn side_effect() -> ::core::ffi::c_int {
2224
unsafe {
2325
puts(b"the return of side effect\0".as_ptr() as *const ::core::ffi::c_char);
@@ -61,7 +63,7 @@ pub unsafe extern "C" fn unary_with_side_effect() {
6163
#[unsafe(no_mangle)]
6264
pub unsafe extern "C" fn compound_literal() {
6365
unsafe {
64-
let mut i: ::core::ffi::c_int = B as ::core::ffi::c_int;
66+
let mut i: ::core::ffi::c_int = B.0 as ::core::ffi::c_int;
6567
}
6668
}
6769
#[unsafe(no_mangle)]

c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2021.snap

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,17 @@ expression: cat tests/snapshots/macrocase.2021.rs
1111
unused_mut
1212
)]
1313
#![deny(unsafe_op_in_unsafe_fn)]
14-
pub type ZSTD_dParameter = ::core::ffi::c_uint;
15-
pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = 1000;
16-
pub const ZSTD_d_windowLogMax: ZSTD_dParameter = 100;
14+
#[derive(Clone, Copy)]
15+
#[repr(transparent)]
16+
pub struct ZSTD_dParameter(pub ::core::ffi::c_uint);
17+
pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = ZSTD_dParameter(1000);
18+
pub const ZSTD_d_windowLogMax: ZSTD_dParameter = ZSTD_dParameter(100);
1719
pub const ZSTD_d_format: ::core::ffi::c_uint = 1000 as ::core::ffi::c_uint;
1820
#[no_mangle]
1921
pub unsafe extern "C" fn ZSTD_dParam_getBounds(mut dParam: ZSTD_dParameter) -> ::core::ffi::c_int {
2022
unsafe {
2123
let mut bounds: ::core::ffi::c_int = 0 as ::core::ffi::c_int;
22-
match dParam as ::core::ffi::c_uint {
24+
match dParam.0 as ::core::ffi::c_uint {
2325
100 => {
2426
bounds = 1 as ::core::ffi::c_int;
2527
return bounds;

c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2024.snap

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,17 @@ expression: cat tests/snapshots/macrocase.2024.rs
1111
unused_mut
1212
)]
1313
#![deny(unsafe_op_in_unsafe_fn)]
14-
pub type ZSTD_dParameter = ::core::ffi::c_uint;
15-
pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = 1000;
16-
pub const ZSTD_d_windowLogMax: ZSTD_dParameter = 100;
14+
#[derive(Clone, Copy)]
15+
#[repr(transparent)]
16+
pub struct ZSTD_dParameter(pub ::core::ffi::c_uint);
17+
pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = ZSTD_dParameter(1000);
18+
pub const ZSTD_d_windowLogMax: ZSTD_dParameter = ZSTD_dParameter(100);
1719
pub const ZSTD_d_format: ::core::ffi::c_uint = 1000 as ::core::ffi::c_uint;
1820
#[unsafe(no_mangle)]
1921
pub unsafe extern "C" fn ZSTD_dParam_getBounds(mut dParam: ZSTD_dParameter) -> ::core::ffi::c_int {
2022
unsafe {
2123
let mut bounds: ::core::ffi::c_int = 0 as ::core::ffi::c_int;
22-
match dParam as ::core::ffi::c_uint {
24+
match dParam.0 as ::core::ffi::c_uint {
2325
100 => {
2426
bounds = 1 as ::core::ffi::c_int;
2527
return bounds;

0 commit comments

Comments
 (0)