diff --git a/c2rust-transpile/src/translator/enums.rs b/c2rust-transpile/src/translator/enums.rs index 88a9045b13..d8d9ad92ea 100644 --- a/c2rust-transpile/src/translator/enums.rs +++ b/c2rust-transpile/src/translator/enums.rs @@ -63,6 +63,19 @@ impl<'c> Translation<'c> { WithStmts::new_val(self.enum_for_i64(type_id, 0)) } + /// Translates a `DeclRef` for an `EnumConstant`. + pub fn convert_enum_constant_decl_ref( + &self, + expr_type_id: CQualTypeId, + enum_constant_id: CEnumConstantId, + ) -> TranslationResult>> { + let val = self.enum_constant_expr(enum_constant_id); + + // Add a cast to the expected integral type. + self.convert_cast_from_enum(expr_type_id.ctype, val) + .map(WithStmts::new_val) + } + /// Translate a cast where the source type, but not the target type, is an `enum` type. pub fn convert_cast_from_enum( &self, diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index da92ccfea8..1eb183b347 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -3125,134 +3125,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 - .try_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![]); - } - - let mut val = WithStmts::new_val(val).merge_unsafe(set_unsafe); - - if lrvalue.is_rvalue() { - val = self.make_cast(ctx, qual_ty, override_ty.unwrap_or(qual_ty), val)?; - } - - Ok(val) - } + DeclRef(result_type_id, decl_id, lrvalue) => self + .convert_decl_ref(ctx, override_ty, result_type_id, decl_id, lrvalue) + .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( @@ -3506,6 +3381,145 @@ impl<'c> Translation<'c> { } } + fn convert_decl_ref( + &self, + ctx: ExprContext, + expected_type_id: Option, + result_type_id: CQualTypeId, + decl_id: CDeclId, + lrvalue: LRValue, + ) -> 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(result_type_id, decl_id); + } + + 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(result_type_id.ctype)?; + let actual_ty = self + .type_converter + .borrow_mut() + .knr_function_type_with_parameters( + &self.ast_context, + result_type_id.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(result_type_id.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 + .try_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(result_type_id.ctype).kind + { + val = mk().method_call_expr(val, "as_mut_ptr", vec![]); + } + + let mut val = WithStmts::new_val(val).merge_unsafe(set_unsafe); + + if lrvalue.is_rvalue() { + val = self.make_cast( + ctx, + result_type_id, + expected_type_id.unwrap_or(result_type_id), + val, + )?; + } + + Ok(val) + } + 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)),