Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion c2rust-transpile/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use c2rust_ast_exporter as ast_exporter;

use crate::build_files::{emit_build_files, get_build_dir, CrateConfig};
use crate::compile_cmds::get_compile_commands;
pub use crate::translator::ReplaceMode;
pub use crate::translator::{EnumMode, ReplaceMode};
use std::prelude::v1::Vec;

type PragmaVec = Vec<(&'static str, Vec<&'static str>)>;
Expand Down Expand Up @@ -108,6 +108,7 @@ pub struct TranspilerConfig {
pub log_level: log::LevelFilter,
pub edition: RustEdition,
pub deny_unsafe_op_in_unsafe_fn: bool,
pub enum_mode: EnumMode,

/// Run `c2rust-postprocess` after transpiling and potentially refactoring.
pub postprocess: bool,
Expand Down
176 changes: 136 additions & 40 deletions c2rust-transpile/src/translator/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ use syn::Expr;
use crate::c_ast::CUnOp;
use crate::{
diagnostics::TranslationResult,
translator::{signed_int_expr, ConvertedDecl, ExprContext, Translation},
translator::{signed_int_expr, ConvertedDecl, EnumMode, ExprContext, Translation},
with_stmts::WithStmts,
CDeclKind, CEnumConstantId, CEnumId, CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId,
CTypeKind, ConstIntExpr,
CDeclKind, CEnumConstantId, CEnumId, CExprId, CExprKind, CLiteral, CQualTypeId, CTypeKind,
ConstIntExpr,
};

impl<'c> Translation<'c> {
Expand All @@ -23,10 +23,24 @@ impl<'c> Translation<'c> {
.borrow()
.resolve_decl_name(enum_id)
.expect("Enums should already be renamed");
let ty = self.convert_type(integral_type.ctype)?;
Ok(ConvertedDecl::Item(
mk().span(span).pub_().type_item(enum_name, ty),
))
let integral_type_rs = self.convert_type(integral_type.ctype)?;
let item = match self.tcfg.enum_mode {
EnumMode::NewType => {
let field = mk().pub_().enum_field(integral_type_rs);
mk().span(span)
.call_attr("derive", vec!["Clone", "Copy"])
.call_attr("repr", vec!["transparent"])
.pub_()
.struct_item(enum_name, vec![field], true)
}

EnumMode::Consts => mk()
.span(span)
.pub_()
.type_item(enum_name, integral_type_rs),
};

Ok(ConvertedDecl::Item(item))
}

pub fn convert_enum_constant(
Expand All @@ -46,48 +60,81 @@ impl<'c> Translation<'c> {
.borrow()
.resolve_decl_name(enum_id)
.expect("Enums should already be renamed");
self.add_import(enum_id, &enum_name);

let ty = mk().ident_ty(enum_name);
let val = match value {
ConstIntExpr::I(value) => signed_int_expr(value),
ConstIntExpr::U(value) => mk().lit_expr(mk().int_unsuffixed_lit(value as u128)),
};
let init = self.enum_constructor_expr(enum_id, val);

Ok(ConvertedDecl::Item(
mk().span(span).pub_().const_item(name, ty, val),
mk().span(span).pub_().const_item(name, ty, init),
))
}

pub fn convert_enum_zero_initializer(&self, type_id: CTypeId) -> WithStmts<Box<Expr>> {
WithStmts::new_val(self.enum_for_i64(type_id, 0))
pub fn convert_enum_zero_initializer(&self, enum_id: CEnumId) -> WithStmts<Box<Expr>> {
WithStmts::new_val(self.enum_for_i64(enum_id, 0))
}

/// Translates a `DeclRef` for an `EnumConstant`.
pub fn convert_enum_constant_decl_ref(
&self,
ctx: ExprContext,
enum_constant_id: CEnumConstantId,
target_type_id: CQualTypeId,
) -> TranslationResult<WithStmts<Box<Expr>>> {
let enum_id = self.ast_context.parents[&enum_constant_id];
let val = self.enum_constant_expr(enum_constant_id);

// Add a cast to the expected integral type.
self.convert_cast_from_enum(ctx, enum_id, target_type_id, val)
}

/// Translate a cast where the source type, but not the target type, is an `enum` type.
pub fn convert_cast_from_enum(
&self,
target_cty: CTypeId,
val: Box<Expr>,
) -> TranslationResult<Box<Expr>> {
// Convert it to the expected integral type.
let ty = self.convert_type(target_cty)?;
Ok(mk().cast_expr(val, ty))
ctx: ExprContext,
enum_id: CEnumId,
target_cty: CQualTypeId,
mut val: Box<Expr>,
) -> TranslationResult<WithStmts<Box<Expr>>> {
match self.tcfg.enum_mode {
// First extract the enum's inner type...
EnumMode::NewType => val = self.integer_from_enum(val),
EnumMode::Consts => {}
}

// Cast from the enum's integral type to the expected integral type.
let source_cty = self.enum_integral_type(enum_id);
self.convert_cast(
ctx,
source_cty,
target_cty,
WithStmts::new_val(val),
None,
None,
None,
)
}

/// Translate a cast where the target type is an `enum` type.
///
/// When translating variable references to `EnumConstant`s, we always insert casts to the
/// expected type. In C, `EnumConstant`s have some integral type, _not_ the enum type. However,
/// if we then immediately have a cast to convert this variable back into an enum type, we would
/// like to produce Rust with _no_ casts. This function handles this simplification.
/// Gets the inner integral value of an enum value.
pub fn integer_from_enum(&self, val: Box<Expr>) -> Box<Expr> {
match self.tcfg.enum_mode {
EnumMode::NewType => mk().anon_field_expr(val, 0),
EnumMode::Consts => val,
}
}

/// Translates a cast where the target type is an `enum` type.
pub fn convert_cast_to_enum(
&self,
ctx: ExprContext,
enum_type_id: CTypeId,
mut source_cty: CQualTypeId,
enum_id: CEnumId,
expr: Option<CExprId>,
val: Box<Expr>,
) -> TranslationResult<Box<Expr>> {
mut val: Box<Expr>,
) -> TranslationResult<WithStmts<Box<Expr>>> {
if let Some(expr) = expr {
match self.ast_context[expr].kind {
// This is the case of finding a variable which is an `EnumConstant` of the same
Expand All @@ -102,38 +149,74 @@ impl<'c> Translation<'c> {
// If this DeclRef expanded to a const macro, we actually need to insert a cast,
// because the translation of a const macro skips implicit casts in its context.
if !expr_is_macro {
return Ok(self.enum_constant_expr(enum_constant_id));
val = self.enum_constant_expr(enum_constant_id);
return Ok(WithStmts::new_val(val));
}
}

CExprKind::Literal(_, CLiteral::Integer(i, _)) => {
return Ok(self.enum_for_i64(enum_type_id, i as i64));
val = self.enum_for_i64(enum_id, i as i64);
return Ok(WithStmts::new_val(val));
}

CExprKind::Unary(_, CUnOp::Negate, subexpr_id, _) => {
if let &CExprKind::Literal(_, CLiteral::Integer(i, _)) =
&self.ast_context[subexpr_id].kind
{
return Ok(self.enum_for_i64(enum_type_id, -(i as i64)));
val = self.enum_for_i64(enum_id, -(i as i64));
return Ok(WithStmts::new_val(val));
}
}

_ => {}
}
}

let target_ty = self.convert_type(enum_type_id)?;
Ok(mk().cast_expr(val, target_ty))
// We could be casting from enum to enum...
if let CTypeKind::Enum(source_enum_id) =
self.ast_context.resolve_type(source_cty.ctype).kind
{
// Casting to ourselves, the audacity!
if source_enum_id == enum_id {
return Ok(WithStmts::new_val(val));
}

match self.tcfg.enum_mode {
// Enum-to-enum casts need to be translated via the inner value as an intermediate.
EnumMode::NewType => val = self.integer_from_enum(val),
EnumMode::Consts => {}
}

source_cty = self.enum_integral_type(source_enum_id);
}

let enum_integral_type = self.enum_integral_type(enum_id);
let mut val = WithStmts::new_val(val);

match self.tcfg.enum_mode {
EnumMode::NewType => {
val =
self.convert_cast(ctx, source_cty, enum_integral_type, val, None, None, None)?;
val = val.map(|val| self.enum_constructor_expr(enum_id, val));
}

EnumMode::Consts => {
let source_type_kind = &self.ast_context.resolve_type(source_cty.ctype).kind;
let enum_integral_type_kind =
&self.ast_context.resolve_type(enum_integral_type.ctype).kind;

if source_type_kind != enum_integral_type_kind {
val = val.map(|val| self.enum_constructor_expr(enum_id, val));
}
}
}

Ok(val)
}

/// Given an integer value this attempts to either generate the corresponding enum
/// variant directly, otherwise it converts a number to the enum type.
fn enum_for_i64(&self, enum_type_id: CTypeId, value: i64) -> Box<Expr> {
let enum_id = match self.ast_context.resolve_type(enum_type_id).kind {
CTypeKind::Enum(enum_id) => enum_id,
_ => panic!("{:?} does not point to an `enum` type", enum_type_id),
};

fn enum_for_i64(&self, enum_id: CEnumId, value: i64) -> Box<Expr> {
if let Some(enum_constant_id) = self.enum_variant_for_i64(enum_id, value) {
return self.enum_constant_expr(enum_constant_id);
}
Expand All @@ -145,8 +228,7 @@ impl<'c> Translation<'c> {
_ => signed_int_expr(value),
};

let target_ty = self.convert_type(enum_type_id).unwrap();
mk().cast_expr(value, target_ty)
self.enum_constructor_expr(enum_id, value)
}

/// Returns the id of the variant of `enum_id` whose value matches `value`, if any.
Expand All @@ -173,6 +255,20 @@ impl<'c> Translation<'c> {
mk().ident_expr(name)
}

fn enum_constructor_expr(&self, enum_id: CEnumId, value: Box<Expr>) -> Box<Expr> {
let enum_name = self
.type_converter
.borrow()
.resolve_decl_name(enum_id)
.unwrap();
self.add_import(enum_id, &enum_name);

match self.tcfg.enum_mode {
EnumMode::NewType => mk().call_expr(mk().ident_expr(enum_name), vec![value]),
EnumMode::Consts => mk().cast_expr(value, mk().ident_ty(enum_name)),
}
}

fn is_variant_of_enum(&self, enum_id: CEnumId, enum_constant_id: CEnumConstantId) -> bool {
let variants = match self.ast_context[enum_id].kind {
CDeclKind::Enum { ref variants, .. } => variants,
Expand All @@ -182,7 +278,7 @@ impl<'c> Translation<'c> {
variants.contains(&enum_constant_id)
}

fn enum_integral_type(&self, enum_id: CEnumId) -> CQualTypeId {
pub fn enum_integral_type(&self, enum_id: CEnumId) -> CQualTypeId {
match self.ast_context[enum_id].kind {
CDeclKind::Enum {
integral_type: Some(integral_type),
Expand Down
Loading
Loading