diff --git a/c2rust-transpile/src/convert_type.rs b/c2rust-transpile/src/convert_type.rs index ac189bb4a3..d9b4edb1dc 100644 --- a/c2rust-transpile/src/convert_type.rs +++ b/c2rust-transpile/src/convert_type.rs @@ -2,8 +2,11 @@ use crate::c_ast::CDeclId; use crate::c_ast::*; use crate::diagnostics::TranslationResult; use crate::renamer::*; +use crate::translator::variadic::mk_va_list_ty; +use crate::TranspilerConfig; use crate::{CrateSet, ExternCrate}; use c2rust_ast_builder::{mk, properties::*}; +use c2rust_rust_tools::RustEdition; use failure::format_err; use indexmap::IndexSet; use std::collections::{HashMap, HashSet}; @@ -17,6 +20,7 @@ enum FieldKey { } pub struct TypeConverter { + pub edition: RustEdition, pub translate_valist: bool, renamer: Renamer, fields: HashMap>, @@ -132,16 +136,10 @@ pub const RESERVED_NAMES: [&str; 100] = [ ]; impl TypeConverter { - // We don't provide a `Default` impl to simplify future compatibility: - // if `TypeConverter` ever gets fields incompatible with `Default`, then - // cleaning out the uses of `impl Default for TypeConverter` can be a pain. - // More practically, there is a single use of `TypeConverter::new` and no - // current plans to use a `Default` impl, so providing it isn't worth the - // potential breakage. - #[allow(clippy::new_without_default)] - pub fn new() -> TypeConverter { + pub fn new(tcfg: &TranspilerConfig) -> TypeConverter { TypeConverter { - translate_valist: false, + edition: tcfg.edition, + translate_valist: tcfg.translate_valist, renamer: Renamer::new(&RESERVED_NAMES), fields: HashMap::new(), suffix_names: HashMap::new(), @@ -318,8 +316,7 @@ impl TypeConverter { ctype: CTypeId, ) -> TranslationResult> { if self.translate_valist && ctxt.is_va_list(ctype) { - let ty = mk().abs_path_ty(vec!["core", "ffi", "VaListImpl"]); - return Ok(ty); + return Ok(mk_va_list_ty(self.edition, None)); } match ctxt.index(ctype).kind { diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index 4687e68714..5c518e94f6 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -360,7 +360,7 @@ impl<'c> Translation<'c> { "__builtin_va_end" => { if ctx.is_unused() && args.len() == 1 { if let Some(_va_id) = self.match_vaend(args[0]) { - // nothing to do since `VaListImpl`s get `Drop`'ed. + // nothing to do since the translated Rust `va_list` values get `Drop`'ed. return Ok(WithStmts::new_val(self.panic("va_end stub"))); } } diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 638521f8ae..97f6bb3922 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -32,6 +32,7 @@ use crate::rust_ast::item_store::ItemStore; use crate::rust_ast::set_span::SetSpan; use crate::rust_ast::{pos_to_span, SpanExt}; use crate::translator::named_references::NamedReference; +use crate::translator::variadic::{mk_va_list_copy, mk_va_list_ty}; use c2rust_ast_builder::{mk, properties::*, Builder}; use c2rust_ast_printer::pprust; @@ -57,7 +58,7 @@ mod operators; mod pointers; mod simd; mod structs_unions; -mod variadic; +pub(crate) mod variadic; pub use crate::diagnostics::{TranslationError, TranslationErrorKind}; use crate::CrateSet; @@ -1532,11 +1533,7 @@ impl<'c> Translation<'c> { main_file: &Path, ) -> Self { let comment_context = CommentContext::new(&mut ast_context); - let mut type_converter = TypeConverter::new(); - - if tcfg.translate_valist { - type_converter.translate_valist = true - } + let type_converter = TypeConverter::new(tcfg); let main_file = ast_context .find_file_id(main_file) @@ -2897,9 +2894,9 @@ impl<'c> Translation<'c> { .unwrap_or_else(|| panic!("Failed to insert variable '{}'", ident)); if self.ast_context.is_va_list(typ.ctype) { - // translate `va_list` variables to `VaListImpl`s and omit the initializer. + // Translate `va_list` variables to the current Rust `va_list` type and omit the initializer. let pat_mut = mk().mutbl().ident_pat(rust_name); - let ty = mk().abs_path_ty(vec!["core", "ffi", "VaListImpl"]); + let ty = mk_va_list_ty(self.tcfg.edition, None); let local_mut = mk().local(pat_mut, Some(ty), None); return Ok(cfg::DeclStmtInfo::new( @@ -4139,7 +4136,7 @@ impl<'c> Translation<'c> { { // No `override_ty` to avoid unwanted casting. val = self.convert_expr(ctx, expr_id, None)?; - val = val.map(|val| mk().method_call_expr(val, "as_va_list", Vec::new())); + val = val.map(|val| mk_va_list_copy(self.tcfg.edition, val)); } else { val = self.convert_expr(ctx, expr_id, override_ty)?; } diff --git a/c2rust-transpile/src/translator/structs_unions.rs b/c2rust-transpile/src/translator/structs_unions.rs index d1437caa5e..b754159e9b 100644 --- a/c2rust-transpile/src/translator/structs_unions.rs +++ b/c2rust-transpile/src/translator/structs_unions.rs @@ -12,6 +12,7 @@ use crate::c_ast::{ MemberKind, }; use crate::diagnostics::TranslationResult; +use crate::translator::variadic::mk_va_list_ty; use crate::translator::{ConvertedDecl, ExprContext, Translation, PADDING_SUFFIX}; use crate::with_stmts::WithStmts; use crate::ExternCrate; @@ -858,15 +859,7 @@ impl<'a> Translation<'a> { // TODO: handle or panic on structs with more than one va_list? let is_va_list = self.ast_context.is_va_list(ctype); let mut ty = if is_va_list { - let path = vec![ - mk().path_segment("core"), - mk().path_segment("ffi"), - mk().path_segment_with_args( - "VaListImpl", - mk().angle_bracketed_args(vec![mk().lifetime("a")]), - ), - ]; - mk().abs_path_ty(path) + mk_va_list_ty(self.tcfg.edition, Some("a")) } else { self.convert_type(ctype)? }; diff --git a/c2rust-transpile/src/translator/variadic.rs b/c2rust-transpile/src/translator/variadic.rs index 7980ece340..1ded621021 100644 --- a/c2rust-transpile/src/translator/variadic.rs +++ b/c2rust-transpile/src/translator/variadic.rs @@ -22,6 +22,38 @@ macro_rules! match_or { }; } +pub fn mk_va_list_ty(edition: RustEdition, lifetime: Option<&str>) -> Box { + // This was updated in , + // first changed in `nightly-2025-12-07`. + // `VaListImpl` was removed. + let name = if edition < Edition2024 { + "VaListImpl" + } else { + "VaList" + }; + let lifetime_args = match lifetime { + None => vec![], + Some(lifetime) => vec![mk().lifetime(lifetime)], + }; + mk().abs_path_ty(vec![ + mk().path_segment("core"), + mk().path_segment("ffi"), + mk().path_segment_with_args(name, mk().angle_bracketed_args(lifetime_args)), + ]) +} + +pub fn mk_va_list_copy(edition: RustEdition, va_list: Box) -> Box { + // This was updated in , + // first changed in `nightly-2025-12-07`. + // `VaListImpl` was removed and `.as_va_list()` was replaced with `.clone()`. + let name = if edition < Edition2024 { + "as_va_list" + } else { + "clone" + }; + mk().method_call_expr(va_list, name, vec![]) +} + impl<'c> Translation<'c> { /// Returns true iff `va_start`, `va_end`, or `va_copy` may be called on `decl_id`. pub fn is_va_decl(&self, decl_id: CDeclId) -> bool { @@ -229,10 +261,12 @@ impl<'c> Translation<'c> { } } - /// Update the current function context by i) enabling the C variadics feature, ii) naming the - /// Rust function argument that corresponds to the ellipsis in the original C function, and iii) - /// building a list of variable declarations to be translated into `VaListImpl`s. Returns the - /// name of the `VaList` function argument for convenience. + /// Update the current function context by + /// * enabling the C variadics feature + /// * naming the Rust function argument that corresponds to the ellipsis in the original C function + /// * building a list of variable declarations to be translated into the current Rust `va_list` type. + /// + /// Returns the name of the `VaList` function argument for convenience. pub fn register_va_decls(&self, body: CStmtId) -> String { self.use_feature("c_variadic"); diff --git a/c2rust-transpile/tests/snapshots.rs b/c2rust-transpile/tests/snapshots.rs index adb982f526..2ec7064dd5 100644 --- a/c2rust-transpile/tests/snapshots.rs +++ b/c2rust-transpile/tests/snapshots.rs @@ -468,7 +468,6 @@ fn test_wide_strings() { #[test] fn test_varargs() { transpile("varargs.c") - .expect_compile_error_edition_2024(cfg!(target_os = "linux")) .arch_specific(true) .os_specific(true) .run(); diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@varargs.c.2024.x86_64.linux.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@varargs.c.2024.x86_64.linux.snap index 0e4b8d26fb..4d09bf8375 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@varargs.c.2024.x86_64.linux.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@varargs.c.2024.x86_64.linux.snap @@ -33,7 +33,7 @@ pub type size_t = usize; #[derive()] #[repr(C)] pub struct vastruct<'a> { - pub args: ::core::ffi::VaListImpl<'a>, + pub args: ::core::ffi::VaList<'a>, } #[unsafe(no_mangle)] pub unsafe extern "C" fn call_printf() { @@ -48,20 +48,20 @@ pub unsafe extern "C" fn my_vprintf( mut format: *const ::core::ffi::c_char, mut ap: ::core::ffi::VaList, ) { - vprintf(format, ap.as_va_list()); + vprintf(format, ap.clone()); } #[unsafe(no_mangle)] pub unsafe extern "C" fn call_vprintf( mut format: *const ::core::ffi::c_char, mut c2rust_args: ... ) { - let mut ap: ::core::ffi::VaListImpl; + let mut ap: ::core::ffi::VaList; ap = c2rust_args.clone(); - my_vprintf(format, ap.as_va_list()); + my_vprintf(format, ap.clone()); } #[unsafe(no_mangle)] pub unsafe extern "C" fn my_printf(mut fmt: *const ::core::ffi::c_char, mut c2rust_args: ...) { - let mut ap: ::core::ffi::VaListImpl; + let mut ap: ::core::ffi::VaList; ap = c2rust_args.clone(); while *fmt != 0 { match *fmt as ::core::ffi::c_int { @@ -100,12 +100,12 @@ pub unsafe extern "C" fn my_printf(mut fmt: *const ::core::ffi::c_char, mut c2ru } #[unsafe(no_mangle)] pub unsafe extern "C" fn simple_vacopy(mut fmt: *const ::core::ffi::c_char, mut c2rust_args: ...) { - let mut ap: ::core::ffi::VaListImpl; - let mut aq: ::core::ffi::VaListImpl; + let mut ap: ::core::ffi::VaList; + let mut aq: ::core::ffi::VaList; ap = c2rust_args.clone(); aq = ap.clone(); - vprintf(fmt, ap.as_va_list()); - vprintf(fmt, aq.as_va_list()); + vprintf(fmt, ap.clone()); + vprintf(fmt, aq.clone()); } #[unsafe(no_mangle)] pub unsafe extern "C" fn valist_struct_member( @@ -120,8 +120,8 @@ pub unsafe extern "C" fn valist_struct_member( }; a.args = c2rust_args.clone(); b.args = a.args.clone(); - vprintf(fmt, a.args.as_va_list()); - vprintf(fmt, b.args.as_va_list()); + vprintf(fmt, a.args.clone()); + vprintf(fmt, b.args.clone()); } #[unsafe(no_mangle)] pub unsafe extern "C" fn valist_struct_pointer_member( @@ -138,19 +138,19 @@ pub unsafe extern "C" fn valist_struct_pointer_member( let mut q: *mut vastruct = &raw mut b; (*p).args = c2rust_args.clone(); (*q).args = (*p).args.clone(); - vprintf(fmt, (*p).args.as_va_list()); - vprintf(fmt, (*q).args.as_va_list()); + vprintf(fmt, (*p).args.clone()); + vprintf(fmt, (*q).args.clone()); } #[unsafe(no_mangle)] pub unsafe extern "C" fn restart_valist(mut fmt: *const ::core::ffi::c_char, mut c2rust_args: ...) { - let mut ap: ::core::ffi::VaListImpl; + let mut ap: ::core::ffi::VaList; ap = c2rust_args.clone(); - vprintf(fmt, ap.as_va_list()); + vprintf(fmt, ap.clone()); ap = c2rust_args.clone(); - vprintf(fmt, ap.as_va_list()); + vprintf(fmt, ap.clone()); } #[unsafe(no_mangle)] -pub unsafe extern "C" fn print_int(mut ap: *mut ::core::ffi::VaListImpl) { +pub unsafe extern "C" fn print_int(mut ap: *mut ::core::ffi::VaList) { printf( b"%d\0".as_ptr() as *const ::core::ffi::c_char, (*ap).arg::<::core::ffi::c_int>(), @@ -158,7 +158,7 @@ pub unsafe extern "C" fn print_int(mut ap: *mut ::core::ffi::VaListImpl) { } #[unsafe(no_mangle)] pub unsafe extern "C" fn borrowed_valist(mut count: size_t, mut c2rust_args: ...) { - let mut ap: ::core::ffi::VaListImpl; + let mut ap: ::core::ffi::VaList; ap = c2rust_args.clone(); while count > 0 as size_t { print_int(&raw mut ap); diff --git a/tests/unit/items/Cargo.toml b/tests/unit/items/Cargo.toml index 4da15111bf..201cf285ed 100644 --- a/tests/unit/items/Cargo.toml +++ b/tests/unit/items/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "items-tests" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/unit/items/src/test_fn_attrs.rs b/tests/unit/items/src/test_fn_attrs.rs index d137c697cb..8cf5fd8d5d 100644 --- a/tests/unit/items/src/test_fn_attrs.rs +++ b/tests/unit/items/src/test_fn_attrs.rs @@ -164,7 +164,7 @@ unsafe extern "C" fn rust_gnu_inline_non_canonical_definition_extern let aliased_fn_syntax = |public| { format!( r#" -extern "C" {{ +unsafe extern "C" {{ #[link_name = "inline_extern"] {}fn aliased_fn(); "#, diff --git a/tests/unit/items/src/test_functions.rs b/tests/unit/items/src/test_functions.rs index 91c53aa08f..48f9c87264 100644 --- a/tests/unit/items/src/test_functions.rs +++ b/tests/unit/items/src/test_functions.rs @@ -1,7 +1,7 @@ use crate::functions::rust_coreutils_static_assert; #[link(name = "test")] -extern "C" { +unsafe extern "C" { fn coreutils_static_assert(); } diff --git a/tests/unit/items/src/test_linking.rs b/tests/unit/items/src/test_linking.rs index c5795d93cb..dfb7f60aba 100644 --- a/tests/unit/items/src/test_linking.rs +++ b/tests/unit/items/src/test_linking.rs @@ -2,7 +2,7 @@ use crate::linking::{rust_l, rust_w}; use std::ffi::c_int; #[link(name = "test")] -extern "C" { +unsafe extern "C" { fn l() -> c_int; fn w() -> c_int; diff --git a/tests/unit/items/src/test_noop.rs b/tests/unit/items/src/test_noop.rs index 21ec513641..4a43d1b15b 100644 --- a/tests/unit/items/src/test_noop.rs +++ b/tests/unit/items/src/test_noop.rs @@ -4,7 +4,7 @@ use crate::noop::rust_noop; use std::ffi::c_int; #[link(name = "test")] -extern "C" { +unsafe extern "C" { fn noop(); fn nofnargs() -> c_int; diff --git a/tests/unit/items/src/test_varargs.rs b/tests/unit/items/src/test_varargs.rs index ddcc8fdbb8..929c8192b1 100644 --- a/tests/unit/items/src/test_varargs.rs +++ b/tests/unit/items/src/test_varargs.rs @@ -13,14 +13,14 @@ use std::ffi::c_char; use std::ffi::CString; #[link(name = "test")] -extern "C" { +unsafe extern "C" { fn call_printf(); } // See #1281. Varargs don't yet work on aarch64. #[cfg(not(target_arch = "aarch64"))] #[link(name = "test")] -extern "C" { +unsafe extern "C" { fn call_vprintf(_: *const c_char, ...); fn my_printf(_: *const c_char, ...);