diff --git a/c2rust-transpile/src/lib.rs b/c2rust-transpile/src/lib.rs index 20fdc8cfea..a1db5911cc 100644 --- a/c2rust-transpile/src/lib.rs +++ b/c2rust-transpile/src/lib.rs @@ -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>)>; @@ -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, diff --git a/c2rust-transpile/src/translator/enums.rs b/c2rust-transpile/src/translator/enums.rs index d1a9c0b8c9..1458090934 100644 --- a/c2rust-transpile/src/translator/enums.rs +++ b/c2rust-transpile/src/translator/enums.rs @@ -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> { @@ -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( @@ -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> { - WithStmts::new_val(self.enum_for_i64(type_id, 0)) + pub fn convert_enum_zero_initializer(&self, enum_id: CEnumId) -> WithStmts> { + 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>> { + 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, - ) -> TranslationResult> { - // 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, + ) -> TranslationResult>> { + 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) -> Box { + 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, - val: Box, - ) -> TranslationResult> { + mut val: Box, + ) -> TranslationResult>> { 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 @@ -102,19 +149,22 @@ 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)); } } @@ -122,18 +172,51 @@ impl<'c> Translation<'c> { } } - 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 { - 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 { if let Some(enum_constant_id) = self.enum_variant_for_i64(enum_id, value) { return self.enum_constant_expr(enum_constant_id); } @@ -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. @@ -173,6 +255,20 @@ impl<'c> Translation<'c> { mk().ident_expr(name) } + fn enum_constructor_expr(&self, enum_id: CEnumId, value: Box) -> Box { + 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, @@ -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), diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index a86e745bdd..ed49f6163f 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -3002,135 +3002,9 @@ impl<'c> Translation<'c> { } } - DeclRef(qual_ty, decl_id, lrvalue) => { - let decl = &self - .ast_context - .get_decl(&decl_id) - .ok_or_else(|| format_err!("Missing declref {:?}", decl_id))? - .kind; - if ctx.expanding_macro.is_some() { - // TODO Determining which declarations have been declared within the scope of the const macro expr - // vs. which are out-of-scope of the const macro is non-trivial, - // so for now, we don't allow const macros referencing any declarations. - return Err(format_translation_err!( - self.ast_context.display_loc(src_loc), - "Cannot yet refer to declarations in a const expr", - )); - - #[allow(unreachable_code)] // TODO temporary (see above). - if let CDeclKind::Variable { - has_static_duration: true, - .. - } = decl - { - return Err(format_translation_err!( - self.ast_context.display_loc(src_loc), - "Cannot refer to static duration variable in a const expression", - )); - } - } - - let varname = decl.get_name().expect("expected variable name").to_owned(); - let rustname = self - .renamer - .borrow_mut() - .get(&decl_id) - .ok_or_else(|| format_err!("name not declared: '{}'", varname))?; - - // Import the referenced global decl into our submodule - if self.tcfg.reorganize_definitions { - self.add_import(decl_id, &rustname); - // match decl { - // CDeclKind::Variable { is_defn: false, .. } => {} - // _ => self.add_import(decl_id, &rustname), - // } - } - - let mut val = mk().path_expr(vec![rustname]); - let mut set_unsafe = false; - - match decl { - CDeclKind::EnumConstant { .. } => { - // If the variable is actually an `EnumConstant`, we need to add a cast to - // the expected integral type. - val = self.convert_cast_from_enum(qual_ty.ctype, val)?; - } - - CDeclKind::Function { parameters, .. } => { - // If we are referring to a function and need its address, we - // need to cast it to fn() to ensure that it has a real address. - if ctx.needs_address() { - let ty = self.convert_type(qual_ty.ctype)?; - let actual_ty = self - .type_converter - .borrow_mut() - .knr_function_type_with_parameters( - &self.ast_context, - qual_ty.ctype, - parameters, - )?; - if let Some(actual_ty) = actual_ty { - if actual_ty != ty { - // If we're casting a concrete function to - // a K&R function pointer type, use transmute - self.import_type(qual_ty.ctype); - - val = transmute_expr(actual_ty, ty, val); - set_unsafe = true; - } - } else { - let decl_kind = &self.ast_context[decl_id].kind; - let kind_with_declared_args = - self.ast_context.fn_decl_ty_with_declared_args(decl_kind); - - if let Some(ty) = self - .ast_context - .type_for_kind(&kind_with_declared_args) - .map(CQualTypeId::new) - { - let ty = self.convert_type(ty.ctype)?; - val = mk().cast_expr(val, ty); - } else { - val = mk().cast_expr(val, ty); - } - } - } - } - - CDeclKind::Variable { - has_static_duration, - has_thread_duration, - .. - } => { - // Accessing a static variable is unsafe. - // In the current nightly, this applies also to taking a raw pointer, - // but this requirement was removed in later versions of the - // `raw_ref_op` feature. - if (*has_static_duration || *has_thread_duration) - && (self.tcfg.edition < Edition2024 || !ctx.needs_address()) - { - set_unsafe = true; - } - } - - _ => {} - } - - if let CTypeKind::VariableArray(..) = - self.ast_context.resolve_type(qual_ty.ctype).kind - { - val = mk().method_call_expr(val, "as_mut_ptr", vec![]); - } - - // if the context wants a different type, add a cast - if let Some(expected_ty) = override_ty { - if lrvalue.is_rvalue() && expected_ty != qual_ty { - val = mk().cast_expr(val, self.convert_type(expected_ty.ctype)?); - } - } - - Ok(WithStmts::new_val(val).merge_unsafe(set_unsafe)) - } + DeclRef(qual_ty, decl_id, lrvalue) => self + .convert_decl_ref(ctx, qual_ty, decl_id, lrvalue, override_ty) + .map_err(|e| e.add_loc(self.ast_context.display_loc(src_loc))), OffsetOf(ty, ref kind) => match kind { OffsetOfKind::Constant(val) => Ok(WithStmts::new_val(self.mk_int_lit( @@ -3453,6 +3327,139 @@ impl<'c> Translation<'c> { } } + fn convert_decl_ref( + &self, + ctx: ExprContext, + qual_ty: CQualTypeId, + decl_id: CDeclId, + lrvalue: LRValue, + override_ty: Option, + ) -> TranslationResult>> { + let decl = &self + .ast_context + .get_decl(&decl_id) + .ok_or_else(|| format_err!("Missing declref {:?}", decl_id))? + .kind; + if ctx.expanding_macro.is_some() { + // TODO Determining which declarations have been declared within the scope of the const macro expr + // vs. which are out-of-scope of the const macro is non-trivial, + // so for now, we don't allow const macros referencing any declarations. + return Err(format_translation_err!( + None, + "Cannot yet refer to declarations in a const expr", + )); + + #[allow(unreachable_code)] // TODO temporary (see above). + if let CDeclKind::Variable { + has_static_duration: true, + .. + } = decl + { + return Err(format_translation_err!( + None, + "Cannot refer to static duration variable in a const expression", + )); + } + } + + if let CDeclKind::EnumConstant { .. } = decl { + return self.convert_enum_constant_decl_ref(ctx, decl_id, qual_ty); + } + + let varname = decl.get_name().expect("expected variable name").to_owned(); + let rustname = self + .renamer + .borrow_mut() + .get(&decl_id) + .ok_or_else(|| format_err!("name not declared: '{}'", varname))?; + + // Import the referenced global decl into our submodule + if self.tcfg.reorganize_definitions { + self.add_import(decl_id, &rustname); + // match decl { + // CDeclKind::Variable { is_defn: false, .. } => {} + // _ => self.add_import(decl_id, &rustname), + // } + } + + let mut val = mk().path_expr(vec![rustname]); + let mut set_unsafe = false; + + match decl { + CDeclKind::Function { parameters, .. } => { + // If we are referring to a function and need its address, we + // need to cast it to fn() to ensure that it has a real address. + if ctx.needs_address() { + let ty = self.convert_type(qual_ty.ctype)?; + let actual_ty = self + .type_converter + .borrow_mut() + .knr_function_type_with_parameters( + &self.ast_context, + qual_ty.ctype, + parameters, + )?; + if let Some(actual_ty) = actual_ty { + if actual_ty != ty { + // If we're casting a concrete function to + // a K&R function pointer type, use transmute + self.import_type(qual_ty.ctype); + + val = transmute_expr(actual_ty, ty, val); + set_unsafe = true; + } + } else { + let decl_kind = &self.ast_context[decl_id].kind; + let kind_with_declared_args = + self.ast_context.fn_decl_ty_with_declared_args(decl_kind); + + if let Some(ty) = self + .ast_context + .type_for_kind(&kind_with_declared_args) + .map(CQualTypeId::new) + { + let ty = self.convert_type(ty.ctype)?; + val = mk().cast_expr(val, ty); + } else { + val = mk().cast_expr(val, ty); + } + } + } + } + + CDeclKind::Variable { + has_static_duration, + has_thread_duration, + .. + } => { + // Accessing a static variable is unsafe. + // In the current nightly, this applies also to taking a raw pointer, + // but this requirement was removed in later versions of the + // `raw_ref_op` feature. + if (*has_static_duration || *has_thread_duration) + && (self.tcfg.edition < Edition2024 || !ctx.needs_address()) + { + set_unsafe = true; + } + } + + _ => {} + } + + if let CTypeKind::VariableArray(..) = self.ast_context.resolve_type(qual_ty.ctype).kind { + val = mk().method_call_expr(val, "as_mut_ptr", vec![]); + } + + // if the context wants a different type, add a cast + if let Some(expected_ty) = override_ty { + if lrvalue.is_rvalue() && expected_ty != qual_ty { + val = mk().cast_expr(val, self.convert_type(expected_ty.ctype)?); + } + } + + Ok(WithStmts::new_val(val).merge_unsafe(set_unsafe)) + } + pub fn convert_constant(&self, constant: ConstIntExpr) -> TranslationResult> { let expr = match constant { ConstIntExpr::U(n) => mk().lit_expr(mk().int_unsuffixed_lit(n as u128)), @@ -3611,20 +3618,16 @@ impl<'c> Translation<'c> { match kind { CastKind::BitCast | CastKind::NoOp => { - self.convert_pointer_to_pointer_cast(source_cty.ctype, target_cty.ctype, val) + self.convert_pointer_to_pointer_cast(source_cty, target_cty, val) } CastKind::IntegralToPointer => { - self.convert_integral_to_pointer_cast(ctx, source_cty.ctype, target_cty.ctype, val) + self.convert_integral_to_pointer_cast(ctx, source_cty, target_cty, val) } - CastKind::PointerToIntegral => self.convert_pointer_to_integral_cast( - ctx, - source_cty.ctype, - target_cty.ctype, - val, - expr, - ), + CastKind::PointerToIntegral => { + self.convert_pointer_to_integral_cast(ctx, source_cty, target_cty, val, expr) + } CastKind::IntegralCast | CastKind::FloatingCast @@ -3658,17 +3661,18 @@ impl<'c> Translation<'c> { self.ast_context[source_cty.ctype].kind { self.f128_cast_to(val, target_ty_kind) - } else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { - // Casts targeting `enum` types... - val.try_map(|val| { - self.convert_cast_to_enum(ctx, target_cty.ctype, enum_decl_id, expr, val) + } else if let &CTypeKind::Enum(enum_id) = target_ty_kind { + val.and_then_try(|val| { + self.convert_cast_to_enum(ctx, source_cty, enum_id, expr, val) }) } else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() { Ok(val.map(|val| { mk().cast_expr(mk().cast_expr(val, mk().path_ty(vec!["u8"])), target_ty) })) - } else if let &CTypeKind::Enum(..) = source_ty_kind { - val.try_map(|val| self.convert_cast_from_enum(target_cty.ctype, val)) + } else if let &CTypeKind::Enum(enum_id) = source_ty_kind { + val.and_then_try(|val| { + self.convert_cast_from_enum(ctx, enum_id, target_cty, val) + }) } else { Ok(val.map(|val| mk().cast_expr(val, target_ty))) } @@ -3933,8 +3937,7 @@ impl<'c> Translation<'c> { field.map(|field| mk().struct_expr(vec![name], vec![field])) } - // Transmute the number `0` into the enum type - CDeclKind::Enum { .. } => self.convert_enum_zero_initializer(type_id), + CDeclKind::Enum { .. } => self.convert_enum_zero_initializer(decl_id), _ => { return Err(TranslationError::generic( @@ -4038,7 +4041,7 @@ impl<'c> Translation<'c> { } let val = if ty.is_enum() { - mk().cast_expr(val, mk().path_ty(vec!["u64"])) + self.integer_from_enum(val) } else { val }; @@ -4360,3 +4363,9 @@ fn neg_expr(arg: Box) -> Box { fn wrapping_neg_expr(arg: Box) -> Box { mk().method_call_expr(arg, "wrapping_neg", vec![]) } + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EnumMode { + NewType, + Consts, +} diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index a03f240ffe..7b9618bf2f 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -638,26 +638,35 @@ impl<'c> Translation<'c> { _ => mk().lit_expr(mk().int_unsuffixed_lit(1)), }; - let one_type_id = - if let CTypeKind::Pointer(..) = self.ast_context.resolve_type(arg_type.ctype).kind { - CQualTypeId::new( + let mut rhs_type_id = arg_type; + let mut compute_lhs_type_id = arg_type; + let mut compute_res_type_id = ty; + + match self.ast_context.resolve_type(arg_type.ctype).kind { + CTypeKind::Pointer(..) => { + rhs_type_id = CQualTypeId::new( self.ast_context .type_for_kind(&CTypeKind::Int) .ok_or_else(|| format_err!("couldn't find type for CTypeKind::Int"))?, - ) - } else { - arg_type - }; + ); + } + CTypeKind::Enum(enum_id) => { + rhs_type_id = self.enum_integral_type(enum_id); + compute_lhs_type_id = rhs_type_id; + compute_res_type_id = rhs_type_id; + } + _ => {} + }; self.convert_assignment_operator_with_rhs( ctx.used(), op, ty, arg, - one_type_id, + rhs_type_id, WithStmts::new_val(one), - Some(arg_type), - Some(ty), + Some(compute_lhs_type_id), + Some(compute_res_type_id), ) } diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index a15fef518c..bd63c4fbc7 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -246,6 +246,10 @@ impl<'c> Translation<'c> { let lhs_node_type = lhs_node .get_type() .ok_or_else(|| format_err!("lhs node bad type"))?; + let rhs_node_type = rhs_node + .get_type() + .ok_or_else(|| format_err!("rhs node bad type"))?; + if self .ast_context .resolve_type(lhs_node_type) @@ -259,7 +263,12 @@ impl<'c> Translation<'c> { } let rhs = self.convert_expr(ctx.used(), rhs, None)?; - rhs.and_then_try(|rhs| { + rhs.and_then_try(|mut rhs| { + // C allows enums to index arrays directly without inserting a numeric cast. + if let CTypeKind::Enum(..) = self.ast_context.resolve_type(rhs_node_type).kind { + rhs = self.integer_from_enum(rhs); + } + let simple_index_array = if ctx.needs_address() { // We can't necessarily index into an array if we're using // that element to compute an address. @@ -426,35 +435,35 @@ impl<'c> Translation<'c> { pub fn convert_pointer_to_pointer_cast( &self, - source_cty: CTypeId, - target_cty: CTypeId, + source_cty: CQualTypeId, + target_cty: CQualTypeId, val: WithStmts>, ) -> TranslationResult>> { - if self.ast_context.is_function_pointer(target_cty) - || self.ast_context.is_function_pointer(source_cty) + if self.ast_context.is_function_pointer(target_cty.ctype) + || self.ast_context.is_function_pointer(source_cty.ctype) { let source_ty = self .type_converter .borrow_mut() - .convert(&self.ast_context, source_cty)?; + .convert(&self.ast_context, source_cty.ctype)?; let target_ty = self .type_converter .borrow_mut() - .convert(&self.ast_context, target_cty)?; + .convert(&self.ast_context, target_cty.ctype)?; if source_ty == target_ty { return Ok(val); } - self.import_type(source_cty); - self.import_type(target_cty); + self.import_type(source_cty.ctype); + self.import_type(target_cty.ctype); Ok(val.and_then(|val| { WithStmts::new_val(transmute_expr(source_ty, target_ty, val)).set_unsafe() })) } else { // Normal case - let target_ty = self.convert_type(target_cty)?; + let target_ty = self.convert_type(target_cty.ctype)?; Ok(val.map(|val| mk().cast_expr(val, target_ty))) } } @@ -462,14 +471,14 @@ impl<'c> Translation<'c> { pub fn convert_integral_to_pointer_cast( &self, ctx: ExprContext, - source_cty: CTypeId, - target_cty: CTypeId, + source_cty: CQualTypeId, + target_cty: CQualTypeId, val: WithStmts>, ) -> TranslationResult>> { - let source_ty_kind = &self.ast_context.resolve_type(source_cty).kind; - let target_ty = self.convert_type(target_cty)?; + let source_ty_kind = &self.ast_context.resolve_type(source_cty.ctype).kind; + let target_ty = self.convert_type(target_cty.ctype)?; - if self.ast_context.is_function_pointer(target_cty) { + if self.ast_context.is_function_pointer(target_cty.ctype) { if ctx.is_const { return Err(format_translation_err!( None, @@ -492,8 +501,8 @@ impl<'c> Translation<'c> { val = mk().cast_expr(val, mk().abs_path_ty(vec!["libc", "size_t"])); mk().cast_expr(val, target_ty) })) - } else if let &CTypeKind::Enum(..) = source_ty_kind { - val.try_map(|val| self.convert_cast_from_enum(target_cty, val)) + } else if let &CTypeKind::Enum(enum_id) = source_ty_kind { + val.and_then_try(|val| self.convert_cast_from_enum(ctx, enum_id, target_cty, val)) } else { Ok(val.map(|val| mk().cast_expr(val, target_ty))) } @@ -502,8 +511,8 @@ impl<'c> Translation<'c> { pub fn convert_pointer_to_integral_cast( &self, ctx: ExprContext, - source_cty: CTypeId, - target_cty: CTypeId, + source_cty: CQualTypeId, + target_cty: CQualTypeId, val: WithStmts>, expr: Option, ) -> TranslationResult>> { @@ -514,16 +523,16 @@ impl<'c> Translation<'c> { )); } - let target_ty = self.convert_type(target_cty)?; - let source_ty = self.convert_type(source_cty)?; - let target_ty_kind = &self.ast_context.resolve_type(target_cty).kind; + let target_ty = self.convert_type(target_cty.ctype)?; + let source_ty = self.convert_type(source_cty.ctype)?; + let target_ty_kind = &self.ast_context.resolve_type(target_cty.ctype).kind; - if self.ast_context.is_function_pointer(source_cty) { + if self.ast_context.is_function_pointer(source_cty.ctype) { Ok(val.and_then(|val| { WithStmts::new_val(transmute_expr(source_ty, target_ty, val)).set_unsafe() })) - } else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { - val.try_map(|val| self.convert_cast_to_enum(ctx, target_cty, enum_decl_id, expr, val)) + } else if let &CTypeKind::Enum(enum_id) = target_ty_kind { + val.and_then_try(|val| self.convert_cast_to_enum(ctx, source_cty, enum_id, expr, val)) } else { Ok(val.map(|val| mk().cast_expr(val, target_ty))) } diff --git a/c2rust-transpile/tests/snapshots.rs b/c2rust-transpile/tests/snapshots.rs index 4f10e330ea..3f1e38d7d6 100644 --- a/c2rust-transpile/tests/snapshots.rs +++ b/c2rust-transpile/tests/snapshots.rs @@ -11,8 +11,7 @@ use c2rust_rust_tools::RustEdition; use c2rust_rust_tools::RustEdition::Edition2021; use c2rust_rust_tools::RustEdition::Edition2024; use c2rust_transpile::renamer::RUST_KEYWORDS; -use c2rust_transpile::ReplaceMode; -use c2rust_transpile::TranspilerConfig; +use c2rust_transpile::{EnumMode, ReplaceMode, TranspilerConfig}; use itertools::Itertools; fn config(edition: RustEdition) -> TranspilerConfig { @@ -68,6 +67,7 @@ fn config(edition: RustEdition) -> TranspilerConfig { .unwrap() .to_path_buf(), ), + enum_mode: EnumMode::NewType, } } diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.clang15.snap index 56ee5f78bf..ead4f9b7fb 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.clang15.snap @@ -15,13 +15,17 @@ expression: cat tests/snapshots/exprs.2021.clang15.rs extern "C" { fn puts(str: *const ::core::ffi::c_char) -> ::core::ffi::c_int; } -pub type E = ::core::ffi::c_uint; -pub const EA: E = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct E(pub ::core::ffi::c_uint); +pub const EA: E = E(0); pub type int_t = ::core::ffi::c_int; -pub type C2Rust_Unnamed = ::core::ffi::c_uint; -pub const C: C2Rust_Unnamed = 2; -pub const B: C2Rust_Unnamed = 1; -pub const A: C2Rust_Unnamed = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct C2Rust_Unnamed(pub ::core::ffi::c_uint); +pub const C: C2Rust_Unnamed = C2Rust_Unnamed(2); +pub const B: C2Rust_Unnamed = C2Rust_Unnamed(1); +pub const A: C2Rust_Unnamed = C2Rust_Unnamed(0); unsafe extern "C" fn side_effect() -> ::core::ffi::c_int { puts(b"the return of side effect\0".as_ptr() as *const ::core::ffi::c_char); return 10 as ::core::ffi::c_int; @@ -60,7 +64,7 @@ pub unsafe extern "C" fn unsigned_compound_desugaring() { let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; let mut u: ::core::ffi::c_uint = 0 as ::core::ffi::c_uint; let mut e: E = EA; - e = (e as ::core::ffi::c_uint).wrapping_add(u) as E; + e = E(e.0.wrapping_add(u)); i = (i as ::core::ffi::c_uint).wrapping_add(u) as ::core::ffi::c_int; } #[no_mangle] @@ -72,7 +76,7 @@ pub unsafe extern "C" fn cast_literals() { } #[no_mangle] pub unsafe extern "C" fn compound_literal() { - let mut i: ::core::ffi::c_int = B as ::core::ffi::c_int; + let mut i: ::core::ffi::c_int = B.0 as ::core::ffi::c_int; } #[no_mangle] pub unsafe extern "C" fn statement_expr() { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.clang15.snap index 04e6019c9c..94b05654a1 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.clang15.snap @@ -15,13 +15,17 @@ expression: cat tests/snapshots/exprs.2024.clang15.rs unsafe extern "C" { unsafe fn puts(str: *const ::core::ffi::c_char) -> ::core::ffi::c_int; } -pub type E = ::core::ffi::c_uint; -pub const EA: E = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct E(pub ::core::ffi::c_uint); +pub const EA: E = E(0); pub type int_t = ::core::ffi::c_int; -pub type C2Rust_Unnamed = ::core::ffi::c_uint; -pub const C: C2Rust_Unnamed = 2; -pub const B: C2Rust_Unnamed = 1; -pub const A: C2Rust_Unnamed = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct C2Rust_Unnamed(pub ::core::ffi::c_uint); +pub const C: C2Rust_Unnamed = C2Rust_Unnamed(2); +pub const B: C2Rust_Unnamed = C2Rust_Unnamed(1); +pub const A: C2Rust_Unnamed = C2Rust_Unnamed(0); unsafe extern "C" fn side_effect() -> ::core::ffi::c_int { puts(b"the return of side effect\0".as_ptr() as *const ::core::ffi::c_char); return 10 as ::core::ffi::c_int; @@ -60,7 +64,7 @@ pub unsafe extern "C" fn unsigned_compound_desugaring() { let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; let mut u: ::core::ffi::c_uint = 0 as ::core::ffi::c_uint; let mut e: E = EA; - e = (e as ::core::ffi::c_uint).wrapping_add(u) as E; + e = E(e.0.wrapping_add(u)); i = (i as ::core::ffi::c_uint).wrapping_add(u) as ::core::ffi::c_int; } #[unsafe(no_mangle)] @@ -72,7 +76,7 @@ pub unsafe extern "C" fn cast_literals() { } #[unsafe(no_mangle)] pub unsafe extern "C" fn compound_literal() { - let mut i: ::core::ffi::c_int = B as ::core::ffi::c_int; + let mut i: ::core::ffi::c_int = B.0 as ::core::ffi::c_int; } #[unsafe(no_mangle)] pub unsafe extern "C" fn statement_expr() { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2021.clang15.snap index 07857d1fdf..c41d837292 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2021.clang15.snap @@ -11,14 +11,16 @@ expression: cat tests/snapshots/macrocase.2021.clang15.rs unused_assignments, unused_mut )] -pub type ZSTD_dParameter = ::core::ffi::c_uint; -pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = 1000; -pub const ZSTD_d_windowLogMax: ZSTD_dParameter = 100; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct ZSTD_dParameter(pub ::core::ffi::c_uint); +pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = ZSTD_dParameter(1000); +pub const ZSTD_d_windowLogMax: ZSTD_dParameter = ZSTD_dParameter(100); pub const ZSTD_d_format: ::core::ffi::c_uint = 1000 as ::core::ffi::c_uint; #[no_mangle] pub unsafe extern "C" fn ZSTD_dParam_getBounds(mut dParam: ZSTD_dParameter) -> ::core::ffi::c_int { let mut bounds: ::core::ffi::c_int = 0 as ::core::ffi::c_int; - match dParam as ::core::ffi::c_uint { + match dParam.0 { 100 => { bounds = 1 as ::core::ffi::c_int; return bounds; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2024.clang15.snap index cdf1b52d30..e4cc4f1de0 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macrocase.c.2024.clang15.snap @@ -12,14 +12,16 @@ expression: cat tests/snapshots/macrocase.2024.clang15.rs unused_assignments, unused_mut )] -pub type ZSTD_dParameter = ::core::ffi::c_uint; -pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = 1000; -pub const ZSTD_d_windowLogMax: ZSTD_dParameter = 100; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct ZSTD_dParameter(pub ::core::ffi::c_uint); +pub const ZSTD_d_experimentalParam1: ZSTD_dParameter = ZSTD_dParameter(1000); +pub const ZSTD_d_windowLogMax: ZSTD_dParameter = ZSTD_dParameter(100); pub const ZSTD_d_format: ::core::ffi::c_uint = 1000 as ::core::ffi::c_uint; #[unsafe(no_mangle)] pub unsafe extern "C" fn ZSTD_dParam_getBounds(mut dParam: ZSTD_dParameter) -> ::core::ffi::c_int { let mut bounds: ::core::ffi::c_int = 0 as ::core::ffi::c_int; - match dParam as ::core::ffi::c_uint { + match dParam.0 { 100 => { bounds = 1 as ::core::ffi::c_int; return bounds; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2021.clang15.snap index d24f14826f..8cfbb62b5d 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2021.clang15.snap @@ -14,9 +14,11 @@ expression: cat tests/snapshots/records.2021.clang15.rs #[derive(Copy, Clone)] #[repr(C)] pub struct AnonEnumInStruct {} -pub type C2Rust_Unnamed = ::core::ffi::c_uint; -pub const VALUE2: C2Rust_Unnamed = 1; -pub const VALUE1: C2Rust_Unnamed = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct C2Rust_Unnamed(pub ::core::ffi::c_uint); +pub const VALUE2: C2Rust_Unnamed = C2Rust_Unnamed(1); +pub const VALUE1: C2Rust_Unnamed = C2Rust_Unnamed(0); #[derive(Copy, Clone)] #[repr(C)] pub struct AnonStructInStruct { @@ -40,9 +42,11 @@ pub struct InsideStruct { pub union AnonEnumInUnion { pub a: ::core::ffi::c_int, } -pub type C2Rust_Unnamed_1 = ::core::ffi::c_uint; -pub const VALUE4: C2Rust_Unnamed_1 = 1; -pub const VALUE3: C2Rust_Unnamed_1 = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct C2Rust_Unnamed_1(pub ::core::ffi::c_uint); +pub const VALUE4: C2Rust_Unnamed_1 = C2Rust_Unnamed_1(1); +pub const VALUE3: C2Rust_Unnamed_1 = C2Rust_Unnamed_1(0); #[derive(Copy, Clone)] #[repr(C)] pub union AnonStructInUnion { @@ -66,7 +70,7 @@ pub struct InsideUnion { } #[no_mangle] pub unsafe extern "C" fn struct_declaration() { - let mut value: ::core::ffi::c_int = VALUE2 as ::core::ffi::c_int; + let mut value: ::core::ffi::c_int = VALUE2.0 as ::core::ffi::c_int; let mut a: AnonEnumInStruct = AnonEnumInStruct {}; let mut b: AnonStructInStruct = AnonStructInStruct { c2rust_unnamed: C2Rust_Unnamed_0 { some_number: 0 }, @@ -77,7 +81,7 @@ pub unsafe extern "C" fn struct_declaration() { } #[no_mangle] pub unsafe extern "C" fn union_declaration() { - let mut value: ::core::ffi::c_int = VALUE4 as ::core::ffi::c_int; + let mut value: ::core::ffi::c_int = VALUE4.0 as ::core::ffi::c_int; let mut a: AnonEnumInUnion = AnonEnumInUnion { a: 0 }; let mut b: AnonStructInUnion = AnonStructInUnion { c2rust_unnamed: C2Rust_Unnamed_2 { some_number: 0 }, diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2024.clang15.snap index 4ee9a80c6a..46cf4abce5 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@records.c.2024.clang15.snap @@ -15,9 +15,11 @@ expression: cat tests/snapshots/records.2024.clang15.rs #[derive(Copy, Clone)] #[repr(C)] pub struct AnonEnumInStruct {} -pub type C2Rust_Unnamed = ::core::ffi::c_uint; -pub const VALUE2: C2Rust_Unnamed = 1; -pub const VALUE1: C2Rust_Unnamed = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct C2Rust_Unnamed(pub ::core::ffi::c_uint); +pub const VALUE2: C2Rust_Unnamed = C2Rust_Unnamed(1); +pub const VALUE1: C2Rust_Unnamed = C2Rust_Unnamed(0); #[derive(Copy, Clone)] #[repr(C)] pub struct AnonStructInStruct { @@ -41,9 +43,11 @@ pub struct InsideStruct { pub union AnonEnumInUnion { pub a: ::core::ffi::c_int, } -pub type C2Rust_Unnamed_1 = ::core::ffi::c_uint; -pub const VALUE4: C2Rust_Unnamed_1 = 1; -pub const VALUE3: C2Rust_Unnamed_1 = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct C2Rust_Unnamed_1(pub ::core::ffi::c_uint); +pub const VALUE4: C2Rust_Unnamed_1 = C2Rust_Unnamed_1(1); +pub const VALUE3: C2Rust_Unnamed_1 = C2Rust_Unnamed_1(0); #[derive(Copy, Clone)] #[repr(C)] pub union AnonStructInUnion { @@ -67,7 +71,7 @@ pub struct InsideUnion { } #[unsafe(no_mangle)] pub unsafe extern "C" fn struct_declaration() { - let mut value: ::core::ffi::c_int = VALUE2 as ::core::ffi::c_int; + let mut value: ::core::ffi::c_int = VALUE2.0 as ::core::ffi::c_int; let mut a: AnonEnumInStruct = AnonEnumInStruct {}; let mut b: AnonStructInStruct = AnonStructInStruct { c2rust_unnamed: C2Rust_Unnamed_0 { some_number: 0 }, @@ -78,7 +82,7 @@ pub unsafe extern "C" fn struct_declaration() { } #[unsafe(no_mangle)] pub unsafe extern "C" fn union_declaration() { - let mut value: ::core::ffi::c_int = VALUE4 as ::core::ffi::c_int; + let mut value: ::core::ffi::c_int = VALUE4.0 as ::core::ffi::c_int; let mut a: AnonEnumInUnion = AnonEnumInUnion { a: 0 }; let mut b: AnonStructInUnion = AnonStructInUnion { c2rust_unnamed: C2Rust_Unnamed_2 { some_number: 0 }, diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@scalar_init.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@scalar_init.c.2021.clang15.snap index 983068e241..912ed190b6 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@scalar_init.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@scalar_init.c.2021.clang15.snap @@ -12,8 +12,10 @@ expression: cat tests/snapshots/scalar_init.2021.clang15.rs unused_mut )] #![feature(raw_ref_op)] -pub type E = ::core::ffi::c_uint; -pub const A: E = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct E(pub ::core::ffi::c_uint); +pub const A: E = E(0); pub const true_0: ::core::ffi::c_int = 1 as ::core::ffi::c_int; #[no_mangle] pub unsafe extern "C" fn scalar_init() { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@scalar_init.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@scalar_init.c.2024.clang15.snap index be7f3a5d96..d6a4077d3a 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@scalar_init.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@scalar_init.c.2024.clang15.snap @@ -12,8 +12,10 @@ expression: cat tests/snapshots/scalar_init.2024.clang15.rs unused_assignments, unused_mut )] -pub type E = ::core::ffi::c_uint; -pub const A: E = 0; +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct E(pub ::core::ffi::c_uint); +pub const A: E = E(0); pub const true_0: ::core::ffi::c_int = 1 as ::core::ffi::c_int; #[unsafe(no_mangle)] pub unsafe extern "C" fn scalar_init() { diff --git a/c2rust/src/bin/c2rust-transpile.rs b/c2rust/src/bin/c2rust-transpile.rs index 39f5ada8e7..34a272ac67 100644 --- a/c2rust/src/bin/c2rust-transpile.rs +++ b/c2rust/src/bin/c2rust-transpile.rs @@ -4,7 +4,7 @@ use regex::Regex; use std::{ffi::OsStr, fs, path::PathBuf}; use c2rust_rust_tools::RustEdition; -use c2rust_transpile::{Diagnostic, ReplaceMode, TranspilerConfig}; +use c2rust_transpile::{Diagnostic, EnumMode, ReplaceMode, TranspilerConfig}; #[derive(Debug, Parser)] #[clap( @@ -326,6 +326,7 @@ fn main() { log_level: args.log_level, edition: args.edition, deny_unsafe_op_in_unsafe_fn: args.deny_unsafe_op_in_unsafe_fn, + enum_mode: EnumMode::NewType, }; // binaries imply emit-build-files if !tcfg.binaries.is_empty() { diff --git a/tests/unit/enums/src/test_enums.rs b/tests/unit/enums/src/test_enums.rs index 1e373fd354..c100fd91cc 100644 --- a/tests/unit/enums/src/test_enums.rs +++ b/tests/unit/enums/src/test_enums.rs @@ -35,8 +35,8 @@ const BUFFER_SIZE6: usize = 1; #[test] pub fn test_variants() { - assert_eq!(A as u32, 0); - assert_eq!(B as u32, 1); + assert_eq!(A.0 as u32, 0); + assert_eq!(B.0 as u32, 1); } #[test] diff --git a/tests/unit/misc/src/test_uninitialized.rs b/tests/unit/misc/src/test_uninitialized.rs index 87425c5b32..505aee8937 100644 --- a/tests/unit/misc/src/test_uninitialized.rs +++ b/tests/unit/misc/src/test_uninitialized.rs @@ -26,9 +26,9 @@ pub fn test_buffer() { #[test] pub fn test_types() { - assert_eq!(foo as u32, 1); - assert_eq!(bar as u32, 2); - assert_eq!(baz as u32, 3); + assert_eq!(foo.0 as u32, 1); + assert_eq!(bar.0 as u32, 2); + assert_eq!(baz.0 as u32, 3); // FIXME: union fields are private // let my_union = u { x: 32 };