Skip to content

Commit 0b1c443

Browse files
committed
transpile: Translate enums into newtype structs
1 parent 368886c commit 0b1c443

9 files changed

Lines changed: 125 additions & 77 deletions

File tree

c2rust-transpile/src/translator/enums.rs

Lines changed: 74 additions & 38 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

5158
let ty = mk().ident_ty(enum_name);
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)
87+
}
88+
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)
7592
}
7693

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.
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: CQualTypeId,
101+
mut val: Box<Expr>,
90102
) -> TranslationResult<Box<Expr>> {
91103
if let Some(expr) = expr {
92104
match self.ast_context[expr].kind {
@@ -109,53 +121,57 @@ impl<'c> Translation<'c> {
109121
}
110122

111123
CExprKind::Literal(_, CLiteral::Integer(i, _)) => {
112-
return Ok(self.enum_for_i64(enum_type_id, i as i64));
124+
return Ok(self.enum_for_i64(enum_id, i as i64));
113125
}
114126

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

123135
_ => {}
124136
}
125137
}
126138

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

131160
/// Given an integer value this attempts to either generate the corresponding enum
132161
/// variant directly, otherwise it converts a number to the enum type.
133-
fn enum_for_i64(&self, enum_type_id: CTypeId, value: i64) -> Box<Expr> {
134-
let enum_id = match self.ast_context.resolve_type(enum_type_id).kind {
135-
CTypeKind::Enum(enum_id) => enum_id,
136-
_ => panic!("{:?} does not point to an `enum` type", enum_type_id),
137-
};
138-
162+
fn enum_for_i64(&self, enum_id: CEnumId, value: i64) -> Box<Expr> {
139163
if let Some(enum_constant_id) = self.enum_variant_for_i64(enum_id, value) {
140164
return self.enum_constant_expr(enum_constant_id);
141165
}
142166

143-
let underlying_type_id = match self.ast_context[enum_id].kind {
144-
CDeclKind::Enum {
145-
integral_type: Some(integral_type),
146-
..
147-
} => integral_type,
148-
_ => panic!("{:?} does not point to an `enum` declaration", enum_id),
149-
};
150-
167+
let underlying_type_id = self.enum_integral_type(enum_id);
151168
let value = match self.ast_context.resolve_type(underlying_type_id.ctype).kind {
152169
CTypeKind::UInt => mk().lit_expr(mk().int_unsuffixed_lit((value as u32) as u128)),
153170
CTypeKind::ULong => mk().lit_expr(mk().int_unsuffixed_lit((value as u64) as u128)),
154171
_ => signed_int_expr(value),
155172
};
156173

157-
let target_ty = self.convert_type(enum_type_id).unwrap();
158-
mk().cast_expr(value, target_ty)
174+
self.enum_constructor_expr(enum_id, value)
159175
}
160176

161177
/// Returns the id of the variant of `enum_id` whose value matches `value`, if any.
@@ -185,9 +201,29 @@ impl<'c> Translation<'c> {
185201
variants.contains(&enum_constant_id)
186202
}
187203

204+
fn enum_constructor_expr(&self, enum_id: CEnumId, value: Box<Expr>) -> Box<Expr> {
205+
let enum_name = self
206+
.type_converter
207+
.borrow()
208+
.resolve_decl_name(enum_id)
209+
.unwrap();
210+
self.add_import(enum_id, &enum_name);
211+
mk().call_expr(mk().ident_expr(enum_name), vec![value])
212+
}
213+
188214
fn enum_constant_expr(&self, enum_constant_id: CEnumConstantId) -> Box<Expr> {
189215
let name = self.renamer.borrow().get(&enum_constant_id).unwrap();
190216
self.add_import(enum_constant_id, &name);
191217
mk().ident_expr(name)
192218
}
219+
220+
fn enum_integral_type(&self, enum_id: CEnumId) -> CQualTypeId {
221+
match self.ast_context[enum_id].kind {
222+
CDeclKind::Enum {
223+
integral_type: Some(integral_type),
224+
..
225+
} => integral_type,
226+
_ => panic!("{:?} does not point to an `enum` declaration", enum_id),
227+
}
228+
}
193229
}

c2rust-transpile/src/translator/mod.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4481,18 +4481,12 @@ impl<'c> Translation<'c> {
44814481
Ok(val.map(|val| mk().call_expr(fn_path, vec![val])))
44824482
} else if let CTypeKind::LongDouble = self.ast_context[source_cty.ctype].kind {
44834483
self.f128_cast_to(val, target_ty_kind)
4484-
} else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind {
4484+
} else if let &CTypeKind::Enum(enum_id) = target_ty_kind {
44854485
// Casts targeting `enum` types...
44864486
let expr =
44874487
expr.ok_or_else(|| format_err!("Casts to enums require a C ExprId"))?;
44884488
val.result_map(|val| {
4489-
self.convert_cast_to_enum(
4490-
ctx,
4491-
target_cty.ctype,
4492-
enum_decl_id,
4493-
Some(expr),
4494-
val,
4495-
)
4489+
self.convert_cast_to_enum(ctx, enum_id, Some(expr), source_cty, val)
44964490
})
44974491
} else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() {
44984492
val.and_then(|x| {
@@ -4794,7 +4788,7 @@ impl<'c> Translation<'c> {
47944788
}
47954789

47964790
// Transmute the number `0` into the enum type
4797-
CDeclKind::Enum { .. } => self.convert_enum_zero_initializer(type_id),
4791+
CDeclKind::Enum { .. } => self.convert_enum_zero_initializer(decl_id),
47984792

47994793
_ => {
48004794
return Err(TranslationError::generic(
@@ -4915,7 +4909,7 @@ impl<'c> Translation<'c> {
49154909
}
49164910

49174911
let val = if ty.is_enum() {
4918-
mk().cast_expr(val, mk().path_ty(vec!["u64"]))
4912+
mk().anon_field_expr(val, 0)
49194913
} else {
49204914
val
49214915
};

c2rust-transpile/src/translator/operators.rs

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

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

229232
let fn_path = mk().abs_path_expr(vec!["f128", "f128", "from"]);
@@ -245,11 +248,9 @@ impl<'c> Translation<'c> {
245248
None,
246249
)?;
247250

248-
let result_ty_kind = &self.ast_context.resolve_type(lhs_type_id.ctype).kind;
249-
250251
if let &CTypeKind::Enum(enum_id) = result_ty_kind {
251252
val = val.result_map(|val| {
252-
self.convert_cast_to_enum(ctx, lhs_type_id.ctype, enum_id, None, val)
253+
self.convert_cast_to_enum(ctx, enum_id, None, compute_res_type_id, val)
253254
})?;
254255
} else if compute_lhs_resolved_ty.kind == CTypeKind::LongDouble {
255256
// We can't as-cast from a non primitive like f128 back to the result_type
@@ -488,9 +489,9 @@ impl<'c> Translation<'c> {
488489
if let &CTypeKind::Enum(enum_id) = result_ty_kind {
489490
val = val.result_map(|val| self.convert_cast_to_enum(
490491
ctx,
491-
expr_type_id.ctype,
492492
enum_id,
493493
None,
494+
result_type_id,
494495
val,
495496
))?;
496497
} else {

c2rust-transpile/src/translator/pointers.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,10 @@ impl<'c> Translation<'c> {
265265
let lhs_node_type = lhs_node
266266
.get_type()
267267
.ok_or_else(|| format_err!("lhs node bad type"))?;
268+
let rhs_node_type = rhs_node
269+
.get_type()
270+
.ok_or_else(|| format_err!("rhs node bad type"))?;
271+
268272
if self
269273
.ast_context
270274
.resolve_type(lhs_node_type)
@@ -278,7 +282,12 @@ impl<'c> Translation<'c> {
278282
}
279283

280284
let rhs = self.convert_expr(ctx.used(), rhs, None)?;
281-
rhs.and_then(|rhs| {
285+
rhs.and_then(|mut rhs| {
286+
// C allows enums to index arrays directly without inserting a numeric cast.
287+
if let CTypeKind::Enum(..) = self.ast_context.resolve_type(rhs_node_type).kind {
288+
rhs = self.integer_from_enum(rhs);
289+
}
290+
282291
let simple_index_array = if ctx.needs_address() {
283292
// We can't necessarily index into an array if we're using
284293
// that element to compute an address.

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ input_file: c2rust-transpile/tests/snapshots/exprs.c
1515
extern "C" {
1616
fn puts(str: *const ::core::ffi::c_char) -> ::core::ffi::c_int;
1717
}
18-
pub type C2RustUnnamed = ::core::ffi::c_uint;
19-
pub const C: C2RustUnnamed = 2;
20-
pub const B: C2RustUnnamed = 1;
21-
pub const A: C2RustUnnamed = 0;
18+
#[derive(Clone, Copy)]
19+
#[repr(transparent)]
20+
pub struct C2RustUnnamed(pub ::core::ffi::c_uint);
21+
pub const C: C2RustUnnamed = C2RustUnnamed(2);
22+
pub const B: C2RustUnnamed = C2RustUnnamed(1);
23+
pub const A: C2RustUnnamed = C2RustUnnamed(0);
2224
unsafe extern "C" fn side_effect() -> ::core::ffi::c_int {
2325
puts(b"the return of side effect\0" as *const u8 as *const ::core::ffi::c_char);
2426
return 10 as ::core::ffi::c_int;
@@ -54,5 +56,5 @@ pub unsafe extern "C" fn unary_with_side_effect() {
5456
}
5557
#[no_mangle]
5658
pub unsafe extern "C" fn compound_literal() {
57-
let mut i: ::core::ffi::c_int = B as ::core::ffi::c_int;
59+
let mut i: ::core::ffi::c_int = B.0 as ::core::ffi::c_int;
5860
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ input_file: c2rust-transpile/tests/snapshots/macrocase.c
1111
unused_assignments,
1212
unused_mut
1313
)]
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
let mut bounds: ::core::ffi::c_int = 0 as ::core::ffi::c_int;
21-
match dParam as ::core::ffi::c_uint {
23+
match dParam.0 as ::core::ffi::c_uint {
2224
100 => {
2325
bounds = 1 as ::core::ffi::c_int;
2426
return bounds;

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ input_file: c2rust-transpile/tests/snapshots/records.c
1414
#[derive(Copy, Clone)]
1515
#[repr(C)]
1616
pub struct AnonEnumInStruct {}
17-
pub type C2RustUnnamed = ::core::ffi::c_uint;
18-
pub const VALUE2: C2RustUnnamed = 1;
19-
pub const VALUE1: C2RustUnnamed = 0;
17+
#[derive(Clone, Copy)]
18+
#[repr(transparent)]
19+
pub struct C2RustUnnamed(pub ::core::ffi::c_uint);
20+
pub const VALUE2: C2RustUnnamed = C2RustUnnamed(1);
21+
pub const VALUE1: C2RustUnnamed = C2RustUnnamed(0);
2022
#[derive(Copy, Clone)]
2123
#[repr(C)]
2224
pub struct AnonStructInStruct {
@@ -40,9 +42,11 @@ pub struct InsideStruct {
4042
pub union AnonEnumInUnion {
4143
pub a: ::core::ffi::c_int,
4244
}
43-
pub type C2RustUnnamed_1 = ::core::ffi::c_uint;
44-
pub const VALUE4: C2RustUnnamed_1 = 1;
45-
pub const VALUE3: C2RustUnnamed_1 = 0;
45+
#[derive(Clone, Copy)]
46+
#[repr(transparent)]
47+
pub struct C2RustUnnamed_1(pub ::core::ffi::c_uint);
48+
pub const VALUE4: C2RustUnnamed_1 = C2RustUnnamed_1(1);
49+
pub const VALUE3: C2RustUnnamed_1 = C2RustUnnamed_1(0);
4650
#[derive(Copy, Clone)]
4751
#[repr(C)]
4852
pub union AnonStructInUnion {
@@ -66,7 +70,7 @@ pub struct InsideUnion {
6670
}
6771
#[no_mangle]
6872
pub unsafe extern "C" fn struct_declaration() {
69-
let mut value: ::core::ffi::c_int = VALUE2 as ::core::ffi::c_int;
73+
let mut value: ::core::ffi::c_int = VALUE2.0 as ::core::ffi::c_int;
7074
let mut a: AnonEnumInStruct = AnonEnumInStruct {};
7175
let mut b: AnonStructInStruct = AnonStructInStruct {
7276
c2rust_unnamed: C2RustUnnamed_0 { some_number: 0 },
@@ -77,7 +81,7 @@ pub unsafe extern "C" fn struct_declaration() {
7781
}
7882
#[no_mangle]
7983
pub unsafe extern "C" fn union_declaration() {
80-
let mut value: ::core::ffi::c_int = VALUE4 as ::core::ffi::c_int;
84+
let mut value: ::core::ffi::c_int = VALUE4.0 as ::core::ffi::c_int;
8185
let mut a: AnonEnumInUnion = AnonEnumInUnion { a: 0 };
8286
let mut b: AnonStructInUnion = AnonStructInUnion {
8387
c2rust_unnamed: C2RustUnnamed_2 { some_number: 0 },

0 commit comments

Comments
 (0)