Skip to content

Commit 28f1743

Browse files
committed
transpile: Translate EnumConstant with associated constants
1 parent 7b1c4ba commit 28f1743

13 files changed

Lines changed: 245 additions & 95 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: 150 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use c2rust_ast_builder::mk;
22
use proc_macro2::Span;
3-
use syn::Expr;
3+
use syn::{Expr, ImplItem};
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,32 +18,67 @@ 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");
2628

27-
let item = match self.tcfg.enum_mode {
29+
match self.tcfg.enum_mode {
2830
EnumMode::NewType => {
2931
let field = mk()
3032
.pub_()
3133
.enum_field(self.convert_type(integral_type.ctype)?);
32-
mk().span(span)
34+
let item = mk()
35+
.span(span)
3336
.call_attr("derive", vec!["Clone", "Copy"])
3437
.call_attr("repr", vec!["transparent"])
3538
.pub_()
36-
.struct_item(enum_name, vec![field], true)
39+
.struct_item(enum_name, vec![field], true);
40+
41+
if variants.is_empty() {
42+
return Ok(ConvertedDecl::Item(item));
43+
}
44+
45+
let self_ty = mk().ident_ty("Self");
46+
let constants: Vec<ImplItem> = variants
47+
.iter()
48+
.map(|&enum_constant_id| {
49+
let span = self
50+
.get_span(SomeId::Decl(enum_constant_id))
51+
.unwrap_or_else(Span::call_site);
52+
53+
let (name, value) = match self.ast_context[enum_constant_id].kind {
54+
CDeclKind::EnumConstant {
55+
ref name, value, ..
56+
} => (name, value),
57+
_ => panic!("{:?} does not point to an enum variant", enum_constant_id),
58+
};
59+
let name = self.type_converter.borrow_mut().declare_field_name(
60+
enum_id,
61+
enum_constant_id,
62+
name,
63+
);
64+
let init = self.make_enum_constant_init(enum_id, value);
65+
66+
mk().span(span)
67+
.pub_()
68+
.const_impl_item(name, self_ty.clone(), init)
69+
})
70+
.collect();
71+
let impl_block = mk().impl_item(mk().ident_ty(enum_name), constants);
72+
73+
Ok(ConvertedDecl::Items(vec![item, impl_block]))
3774
}
3875

3976
EnumMode::Consts => {
4077
let ty = self.convert_type(integral_type.ctype)?;
41-
mk().span(span).pub_().type_item(enum_name, ty)
78+
let item = mk().span(span).pub_().type_item(enum_name, ty);
79+
Ok(ConvertedDecl::Item(item))
4280
}
43-
};
44-
45-
Ok(ConvertedDecl::Item(item))
81+
}
4682
}
4783

4884
pub fn convert_enum_constant(
@@ -51,34 +87,64 @@ impl<'c> Translation<'c> {
5187
span: Span,
5288
value: ConstIntExpr,
5389
) -> 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");
90+
match self.tcfg.enum_mode {
91+
EnumMode::NewType => Ok(ConvertedDecl::NoItem),
92+
93+
EnumMode::Consts => {
94+
let name = self
95+
.renamer
96+
.borrow_mut()
97+
.get(&enum_constant_id)
98+
.expect("Enum constant not named");
99+
let enum_id = self.ast_context.parents[&enum_constant_id];
100+
let enum_name = self
101+
.type_converter
102+
.borrow()
103+
.resolve_decl_name(enum_id)
104+
.expect("Enums should already be renamed");
105+
let ty = mk().ident_ty(enum_name);
106+
let init = self.make_enum_constant_init(enum_id, value);
107+
108+
Ok(ConvertedDecl::Item(
109+
mk().span(span).pub_().const_item(name, ty, init),
110+
))
111+
}
112+
}
113+
}
65114

66-
let ty = mk().ident_ty(enum_name);
115+
fn make_enum_constant_init(&self, enum_id: CEnumId, value: ConstIntExpr) -> Box<Expr> {
67116
let val = match value {
68117
ConstIntExpr::I(value) => signed_int_expr(value),
69118
ConstIntExpr::U(value) => mk().lit_expr(mk().int_unsuffixed_lit(value as u128)),
70119
};
71-
let init = self.enum_constructor_expr(enum_id, val);
72-
73-
Ok(ConvertedDecl::Item(
74-
mk().span(span).pub_().const_item(name, ty, init),
75-
))
120+
self.enum_constructor_expr(enum_id, val, true)
76121
}
77122

78123
pub fn convert_enum_zero_initializer(&self, enum_id: CEnumId) -> WithStmts<Box<Expr>> {
79124
WithStmts::new_val(self.enum_for_i64(enum_id, 0))
80125
}
81126

