Skip to content

Commit 03a1045

Browse files
committed
transpile: let can_propagate_cast handle casting EnumConstant to its own enum
1 parent 2fe1fd2 commit 03a1045

3 files changed

Lines changed: 47 additions & 30 deletions

File tree

c2rust-transpile/src/c_ast/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,14 @@ impl TypedAstContext {
780780
TypeOf(ty) => ty,
781781
Paren(ty) => ty,
782782
Auto(ty) => ty,
783+
784+
// As an exception, resolve typedefs in `prenamed_decls`, because they do not
785+
// get translated as type aliases in the final code.
786+
Typedef(decl) if self.prenamed_decls.contains_key(&decl) => match self[decl].kind {
787+
CDeclKind::Typedef { typ: ty, .. } => ty.ctype,
788+
_ => panic!("Typedef decl did not point to a typedef"),
789+
},
790+
783791
_ => return typ,
784792
};
785793
self.resolve_type_id_no_typedef(ty)

c2rust-transpile/src/translator/enums.rs

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use crate::{
77
diagnostics::TranslationResult,
88
translator::{signed_int_expr, ConvertedDecl, EnumMode, ExprContext, Translation},
99
with_stmts::WithStmts,
10-
CDeclKind, CEnumConstantId, CEnumId, CExprId, CExprKind, CLiteral, CQualTypeId, CTypeKind,
11-
ConstIntExpr,
10+
CDeclKind, CEnumConstantId, CEnumId, CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId,
11+
CTypeKind, ConstIntExpr,
1212
};
1313

1414
impl<'c> Translation<'c> {
@@ -87,11 +87,17 @@ impl<'c> Translation<'c> {
8787
enum_constant_id: CEnumConstantId,
8888
target_type_id: CQualTypeId,
8989
) -> TranslationResult<WithStmts<Box<Expr>>> {
90-
let enum_id = self.ast_context.parents[&enum_constant_id];
91-
let val = self.enum_constant_expr(enum_constant_id);
90+
let mut val = WithStmts::new_val(self.enum_constant_expr(enum_constant_id));
91+
92+
if !self.enum_constant_matches_type(target_type_id.ctype, enum_constant_id) {
93+
// Add a cast to the expected integral type.
94+
let enum_id = self.ast_context.parents[&enum_constant_id];
95+
val = val.and_then_try(|val| {
96+
self.convert_cast_from_enum(ctx, enum_id, target_type_id, val)
97+
})?;
98+
}
9299

93-
// Add a cast to the expected integral type.
94-
self.convert_cast_from_enum(ctx, enum_id, target_type_id, val)
100+
Ok(val)
95101
}
96102

97103
/// Translate a cast where the source type, but not the target type, is an `enum` type.
@@ -140,23 +146,6 @@ impl<'c> Translation<'c> {
140146
) -> TranslationResult<WithStmts<Box<Expr>>> {
141147
if let Some(expr) = expr {
142148
match self.ast_context[expr].kind {
143-
// This is the case of finding a variable which is an `EnumConstant` of the same
144-
// enum we are casting to. Here, we can just remove the extraneous cast instead of
145-
// generating a new one.
146-
CExprKind::DeclRef(_, enum_constant_id, _)
147-
if self.is_variant_of_enum(enum_id, enum_constant_id) =>
148-
{
149-
// `enum`s shouldn't need portable `override_ty`s.
150-
let expr_is_macro = self.expr_is_expanded_macro(ctx, expr, None);
151-
152-
// If this DeclRef expanded to a const macro, we actually need to insert a cast,
153-
// because the translation of a const macro skips implicit casts in its context.
154-
if !expr_is_macro {
155-
val = self.enum_constant_expr(enum_constant_id);
156-
return Ok(WithStmts::new_val(val));
157-
}
158-
}
159-
160149
CExprKind::Literal(_, CLiteral::Integer(i, _)) => {
161150
val = self.enum_for_i64(enum_id, i as i64);
162151
return Ok(WithStmts::new_val(val));
@@ -272,13 +261,17 @@ impl<'c> Translation<'c> {
272261
}
273262
}
274263

275-
fn is_variant_of_enum(&self, enum_id: CEnumId, enum_constant_id: CEnumConstantId) -> bool {
276-
let variants = match self.ast_context[enum_id].kind {
277-
CDeclKind::Enum { ref variants, .. } => variants,
278-
_ => panic!("{:?} does not point to an `enum` declaration", enum_id),
264+
pub fn enum_constant_matches_type(
265+
&self,
266+
type_id: CTypeId,
267+
enum_constant_id: CEnumConstantId,
268+
) -> bool {
269+
let type_enum_id = match self.ast_context.resolve_type_no_typedef(type_id).kind {
270+
CTypeKind::Enum(enum_id) => enum_id,
271+
_ => return false,
279272
};
280-
281-
variants.contains(&enum_constant_id)
273+
let constant_enum_id = self.ast_context.parents[&enum_constant_id];
274+
type_enum_id == constant_enum_id
282275
}
283276

284277
pub fn enum_integral_type(&self, enum_id: CEnumId) -> CQualTypeId {

c2rust-transpile/src/translator/mod.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3351,7 +3351,11 @@ impl<'c> Translation<'c> {
33513351
}
33523352

33533353
if let CDeclKind::EnumConstant { .. } = decl {
3354-
return self.convert_enum_constant_decl_ref(ctx, decl_id, qual_ty);
3354+
return self.convert_enum_constant_decl_ref(
3355+
ctx,
3356+
decl_id,
3357+
override_ty.unwrap_or(qual_ty),
3358+
);
33553359
}
33563360

33573361
let varname = decl.get_name().expect("expected variable name").to_owned();
@@ -3588,6 +3592,18 @@ impl<'c> Translation<'c> {
35883592
}
35893593

35903594
let expr_kind = &self.ast_context[expr_id].kind;
3595+
3596+
if let &CExprKind::DeclRef(_, decl_id, _) = expr_kind {
3597+
if let CDeclKind::EnumConstant { .. } = self.ast_context[decl_id].kind {
3598+
// In C, `EnumConstant`s have some integral type, _not_ the enum type.
3599+
// However, if we then immediately have a cast to convert this variable back into
3600+
// the enum type, we would like to produce Rust with _no_ casts.
3601+
if self.enum_constant_matches_type(target_type_id.ctype, decl_id) {
3602+
return true;
3603+
}
3604+
}
3605+
}
3606+
35913607
let mut literal_expr_kind = expr_kind;
35923608
let mut is_negated = false;
35933609

0 commit comments

Comments
 (0)