Skip to content

Commit 804eecf

Browse files
committed
transpile: Translate EnumConstant with associated constants
1 parent 7b1c4ba commit 804eecf

13 files changed

Lines changed: 243 additions & 104 deletions

c2rust-ast-builder/src/builder.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,26 @@ impl Builder {
14161416
}))
14171417
}
14181418

1419+
pub fn const_impl_item<I>(self, name: I, ty: Box<Type>, init: Box<Expr>) -> ImplItem
1420+
where
1421+
I: Make<Ident>,
1422+
{
1423+
let name = name.make(&self);
1424+
ImplItem::Const(ImplItemConst {
1425+
attrs: self.attrs,
1426+
vis: self.vis,
1427+
defaultness: None,
1428+
const_token: Token![const](self.span),
1429+
ident: name,
1430+
generics: self.generics,
1431+
colon_token: Token![:](self.span),
1432+
ty: *ty,
1433+
eq_token: Token![=](self.span),
1434+
expr: *init,
1435+
semi_token: Token![;](self.span),
1436+
})
1437+
}
1438+
14191439
pub fn fn_item<S>(self, sig: S, mut block: Block) -> Box<Item>
14201440
where
14211441
S: Make<Signature>,

c2rust-transpile/src/translator/enums.rs

Lines changed: 146 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use syn::Expr;
44

