Skip to content

Commit c7607cd

Browse files
committed
transpile: Refactor enums module
1 parent ed55e86 commit c7607cd

File tree

2 files changed

+72
-67
lines changed

2 files changed

+72
-67
lines changed
Lines changed: 69 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use c2rust_ast_builder::mk;
22
use proc_macro2::Span;
3-
use syn::{Expr, ExprCast, Type};
3+
use syn::Expr;
44

55
use crate::{
66
c_ast,
77
diagnostics::TranslationResult,
8-
translator::{signed_int_expr, unparen, ConvertedDecl, Translation},
8+
translator::{signed_int_expr, ConvertedDecl, ExprContext, Translation},
99
with_stmts::WithStmts,
1010
CDeclKind, CEnumConstantId, CEnumId, CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId,
1111
CTypeKind, ConstIntExpr,
@@ -49,7 +49,7 @@ impl<'c> Translation<'c> {
4949
.expect("Enums should already be renamed");
5050
self.add_import(enum_id, &enum_name);
5151

52-
let ty = mk().path_ty(vec![enum_name]);
52+
let ty = mk().ident_ty(enum_name);
5353
let val = match value {
5454
ConstIntExpr::I(value) => signed_int_expr(value),
5555
ConstIntExpr::U(value) => mk().lit_expr(mk().int_unsuffixed_lit(value as u128)),
@@ -74,8 +74,6 @@ impl<'c> Translation<'c> {
7474
val: Box<Expr>,
7575
) -> TranslationResult<Box<Expr>> {
7676
// Convert it to the expected integral type.
77-
// When modifying this, look at `Translation::convert_cast_to_enum` -
78-
// this function assumes `DeclRef`'s to `EnumConstants`'s will translate to casts.
7977
let ty = self.convert_type(target_cty)?;
8078
Ok(mk().cast_expr(val, ty))
8179
}
@@ -88,98 +86,110 @@ impl<'c> Translation<'c> {
8886
/// like to produce Rust with _no_ casts. This function handles this simplification.
8987
pub fn convert_cast_to_enum(
9088
&self,
91-
enum_type: CTypeId,
92-
enum_decl: CEnumId, // ID of the enum declaration corresponding to the target type
93-
expr: CExprId, // ID of initial C argument to cast
94-
val: WithStmts<Box<Expr>>, // translated Rust argument to cast
95-
_source_ty: Box<Type>, // source type of cast
96-
target_ty: Box<Type>, // target type of cast
97-
) -> WithStmts<Box<Expr>> {
98-
// Extract the IDs of the `EnumConstant` decls underlying the enum.
99-
let variants = match self.ast_context[enum_decl].kind {
100-
CDeclKind::Enum { ref variants, .. } => variants,
101-
_ => panic!("{:?} does not point to an `enum` declaration", enum_decl),
102-
};
103-
89+
ctx: ExprContext,
90+
enum_type_id: CTypeId,
91+
enum_id: CEnumId,
92+
expr: CExprId,
93+
val: Box<Expr>,
94+
) -> TranslationResult<Box<Expr>> {
10495
match self.ast_context[expr].kind {
10596
// This is the case of finding a variable which is an `EnumConstant` of the same enum
10697
// we are casting to. Here, we can just remove the extraneous cast instead of generating
10798
// a new one.
108-
CExprKind::DeclRef(_, decl_id, _) if variants.contains(&decl_id) => {
109-
return val.map(|x| match *unparen(&x) {
110-
Expr::Cast(ExprCast { ref expr, .. }) => expr.clone(),
111-
// If this DeclRef expanded to a const macro, we actually need to insert a cast,
112-
// because the translation of a const macro skips implicit casts in its context.
113-
Expr::Path(..) => mk().cast_expr(x, target_ty),
114-
_ => panic!(
115-
"DeclRef {:?} of enum {:?} is not cast: {x:?}",
116-
expr, enum_decl
117-
),
118-
});
99+
CExprKind::DeclRef(_, enum_constant_id, _)
100+
if self.is_variant_of_enum(enum_id, enum_constant_id) =>
101+
{
102+
let expr_is_macro = matches!(
103+
self.convert_const_macro_expansion(ctx, expr, None),
104+
Ok(Some(_))
105+
);
106+
107+
// If this DeclRef expanded to a const macro, we actually need to insert a cast,
108+
// because the translation of a const macro skips implicit casts in its context.
109+
if !expr_is_macro {
110+
return Ok(self.enum_constant_expr(enum_constant_id));
111+
}
119112
}
120113

121114
CExprKind::Literal(_, CLiteral::Integer(i, _)) => {
122-
return val.map(|_| self.enum_for_i64(enum_type, i as i64));
115+
return Ok(self.enum_for_i64(enum_type_id, i as i64));
123116
}
124117

125118
CExprKind::Unary(_, c_ast::UnOp::Negate, subexpr_id, _) => {
126119
if let &CExprKind::Literal(_, CLiteral::Integer(i, _)) =
127120
&self.ast_context[subexpr_id].kind
128121
{
129-
return val.map(|_| self.enum_for_i64(enum_type, -(i as i64)));
122+
return Ok(self.enum_for_i64(enum_type_id, -(i as i64)));
130123
}
131124
}
132125

133-
// In all other cases, a cast to an enum requires a `transmute` - Rust enums cannot be
134-
// converted into integral types as easily as C ones.
135126
_ => {}
136127
}
137128

138-
val.map(|x| mk().cast_expr(x, target_ty))
129+
let target_ty = self.convert_type(enum_type_id)?;
130+
Ok(mk().cast_expr(val, target_ty))
139131
}
140132

141133
/// Given an integer value this attempts to either generate the corresponding enum
142134
/// variant directly, otherwise it transmutes a number to the enum type.
143135
fn enum_for_i64(&self, enum_type_id: CTypeId, value: i64) -> Box<Expr> {
144-
let def_id = match self.ast_context.resolve_type(enum_type_id).kind {
145-
CTypeKind::Enum(def_id) => def_id,
136+
let enum_id = match self.ast_context.resolve_type(enum_type_id).kind {
137+
CTypeKind::Enum(enum_id) => enum_id,
146138
_ => panic!("{:?} does not point to an `enum` type", enum_type_id),
147139
};
148140

149-
let (variants, underlying_type_id) = match self.ast_context[def_id].kind {
141+
if let Some(enum_constant_id) = self.enum_variant_for_i64(enum_id, value) {
142+
return self.enum_constant_expr(enum_constant_id);
143+
}
144+
145+
let underlying_type_id = match self.ast_context[enum_id].kind {
150146
CDeclKind::Enum {
151-
ref variants,
152-
integral_type,
147+
integral_type: Some(integral_type),
153148
..
154-
} => (variants, integral_type),
155-
_ => panic!("{:?} does not point to an `enum` declaration", def_id),
149+
} => integral_type,
150+
_ => panic!("{:?} does not point to an `enum` declaration", enum_id),
156151
};
157152

158-
for &variant_id in variants {
159-
match self.ast_context[variant_id].kind {
160-
CDeclKind::EnumConstant { value: v, .. } => {
161-
if v == ConstIntExpr::I(value) || v == ConstIntExpr::U(value as u64) {
162-
let name = self.renamer.borrow().get(&variant_id).unwrap();
163-
164-
// Import the enum variant if needed
165-
self.add_import(variant_id, &name);
166-
return mk().path_expr(vec![name]);
167-
}
168-
}
169-
_ => panic!("{:?} does not point to an enum variant", variant_id),
170-
}
171-
}
172-
173-
let underlying_type_id =
174-
underlying_type_id.expect("Attempt to construct value of forward declared enum");
175153
let value = match self.ast_context.resolve_type(underlying_type_id.ctype).kind {
176154
CTypeKind::UInt => mk().lit_expr(mk().int_unsuffixed_lit((value as u32) as u128)),
177155
CTypeKind::ULong => mk().lit_expr(mk().int_unsuffixed_lit((value as u64) as u128)),
178156
_ => signed_int_expr(value),
179157
};
180158

181159
let target_ty = self.convert_type(enum_type_id).unwrap();
182-
183160
mk().cast_expr(value, target_ty)
184161
}
162+
163+
/// Returns the id of the variant of `enum_id` whose value matches `value`, if any.
164+
fn enum_variant_for_i64(&self, enum_id: CEnumId, value: i64) -> Option<CEnumConstantId> {
165+
let variants = match self.ast_context[enum_id].kind {
166+
CDeclKind::Enum { ref variants, .. } => variants,
167+
_ => panic!("{:?} does not point to an `enum` declaration", enum_id),
168+
};
169+
170+
variants
171+
.iter()
172+
.copied()
173+
.find(|&variant_id| match self.ast_context[variant_id].kind {
174+
CDeclKind::EnumConstant { value: v, .. } => {
175+
v == ConstIntExpr::I(value) || v == ConstIntExpr::U(value as u64)
176+
}
177+
_ => panic!("{:?} does not point to an enum variant", variant_id),
178+
})
179+
}
180+
181+
fn is_variant_of_enum(&self, enum_id: CEnumId, enum_constant_id: CEnumConstantId) -> bool {
182+
let variants = match self.ast_context[enum_id].kind {
183+
CDeclKind::Enum { ref variants, .. } => variants,
184+
_ => panic!("{:?} does not point to an `enum` declaration", enum_id),
185+
};
186+
187+
variants.contains(&enum_constant_id)
188+
}
189+
190+
fn enum_constant_expr(&self, enum_constant_id: CEnumConstantId) -> Box<Expr> {
191+
let name = self.renamer.borrow().get(&enum_constant_id).unwrap();
192+
self.add_import(enum_constant_id, &name);
193+
mk().ident_expr(name)
194+
}
185195
}

c2rust-transpile/src/translator/mod.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4554,14 +4554,9 @@ impl<'c> Translation<'c> {
45544554
// Casts targeting `enum` types...
45554555
let expr =
45564556
expr.ok_or_else(|| format_err!("Casts to enums require a C ExprId"))?;
4557-
Ok(self.convert_cast_to_enum(
4558-
target_cty.ctype,
4559-
enum_decl_id,
4560-
expr,
4561-
val,
4562-
source_ty,
4563-
target_ty,
4564-
))
4557+
val.result_map(|val| {
4558+
self.convert_cast_to_enum(ctx, target_cty.ctype, enum_decl_id, expr, val)
4559+
})
45654560
} else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() {
45664561
val.and_then(|x| {
45674562
Ok(WithStmts::new_val(mk().cast_expr(

0 commit comments

Comments
 (0)