11use c2rust_ast_builder:: mk;
22use proc_macro2:: Span ;
3- use syn:: { Expr , ExprCast , Type } ;
3+ use syn:: Expr ;
44
55use 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}
0 commit comments