55
use crate::c_ast::CUnOp;
66
use crate::{
7+
c_ast::iterators::SomeId,
78
diagnostics::TranslationResult,
89
translator::{signed_int_expr, ConvertedDecl, EnumMode, ExprContext, Translation},
910
with_stmts::WithStmts,
@@ -17,68 +18,121 @@ impl<'c> Translation<'c> {
1718
enum_id: CEnumId,
1819
span: Span,
1920
integral_type: CQualTypeId,
21+
variants: &[CEnumConstantId],
2022
) -> TranslationResult<ConvertedDecl> {
2123
let enum_name = &self
2224
.type_converter
2325
.borrow()
2426
.resolve_decl_name(enum_id)
2527
.expect("Enums should already be renamed");
26-
27-
let item = match self.tcfg.enum_mode {
28+
let integral_type_rs = self.convert_type(integral_type.ctype)?;
29+
let enum_item = match self.tcfg.enum_mode {
2830
EnumMode::NewType => {
29-
let field = mk()
30-
.pub_()
31-
.enum_field(self.convert_type(integral_type.ctype)?);
31+
let field = mk().pub_().enum_field(integral_type_rs);
3232
mk().span(span)
3333
.call_attr("derive", vec!["Clone", "Copy"])
3434
.call_attr("repr", vec!["transparent"])
3535
.pub_()
3636
.struct_item(enum_name, vec![field], true)
3737
}
3838

39-
EnumMode::Consts => {
40-
let ty = self.convert_type(integral_type.ctype)?;
41-
mk().span(span).pub_().type_item(enum_name, ty)
42-
}
39+
EnumMode::Consts => mk()
40+
.span(span)
41+
.pub_()
42+
.type_item(enum_name, integral_type_rs),
4343
};
4444

45-
Ok(ConvertedDecl::Item(item))
46-
}
45+
if variants.is_empty() {
46+
return Ok(ConvertedDecl::Item(enum_item));
47+
}
4748

48-
pub fn convert_enum_constant(
49-
&self,
50-
enum_constant_id: CEnumConstantId,
51-
span: Span,
52-
value: ConstIntExpr,
53-
) -> TranslationResult<ConvertedDecl> {
54-
let name = self
55-
.renamer
56-
.borrow_mut()
57-
.get(&enum_constant_id)
58-
.expect("Enum constant not named");
59-
let enum_id = self.ast_context.parents[&enum_constant_id];
60-
let enum_name = self
61-
.type_converter
62-
.borrow()
63-
.resolve_decl_name(enum_id)
64-
.expect("Enums should already be renamed");
49+
match self.tcfg.enum_mode {
50+
EnumMode::NewType => {
51+
let enum_type = mk().ident_ty("Self");
52+
let constants = variants
53+
.iter()
54+
.map(|&enum_constant_id| {
55+
let name = match self.ast_context[enum_constant_id].kind {
56+
CDeclKind::EnumConstant { ref name, .. } => self
57+
.type_converter
58+
.borrow_mut()
59+
.declare_field_name(enum_id, enum_constant_id, name),
60+
_ => panic!("{:?} does not point to an enum variant", enum_constant_id),
61+
};
62+
let (span, init) = self.make_enum_constant_init(enum_constant_id);
63+
mk().span(span)
64+
.pub_()
65+
.const_impl_item(name, enum_type.clone(), init)
66+
})
67+
.collect();
68+
69+
let impl_block = mk().impl_item(mk().ident_ty(enum_name), constants);
70+
Ok(ConvertedDecl::Items(vec![enum_item, impl_block]))
71+
}
6572

66-
let ty = mk().ident_ty(enum_name);
73+
EnumMode::Consts => {
74+
let enum_type = mk().ident_ty(enum_name);
75+
let constants_iter = variants.iter().map(|&enum_constant_id| {
76+
let name = self
77+
.renamer
78+
.borrow_mut()
79+
.get(&enum_constant_id)
80+
.expect("Enum constant not named");
81+
let (span, init) = self.make_enum_constant_init(enum_constant_id);
82+
mk().span(span)
83+
.pub_()
84+
.const_item(name, enum_type.clone(), init)
85+
});
86+
87+
let items = std::iter::once(enum_item).chain(constants_iter).collect();
88+
Ok(ConvertedDecl::Items(items))
89+
}
90+
}
91+
}
92+
93+
fn make_enum_constant_init(&self, enum_constant_id: CEnumConstantId) -> (Span, Box<Expr>) {
94+
let value = match self.ast_context[enum_constant_id].kind {
95+
CDeclKind::EnumConstant { value, .. } => value,
96+
_ => panic!("{:?} does not point to an enum variant", enum_constant_id),
97+
};
6798
let val = match value {
6899
ConstIntExpr::I(value) => signed_int_expr(value),
69100
ConstIntExpr::U(value) => mk().lit_expr(mk().int_unsuffixed_lit(value as u128)),
70101
};
71-
let init = self.enum_constructor_expr(enum_id, val);
102+
let enum_id = self.ast_context.parents[&enum_constant_id];
103+
let init = self.enum_constructor_expr(enum_id, val, true);
104+
let span = self
105+
.get_span(SomeId::Decl(enum_constant_id))
106+
.unwrap_or_else(Span::call_site);
72107

73-
Ok(ConvertedDecl::Item(
74-
mk().span(span).pub_().const_item(name, ty, init),
75-
))
108+
(span, init)
76109
}
77110

78111
pub fn convert_enum_zero_initializer(&self, enum_id: CEnumId) -> WithStmts<Box<Expr>> {
79112
WithStmts::new_val(self.enum_for_i64(enum_id, 0))
80113
}
81114

115+
/// Translates a `DeclRef` for an `EnumConstant`.
116+
pub fn convert_enum_constant_decl_ref(
117+
&self,
118+
ctx: ExprContext,
119+
enum_constant_id: CEnumConstantId,
120+
target_cty: CQualTypeId,
121+
) -> TranslationResult<WithStmts<Box<Expr>>> {
122+
let enum_id = self.ast_context.parents[&enum_constant_id];
123+
let val = self.enum_constant_expr(enum_id, enum_constant_id);
124+
125+
// If our target type is this enum type, don't bother with a cast.
126+
if matches!(
127+
self.ast_context.resolve_type(target_cty.ctype).kind,
128+
CTypeKind::Enum(target_enum_id) if target_enum_id == enum_id
129+
) {
130+
Ok(WithStmts::new_val(val))
131+
} else {
132+
self.convert_cast_from_enum(ctx, enum_id, target_cty, val)
133+
}
134+
}
135+
82136
/// Translate a cast where the source type, but not the target type, is an `enum` type.
83137
pub fn convert_cast_from_enum(
84138
&self,
@@ -145,7 +199,7 @@ impl<'c> Translation<'c> {
145199
// If this DeclRef expanded to a const macro, we actually need to insert a cast,
146200
// because the translation of a const macro skips implicit casts in its context.
147201
if !expr_is_macro {
148-
val = self.enum_constant_expr(enum_constant_id);
202+
val = self.enum_constant_expr(enum_id, enum_constant_id);
149203
return Ok(WithStmts::new_val(val));
150204
}
151205
}
@@ -202,14 +256,14 @@ impl<'c> Translation<'c> {
202256
}
203257

204258
// Construct a new enum value.
205-
Ok(val.map(|val| self.enum_constructor_expr(enum_id, val)))
259+
Ok(val.map(|val| self.enum_constructor_expr(enum_id, val, false)))
206260
}
207261

208262
/// Given an integer value this attempts to either generate the corresponding enum
209263
/// variant directly, otherwise it converts a number to the enum type.
210264
fn enum_for_i64(&self, enum_id: CEnumId, value: i64) -> Box<Expr> {
211265
if let Some(enum_constant_id) = self.enum_variant_for_i64(enum_id, value) {
212-
return self.enum_constant_expr(enum_constant_id);
266+
return self.enum_constant_expr(enum_id, enum_constant_id);
213267
}
214268

215269
let underlying_type_id = self.enum_integral_type(enum_id);
@@ -219,7 +273,7 @@ impl<'c> Translation<'c> {
219273
_ => signed_int_expr(value),
220274
};
221275

222-
self.enum_constructor_expr(enum_id, value)
276+
self.enum_constructor_expr(enum_id, value, false)
223277
}
224278

225279
/// Returns the id of the variant of `enum_id` whose value matches `value`, if any.
@@ -240,24 +294,65 @@ impl<'c> Translation<'c> {
240294
})
241295
}
242296