127+
/// Translates a `DeclRef` for an `EnumConstant`.
128+
pub fn convert_enum_constant_decl_ref(
129+
&self,
130+
ctx: ExprContext,
131+
enum_constant_id: CEnumConstantId,
132+
target_cty: CQualTypeId,
133+
) -> TranslationResult<WithStmts<Box<Expr>>> {
134+
let enum_id = self.ast_context.parents[&enum_constant_id];
135+
let val = self.enum_constant_expr(enum_id, enum_constant_id);
136+
137+
// If our target type is this enum type, don't bother with a cast.
138+
if matches!(
139+
self.ast_context.resolve_type(target_cty.ctype).kind,
140+
CTypeKind::Enum(target_enum_id) if target_enum_id == enum_id
141+
) {
142+
Ok(WithStmts::new_val(val))
143+
} else {
144+
self.convert_cast_from_enum(ctx, enum_id, target_cty, val)
145+
}
146+
}
147+
82148
/// Translate a cast where the source type, but not the target type, is an `enum` type.
83149
pub fn convert_cast_from_enum(
84150
&self,
@@ -145,7 +211,7 @@ impl<'c> Translation<'c> {
145211
// If this DeclRef expanded to a const macro, we actually need to insert a cast,
146212
// because the translation of a const macro skips implicit casts in its context.
147213
if !expr_is_macro {
148-
val = self.enum_constant_expr(enum_constant_id);
214+
val = self.enum_constant_expr(enum_id, enum_constant_id);
149215
return Ok(WithStmts::new_val(val));
150216
}
151217
}
@@ -202,14 +268,14 @@ impl<'c> Translation<'c> {
202268
}
203269

204270
// Construct a new enum value.
205-
Ok(val.map(|val| self.enum_constructor_expr(enum_id, val)))
271+
Ok(val.map(|val| self.enum_constructor_expr(enum_id, val, false)))
206272
}
207273

208274
/// Given an integer value this attempts to either generate the corresponding enum
209275
/// variant directly, otherwise it converts a number to the enum type.
210276
fn enum_for_i64(&self, enum_id: CEnumId, value: i64) -> Box<Expr> {
211277
if let Some(enum_constant_id) = self.enum_variant_for_i64(enum_id, value) {
212-
return self.enum_constant_expr(enum_constant_id);
278+
return self.enum_constant_expr(enum_id, enum_constant_id);
213279
}
214280

215281
let underlying_type_id = self.enum_integral_type(enum_id);
@@ -219,7 +285,7 @@ impl<'c> Translation<'c> {
219285
_ => signed_int_expr(value),
220286
};
221287

222-
self.enum_constructor_expr(enum_id, value)
288+
self.enum_constructor_expr(enum_id, value, false)
223289
}
224290

225291
/// Returns the id of the variant of `enum_id` whose value matches `value`, if any.
@@ -240,24 +306,65 @@ impl<'c> Translation<'c> {
240306
})
241307
}
242308

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-
309+
fn enum_constructor_expr(
310+
&self,
311+
enum_id: CEnumId,
312+
value: Box<Expr>,
313+
use_self_ty: bool,
314+
) -> Box<Expr> {
251315
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)),
316+
EnumMode::NewType => {
317+
let func = if use_self_ty {
318+
mk().ident_expr("Self")
319+
} else {
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+
mk().ident_expr(enum_name)
327+
};
328+
329+
mk().call_expr(func, vec![value])
330+
}
331+
EnumMode::Consts => {
332+
let enum_name = self
333+
.type_converter
334+
.borrow()
335+
.resolve_decl_name(enum_id)
336+
.unwrap();
337+
self.add_import(enum_id, &enum_name);
338+
339+
mk().cast_expr(value, mk().ident_ty(enum_name))
340+
}
254341
}
255342
}
256343

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)
344+
fn enum_constant_expr(&self, enum_id: CEnumId, enum_constant_id: CEnumConstantId) -> Box<Expr> {
345+
match self.tcfg.enum_mode {
346+
EnumMode::NewType => {
347+
let enum_name = self
348+
.type_converter
349+
.borrow()
350+
.resolve_decl_name(enum_id)
351+
.unwrap();
352+
let name = self
353+
.type_converter
354+
.borrow()
355+
.resolve_field_name(Some(enum_id), enum_constant_id)
356+
.unwrap();
357+
358+
self.add_import(enum_id, &enum_name);
359+
mk().path_expr(vec![enum_name, name])
360+
}
361+
362+
EnumMode::Consts => {
363+
let name = self.renamer.borrow().get(&enum_constant_id).unwrap();
364+
self.add_import(enum_constant_id, &name);
365+
mk().ident_expr(name)
366+
}
367+
}
261368
}
262369

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

c2rust-transpile/src/translator/mod.rs

Lines changed: 11 additions & 10 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,9 +1921,10 @@ 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

19261929
EnumConstant { value, .. } => self.convert_enum_constant(decl_id, span, value),
19271930

@@ -3455,6 +3458,11 @@ impl<'c> Translation<'c> {
34553458
}
34563459

34573460
let varname = decl.get_name().expect("expected variable name").to_owned();
3461+
3462+
if let &CDeclKind::EnumConstant { .. } = decl {
3463+
return self.convert_enum_constant_decl_ref(ctx, decl_id, qual_ty);
3464+
}
3465+
34583466
let rustname = self
34593467
.renamer
34603468
.borrow_mut()
@@ -3472,13 +3480,6 @@ impl<'c> Translation<'c> {
34723480

34733481
let mut val = mk().path_expr(vec![rustname]);
34743482

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-
34823483
// If we are referring to a function and need its address, we
34833484
// need to cast it to fn() to ensure that it has a real address.
34843485
let mut set_unsafe = false;

0 commit comments

Comments
 (0)