Skip to content

Commit d333156

Browse files
committed
transpile: let can_propagate_cast handle casting EnumConstant to its own enum
1 parent f7662b6 commit d333156

4 files changed

Lines changed: 57 additions & 41 deletions

File tree

c2rust-transpile/src/c_ast/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,14 @@ impl TypedAstContext {
751751
Decayed(ty) => ty,
752752
TypeOf(ty) => ty,
753753
Paren(ty) => ty,
754+
755+
// As an exception, resolve typedefs in `prenamed_decls`, because they do not
756+
// get translated as type aliases in the final code.
757+
Typedef(decl) if self.prenamed_decls.contains_key(&decl) => match self[decl].kind {
758+
CDeclKind::Typedef { typ: ty, .. } => ty.ctype,
759+
_ => panic!("Typedef decl did not point to a typedef"),
760+
},
761+
754762
_ => return typ,
755763
};
756764
self.resolve_type_id_no_typedef(ty)

c2rust-transpile/src/translator/enums.rs

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use syn::Expr;
55
use crate::c_ast::CUnOp;
66
use crate::{
77
diagnostics::TranslationResult,
8-
translator::{signed_int_expr, ConvertedDecl, ExprContext, Translation},
8+
translator::{signed_int_expr, ConvertedDecl, Translation},
99
with_stmts::WithStmts,
1010
CDeclKind, CEnumConstantId, CEnumId, CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId,
1111
CTypeKind, ConstIntExpr,
@@ -75,37 +75,14 @@ impl<'c> Translation<'c> {
7575
}
7676

7777
/// 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.
8378
pub fn convert_cast_to_enum(
8479
&self,
85-
ctx: ExprContext,
8680
enum_type_id: CTypeId,
87-
enum_id: CEnumId,
8881
expr: Option<CExprId>,
8982
val: Box<Expr>,
9083
) -> TranslationResult<Box<Expr>> {
9184
if let Some(expr) = expr {
9285
match self.ast_context[expr].kind {
93-
// This is the case of finding a variable which is an `EnumConstant` of the same
94-
// enum we are casting to. Here, we can just remove the extraneous cast instead of
95-
// generating a new one.
96-
CExprKind::DeclRef(_, enum_constant_id, _)
97-
if self.is_variant_of_enum(enum_id, enum_constant_id) =>
98-
{
99-
// `enum`s shouldn't need portable `override_ty`s.
100-
let expr_is_macro = self.expr_is_expanded_macro(ctx, expr, None);
101-
102-
// If this DeclRef expanded to a const macro, we actually need to insert a cast,
103-
// because the translation of a const macro skips implicit casts in its context.
104-
if !expr_is_macro {
105-
return Ok(self.enum_constant_expr(enum_constant_id));
106-
}
107-
}
108-
10986
CExprKind::Literal(_, CLiteral::Integer(i, _)) => {
11087
return Ok(self.enum_for_i64(enum_type_id, i as i64));
11188
}
@@ -173,13 +150,17 @@ impl<'c> Translation<'c> {
173150
mk().ident_expr(name)
174151
}
175152

176-
fn is_variant_of_enum(&self, enum_id: CEnumId, enum_constant_id: CEnumConstantId) -> bool {
177-
let variants = match self.ast_context[enum_id].kind {
178-
CDeclKind::Enum { ref variants, .. } => variants,
179-
_ => panic!("{:?} does not point to an `enum` declaration", enum_id),
153+
pub fn enum_constant_matches_type(
154+
&self,
155+
type_id: CTypeId,
156+
enum_constant_id: CEnumConstantId,
157+
) -> bool {
158+
let type_enum_id = match self.ast_context.resolve_type_no_typedef(type_id).kind {
159+
CTypeKind::Enum(enum_id) => enum_id,
160+
_ => return false,
180161
};
181-
182-
variants.contains(&enum_constant_id)
162+
let constant_enum_id = self.ast_context.parents[&enum_constant_id];
163+
type_enum_id == constant_enum_id
183164
}
184165

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

c2rust-transpile/src/translator/mod.rs

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3104,7 +3104,13 @@ impl<'c> Translation<'c> {
31043104
// If the variable is actually an `EnumConstant`, we need to add a cast to the
31053105
// expected integral type.
31063106
if let &CDeclKind::EnumConstant { .. } = decl {
3107-
val = self.convert_cast_from_enum(qual_ty.ctype, val)?;
3107+
if !self
3108+
.enum_constant_matches_type(override_ty.unwrap_or(qual_ty).ctype, decl_id)
3109+
{
3110+
val = self.convert_cast_from_enum(qual_ty.ctype, val)?;
3111+
}
3112+
3113+
return Ok(WithStmts::new_val(val));
31083114
}
31093115

31103116
// If we are referring to a function and need its address, we
@@ -3265,7 +3271,7 @@ impl<'c> Translation<'c> {
32653271
// But for some expression types, if we don't absolutely have to cast,
32663272
// we would rather the expression is translated according to the type we're
32673273
// expecting, and then we can skip the cast entirely.
3268-
if !is_explicit && self.can_propagate_cast(expr, target_ty) {
3274+
if !is_explicit && self.can_propagate_cast(ctx, expr, target_ty) {
32693275
return self.convert_expr(ctx, expr, Some(target_ty));
32703276
}
32713277

@@ -3514,8 +3520,33 @@ impl<'c> Translation<'c> {
35143520
}
35153521
}
35163522

3517-
fn can_propagate_cast(&self, expr_id: CExprId, target_type_id: CQualTypeId) -> bool {
3523+
fn can_propagate_cast(
3524+
&self,
3525+
ctx: ExprContext,
3526+
expr_id: CExprId,
3527+
target_type_id: CQualTypeId,
3528+
) -> bool {
35183529
let expr_kind = &self.ast_context[expr_id].kind;
3530+
3531+
if let &CExprKind::DeclRef(_, decl_id, _) = expr_kind {
3532+
if let CDeclKind::EnumConstant { .. } = self.ast_context[decl_id].kind {
3533+
// In C, `EnumConstant`s have some integral type, _not_ the enum type.
3534+
// However, if we then immediately have a cast to convert this variable back into
3535+
// the enum type, we would like to produce Rust with _no_ casts.
3536+
if self.enum_constant_matches_type(target_type_id.ctype, decl_id) {
3537+
// `enum`s shouldn't need portable `override_ty`s.
3538+
let expr_is_macro = self.expr_is_expanded_macro(ctx, expr_id, None);
3539+
3540+
// If this DeclRef expanded to a const macro, we actually need to insert
3541+
// a cast, because the translation of a const macro skips implicit casts
3542+
// in its context.
3543+
if !expr_is_macro {
3544+
return true;
3545+
}
3546+
}
3547+
}
3548+
}
3549+
35193550
let mut literal_expr_kind = expr_kind;
35203551
let mut is_negated = false;
35213552

@@ -3742,11 +3773,9 @@ impl<'c> Translation<'c> {
37423773
self.ast_context[source_cty.ctype].kind
37433774
{
37443775
self.f128_cast_to(val, target_ty_kind)
3745-
} else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind {
3776+
} else if let &CTypeKind::Enum(_) = target_ty_kind {
37463777
// Casts targeting `enum` types...
3747-
val.result_map(|val| {
3748-
self.convert_cast_to_enum(ctx, target_cty.ctype, enum_decl_id, expr, val)
3749-
})
3778+
val.result_map(|val| self.convert_cast_to_enum(target_cty.ctype, expr, val))
37503779
} else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() {
37513780
Ok(val.map(|val| {
37523781
mk().cast_expr(mk().cast_expr(val, mk().path_ty(vec!["u8"])), target_ty)

c2rust-transpile/src/translator/pointers.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -533,10 +533,8 @@ impl<'c> Translation<'c> {
533533
source_ty, target_ty, val,
534534
)))
535535
})
536-
} else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind {
537-
val.result_map(|val| {
538-
self.convert_cast_to_enum(ctx, target_cty, enum_decl_id, expr, val)
539-
})
536+
} else if let &CTypeKind::Enum(_) = target_ty_kind {
537+
val.result_map(|val| self.convert_cast_to_enum(target_cty, expr, val))
540538
} else {
541539
Ok(val.map(|val| mk().cast_expr(val, target_ty)))
542540
}

0 commit comments

Comments
 (0)