243-
fn enum_constructor_expr(&self, enum_id: CEnumId, value: Box<Expr>) -> Box<Expr> {
244-
let enum_name = self
245-
.type_converter
246-
.borrow()
247-
.resolve_decl_name(enum_id)
248-
.unwrap();
249-
self.add_import(enum_id, &enum_name);
250-
297+
fn enum_constructor_expr(
298+
&self,
299+
enum_id: CEnumId,
300+
value: Box<Expr>,
301+
use_self_ty: bool,
302+
) -> Box<Expr> {
251303
match self.tcfg.enum_mode {
252-
EnumMode::NewType => mk().call_expr(mk().ident_expr(enum_name), vec![value]),
253-
EnumMode::Consts => mk().cast_expr(value, mk().ident_ty(enum_name)),
304+
EnumMode::NewType => {
305+
let func = if use_self_ty {
306+
mk().ident_expr("Self")
307+
} else {
308+
let enum_name = self
309+
.type_converter
310+
.borrow()
311+
.resolve_decl_name(enum_id)
312+
.unwrap();
313+
self.add_import(enum_id, &enum_name);
314+
mk().ident_expr(enum_name)
315+
};
316+
317+
mk().call_expr(func, vec![value])
318+
}
319+
EnumMode::Consts => {
320+
let enum_name = self
321+
.type_converter
322+
.borrow()
323+
.resolve_decl_name(enum_id)
324+
.unwrap();
325+
self.add_import(enum_id, &enum_name);
326+
327+
mk().cast_expr(value, mk().ident_ty(enum_name))
328+
}
254329
}
255330
}
256331

257-
fn enum_constant_expr(&self, enum_constant_id: CEnumConstantId) -> Box<Expr> {
258-
let name = self.renamer.borrow().get(&enum_constant_id).unwrap();
259-
self.add_import(enum_constant_id, &name);
260-
mk().ident_expr(name)
332+
fn enum_constant_expr(&self, enum_id: CEnumId, enum_constant_id: CEnumConstantId) -> Box<Expr> {
333+
match self.tcfg.enum_mode {
334+
EnumMode::NewType => {
335+
let enum_name = self
336+
.type_converter
337+
.borrow()
338+
.resolve_decl_name(enum_id)
339+
.unwrap();
340+
let name = self
341+
.type_converter
342+
.borrow()
343+
.resolve_field_name(Some(enum_id), enum_constant_id)
344+
.unwrap();
345+
346+
self.add_import(enum_id, &enum_name);
347+
mk().path_expr(vec![enum_name, name])
348+
}
349+
350+
EnumMode::Consts => {
351+
let name = self.renamer.borrow().get(&enum_constant_id).unwrap();
352+
self.add_import(enum_constant_id, &name);
353+
mk().ident_expr(name)
354+
}
355+
}
261356
}
262357

263358
fn is_variant_of_enum(&self, enum_id: CEnumId, enum_constant_id: CEnumConstantId) -> bool {

c2rust-transpile/src/translator/mod.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,10 @@ pub fn translate(
769769
Union { ref name, .. } => some_type_name(name.as_ref().map(String::as_str)),
770770
Typedef { ref name, .. } => Name::Type(name),
771771
Function { ref name, .. } => Name::Var(name),
772-
EnumConstant { ref name, .. } => Name::Var(name),
772+
EnumConstant { ref name, .. } => match t.tcfg.enum_mode {
773+
EnumMode::NewType => Name::None,
774+
EnumMode::Consts => Name::Var(name),
775+
},
773776
Variable { ref ident, .. } if t.ast_context.c_decls_top.contains(&decl_id) => {
774777
Name::Var(ident)
775778
}
@@ -847,7 +850,6 @@ pub fn translate(
847850
let needs_export = match decl.kind {
848851
Struct { .. } => true,
849852
Enum { .. } => true,
850-
EnumConstant { .. } => true,
851853
Union { .. } => true,
852854
Typedef { .. } => {
853855
// Only check the key as opposed to `contains`
@@ -1919,11 +1921,13 @@ impl<'c> Translation<'c> {
19191921
)),
19201922

19211923
Enum {
1924+
ref variants,
19221925
integral_type: Some(integral_type),
19231926
..
1924-
} => self.convert_enum(decl_id, span, integral_type),
1927+
} => self.convert_enum(decl_id, span, integral_type, variants),
19251928

1926-
EnumConstant { value, .. } => self.convert_enum_constant(decl_id, span, value),
1929+
// EnumConstant is translated as part of Enum.
1930+
EnumConstant { .. } => Ok(ConvertedDecl::NoItem),
19271931

19281932
// We can allow non top level function declarations (i.e. extern
19291933
// declarations) without any problem. Clang doesn't support nested
@@ -3455,6 +3459,11 @@ impl<'c> Translation<'c> {
34553459
}
34563460

34573461
let varname = decl.get_name().expect("expected variable name").to_owned();
3462+
3463+
if let &CDeclKind::EnumConstant { .. } = decl {
3464+
return self.convert_enum_constant_decl_ref(ctx, decl_id, qual_ty);
3465+
}
3466+
34583467
let rustname = self
34593468
.renamer
34603469
.borrow_mut()
@@ -3472,13 +3481,6 @@ impl<'c> Translation<'c> {
34723481

34733482
let mut val = mk().path_expr(vec![rustname]);
34743483

3475-
// If the variable is actually an `EnumConstant`, we need to add a cast to the
3476-
// expected integral type.
3477-
if let &CDeclKind::EnumConstant { .. } = decl {
3478-
let enum_id = self.ast_context.parents[&decl_id];
3479-
return self.convert_cast_from_enum(ctx, enum_id, qual_ty, val);
3480-
}
3481-
34823484
// If we are referring to a function and need its address, we
34833485
// need to cast it to fn() to ensure that it has a real address.
34843486
let mut set_unsafe = false;

0 commit comments

Comments
 (0)