Skip to content

Commit 797e7a0

Browse files
committed
transpile: Translate enums into newtype structs
1 parent 9dd6f33 commit 797e7a0

12 files changed

Lines changed: 137 additions & 87 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
@@ -4545,18 +4545,12 @@ impl<'c> Translation<'c> {
45454545
Ok(val.map(|val| mk().call_expr(fn_path, vec![val])))
45464546
} else if let CTypeKind::LongDouble = self.ast_context[source_cty.ctype].kind {
45474547
self.f128_cast_to(val, target_ty_kind)
4548-
} else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind {
4548+
} else if let &CTypeKind::Enum(enum_id) = target_ty_kind {
45494549
// Casts targeting `enum` types...
45504550
let expr =
45514551
expr.ok_or_else(|| format_err!("Casts to enums require a C ExprId"))?;
45524552
val.result_map(|val| {
4553-
self.convert_cast_to_enum(
4554-
ctx,
4555-
target_cty.ctype,
4556-
enum_decl_id,
4557-
Some(expr),
4558-
val,
4559-
)
4553+
self.convert_cast_to_enum(ctx, enum_id, Some(expr), source_cty.ctype, val)
45604554
})
45614555
} else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() {
45624556
val.and_then(|x| {
@@ -4837,7 +4831,7 @@ impl<'c> Translation<'c> {
48374831
}
48384832

48394833
// Transmute the number `0` into the enum type
4840-
CDeclKind::Enum { .. } => self.convert_enum_zero_initializer(type_id),
4834+
CDeclKind::Enum { .. } => self.convert_enum_zero_initializer(decl_id),
48414835

48424836
_ => {
48434837
return Err(TranslationError::generic(
@@ -4958,7 +4952,7 @@ impl<'c> Translation<'c> {
49584952
}
49594953

49604954
let val = if ty.is_enum() {
4961-
mk().cast_expr(val, mk().path_ty(vec!["u64"]))
4955+
mk().anon_field_expr(val, 0)
49624956
} else {
49634957
val
49644958
};

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 resolve_lhs_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(..) = resolve_lhs_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 resolve_lhs_kind = &self.ast_context.resolve_type(lhs_type_id.ctype).kind;
249-
250251
val = if let &CTypeKind::Enum(enum_id) = resolve_lhs_kind {
251252
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_lhs_type_id.ctype, 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
@@ -487,9 +488,9 @@ impl<'c> Translation<'c> {
487488
val = if let &CTypeKind::Enum(enum_id) = expr_resolved_ty_kind {
488489
val.result_map(|val| self.convert_cast_to_enum(
489490
ctx,
490-
expr_type_id.ctype,
491491
enum_id,
492492
None,
493+
expr_type_id.ctype,
493494
val,
494495
))?
495496
} else {

c2rust-transpile/src/translator/pointers.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,10 @@ impl<'c> Translation<'c> {
261261
let lhs_node_type = lhs_node
262262
.get_type()
263263
.ok_or_else(|| format_err!("lhs node bad type"))?;
264+
let rhs_node_type = rhs_node
265+
.get_type()
266+
.ok_or_else(|| format_err!("rhs node bad type"))?;
267+
264268
if self
265269
.ast_context
266270
.resolve_type(lhs_node_type)
@@ -274,7 +278,12 @@ impl<'c> Translation<'c> {
274278
}
275279

276280
let rhs = self.convert_expr(ctx.used(), rhs, None)?;
277-
rhs.and_then(|rhs| {
281+
rhs.and_then(|mut rhs| {
282+
// C allows enums to index arrays directly without inserting a numeric cast.
283+
if let CTypeKind::Enum(..) = self.ast_context.resolve_type(rhs_node_type).kind {
284+
rhs = self.integer_from_enum(rhs);
285+
}
286+
278287
let simple_index_array = if ctx.needs_address() {
279288
// We can't necessarily index into an array if we're using
280289
// that element to compute an address.

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ expression: cat tests/snapshots/exprs.2021.rs
1414
extern "C" {
1515
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
puts(b"the return of side effect\0".as_ptr() as *const ::core::ffi::c_char);
2325
return 10 as ::core::ffi::c_int;
@@ -53,7 +55,7 @@ pub unsafe extern "C" fn unary_with_side_effect() {
5355
}
5456
#[no_mangle]
5557
pub unsafe extern "C" fn compound_literal() {
56-
let mut i: ::core::ffi::c_int = B as ::core::ffi::c_int;
58+
let mut i: ::core::ffi::c_int = B.0 as ::core::ffi::c_int;
5759
}
5860
#[no_mangle]
5961
pub unsafe extern "C" fn statement_expr() {

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ expression: cat tests/snapshots/exprs.2024.rs
1313
unsafe extern "C" {
1414
fn puts(str: *const ::core::ffi::c_char) -> ::core::ffi::c_int;
1515
}
16-
pub type C2Rust_Unnamed = ::core::ffi::c_uint;
17-
pub const C: C2Rust_Unnamed = 2;
18-
pub const B: C2Rust_Unnamed = 1;
19-
pub const A: C2Rust_Unnamed = 0;
16+
#[derive(Clone, Copy)]
17+
#[repr(transparent)]
18+
pub struct C2Rust_Unnamed(pub ::core::ffi::c_uint);
19+
pub const C: C2Rust_Unnamed = C2Rust_Unnamed(2);
20+
pub const B: C2Rust_Unnamed = C2Rust_Unnamed(1);
21+
pub const A: C2Rust_Unnamed = C2Rust_Unnamed(0);
2022
unsafe extern "C" fn side_effect() -> ::core::ffi::c_int {
2123
puts(b"the return of side effect\0".as_ptr() as *const ::core::ffi::c_char);
2224
return 10 as ::core::ffi::c_int;
@@ -52,7 +54,7 @@ pub unsafe extern "C" fn unary_with_side_effect() {
5254
}
5355
#[unsafe(no_mangle)]
5456
pub unsafe extern "C" fn compound_literal() {
55-
let mut i: ::core::ffi::c_int = B as ::core::ffi::c_int;
57+
let mut i: ::core::ffi::c_int = B.0 as ::core::ffi::c_int;
5658
}
5759
#[unsafe(no_mangle)]
5860
pub unsafe extern "C" fn statement_expr() {

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@ expression: cat tests/snapshots/macrocase.2021.rs
1010
unused_assignments,
1111
unused_mut
1212
)]
13-
pub type ZSTD_dParameter = ::core::ffi::c_uint;
14-
pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = 1000;
15-
pub const ZSTD_d_windowLogMax: ZSTD_dParameter = 100;
13+
#[derive(Clone, Copy)]
14+
#[repr(transparent)]
15+
pub struct ZSTD_dParameter(pub ::core::ffi::c_uint);
16+
pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = ZSTD_dParameter(1000);
17+
pub const ZSTD_d_windowLogMax: ZSTD_dParameter = ZSTD_dParameter(100);
1618
pub const ZSTD_d_format: ::core::ffi::c_uint = 1000 as ::core::ffi::c_uint;
1719
#[no_mangle]
1820
pub unsafe extern "C" fn ZSTD_dParam_getBounds(mut dParam: ZSTD_dParameter) -> ::core::ffi::c_int {
1921
let mut bounds: ::core::ffi::c_int = 0 as ::core::ffi::c_int;
20-
match dParam as ::core::ffi::c_uint {
22+
match dParam.0 as ::core::ffi::c_uint {
2123
100 => {
2224
bounds = 1 as ::core::ffi::c_int;
2325
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
@@ -10,14 +10,16 @@ expression: cat tests/snapshots/macrocase.2024.rs
1010
unused_assignments,
1111
unused_mut
1212
)]
13-
pub type ZSTD_dParameter = ::core::ffi::c_uint;
14-
pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = 1000;
15-
pub const ZSTD_d_windowLogMax: ZSTD_dParameter = 100;
13+
#[derive(Clone, Copy)]
14+
#[repr(transparent)]
15+
pub struct ZSTD_dParameter(pub ::core::ffi::c_uint);
16+
pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = ZSTD_dParameter(1000);
17+
pub const ZSTD_d_windowLogMax: ZSTD_dParameter = ZSTD_dParameter(100);
1618
pub const ZSTD_d_format: ::core::ffi::c_uint = 1000 as ::core::ffi::c_uint;
1719
#[unsafe(no_mangle)]
1820
pub unsafe extern "C" fn ZSTD_dParam_getBounds(mut dParam: ZSTD_dParameter) -> ::core::ffi::c_int {
1921
let mut bounds: ::core::ffi::c_int = 0 as ::core::ffi::c_int;
20-
match dParam as ::core::ffi::c_uint {
22+
match dParam.0 as ::core::ffi::c_uint {
2123
100 => {
2224
bounds = 1 as ::core::ffi::c_int;
2325
return bounds;

0 commit comments

Comments
 (0)