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
8 changes: 8 additions & 0 deletions c2rust-transpile/src/c_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,14 @@ impl TypedAstContext {
TypeOf(ty) => ty,
Paren(ty) => ty,
Auto(ty) => ty,

// As an exception, resolve typedefs in `prenamed_decls`, because they do not
// get translated as type aliases in the final code.
Typedef(decl) if self.prenamed_decls.contains_key(&decl) => match self[decl].kind {
CDeclKind::Typedef { typ: ty, .. } => ty.ctype,
_ => panic!("Typedef decl did not point to a typedef"),
},

_ => return typ,
};
self.resolve_type_id_no_typedef(ty)
Expand Down
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
212 changes: 139 additions & 73 deletions c2rust-transpile/src/translator/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ use c2rust_ast_builder::mk;
use proc_macro2::Span;
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, CQualTypeId, CTypeId, CTypeKind, ConstIntExpr,
};

impl<'c> Translation<'c> {
Expand All @@ -23,10 +21,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,94 +58,131 @@ 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 mut val = WithStmts::new_val(self.enum_constant_expr(enum_constant_id));

if !self.enum_constant_matches_type(target_type_id.ctype, enum_constant_id) {
// Add a cast to the expected integral type.
let enum_id = self.ast_context.parents[&enum_constant_id];
val = val.and_then_try(|val| {
self.convert_cast_from_enum(ctx, enum_id, target_type_id, val)
})?;
}

Ok(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>> {
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
// enum we are casting to. Here, we can just remove the extraneous cast instead of
// generating a new one.
CExprKind::DeclRef(_, enum_constant_id, _)
if self.is_variant_of_enum(enum_id, enum_constant_id) =>
{
// `enum`s shouldn't need portable `override_ty`s.
let expr_is_macro = self.expr_is_expanded_macro(ctx, expr, None);

// 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));
}
}
mut val: Box<Expr>,
) -> TranslationResult<WithStmts<Box<Expr>>> {
// 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));
}

CExprKind::Literal(_, CLiteral::Integer(i, _)) => {
return Ok(self.enum_for_i64(enum_type_id, i as i64));
}
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 => {}
}

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)));
}
}
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));
}
}
}

let target_ty = self.convert_type(enum_type_id)?;
Ok(mk().cast_expr(val, target_ty))
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),
};

pub 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 +194,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,16 +221,34 @@ impl<'c> Translation<'c> {
mk().ident_expr(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,
_ => panic!("{:?} does not point to an `enum` declaration", enum_id),
};
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);

variants.contains(&enum_constant_id)
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)),
}
}

pub fn enum_constant_matches_type(
&self,
type_id: CTypeId,
enum_constant_id: CEnumConstantId,
) -> bool {
let type_enum_id = match self.ast_context.resolve_type_no_typedef(type_id).kind {
CTypeKind::Enum(enum_id) => enum_id,
_ => return false,
};
let constant_enum_id = self.ast_context.parents[&enum_constant_id];
type_enum_id == constant_enum_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
13 changes: 13 additions & 0 deletions c2rust-transpile/src/translator/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ impl<'c> Translation<'c> {
base: IntBase,
negative: bool,
) -> TranslationResult<Box<Expr>> {
let type_resolved_id = self.ast_context.resolve_type_id(ty.ctype);

if let CTypeKind::Enum(enum_id) = self.ast_context[type_resolved_id].kind {
let mut val = val as i64;

if negative {
val = -val;
}

return Ok(self.enum_for_i64(enum_id, val));
}

let lit = match base {
IntBase::Dec => mk().int_unsuffixed_lit(val),
IntBase::Hex => mk().float_unsuffixed_lit(&format!("0x{:x}", val)),
Expand All @@ -35,6 +47,7 @@ impl<'c> Translation<'c> {
pub fn literal_matches_ty(&self, lit: &CLiteral, ty: CQualTypeId, is_negated: bool) -> bool {
let ty_kind = &self.ast_context.resolve_type(ty.ctype).kind;
match *lit {
CLiteral::Integer(_, _) if ty_kind.is_enum() => true,
CLiteral::Integer(value, _) | CLiteral::Character(value)
if ty_kind.is_integral_type() && !ty_kind.is_bool() =>
{
Expand Down
Loading
Loading