Skip to content

Commit 9dd6f33

Browse files
authored
transpile: fix variadics in edition 2024 (#1657)
`VaListImpl` was removed by rust-lang/rust#141980 in `nightly-2025-12-07`, so for the `nightly-2026-03-03` we're using for `--edition 2024`, we need to switch from `VaListImpl` to `VaList` and from `.as_va_list()` to `.clone()`. However, this isn't a change strictly tied to the edition change, but just `rustc` changing over time, so we may want to introduce a `--nightly-toolchain` or similar argument in addition to `--edition` to control this, in case `nightly-2025-12-06` with `--edition 2024` is desired, for example. This is also true of a bunch of other changes, like * `#![feature(stdsimd)]` split/stabilized * `#![feature(raw_ref_op)]` stabilized * `#![feature(label_break_value)]` stabilized * `#![feature(asm)]` stabilized * `core::intrinsics::pref_align_of` removed * atomic `core::intrinsics` changed * `[profile.release] strip = "debuginfo"` made the default
2 parents 9e4c628 + 893f29e commit 9dd6f33

13 files changed

Lines changed: 80 additions & 60 deletions

File tree

c2rust-transpile/src/convert_type.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ use crate::c_ast::CDeclId;
22
use crate::c_ast::*;
33
use crate::diagnostics::TranslationResult;
44
use crate::renamer::*;
5+
use crate::translator::variadic::mk_va_list_ty;
6+
use crate::TranspilerConfig;
57
use crate::{CrateSet, ExternCrate};
68
use c2rust_ast_builder::{mk, properties::*};
9+
use c2rust_rust_tools::RustEdition;
710
use failure::format_err;
811
use indexmap::IndexSet;
912
use std::collections::{HashMap, HashSet};
@@ -17,6 +20,7 @@ enum FieldKey {
1720
}
1821

1922
pub struct TypeConverter {
23+
pub edition: RustEdition,
2024
pub translate_valist: bool,
2125
renamer: Renamer<CDeclId>,
2226
fields: HashMap<CDeclId, Renamer<FieldKey>>,
@@ -132,16 +136,10 @@ pub const RESERVED_NAMES: [&str; 100] = [
132136
];
133137

134138
impl TypeConverter {
135-
// We don't provide a `Default` impl to simplify future compatibility:
136-
// if `TypeConverter` ever gets fields incompatible with `Default`, then
137-
// cleaning out the uses of `impl Default for TypeConverter` can be a pain.
138-
// More practically, there is a single use of `TypeConverter::new` and no
139-
// current plans to use a `Default` impl, so providing it isn't worth the
140-
// potential breakage.
141-
#[allow(clippy::new_without_default)]
142-
pub fn new() -> TypeConverter {
139+
pub fn new(tcfg: &TranspilerConfig) -> TypeConverter {
143140
TypeConverter {
144-
translate_valist: false,
141+
edition: tcfg.edition,
142+
translate_valist: tcfg.translate_valist,
145143
renamer: Renamer::new(&RESERVED_NAMES),
146144
fields: HashMap::new(),
147145
suffix_names: HashMap::new(),
@@ -318,8 +316,7 @@ impl TypeConverter {
318316
ctype: CTypeId,
319317
) -> TranslationResult<Box<Type>> {
320318
if self.translate_valist && ctxt.is_va_list(ctype) {
321-
let ty = mk().abs_path_ty(vec!["core", "ffi", "VaListImpl"]);
322-
return Ok(ty);
319+
return Ok(mk_va_list_ty(self.edition, None));
323320
}
324321

325322
match ctxt.index(ctype).kind {

c2rust-transpile/src/translator/builtins.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ impl<'c> Translation<'c> {
360360
"__builtin_va_end" => {
361361
if ctx.is_unused() && args.len() == 1 {
362362
if let Some(_va_id) = self.match_vaend(args[0]) {
363-
// nothing to do since `VaListImpl`s get `Drop`'ed.
363+
// nothing to do since the translated Rust `va_list` values get `Drop`'ed.
364364
return Ok(WithStmts::new_val(self.panic("va_end stub")));
365365
}
366366
}

c2rust-transpile/src/translator/mod.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use crate::rust_ast::item_store::ItemStore;
3232
use crate::rust_ast::set_span::SetSpan;
3333
use crate::rust_ast::{pos_to_span, SpanExt};
3434
use crate::translator::named_references::NamedReference;
35+
use crate::translator::variadic::{mk_va_list_copy, mk_va_list_ty};
3536
use c2rust_ast_builder::{mk, properties::*, Builder};
3637
use c2rust_ast_printer::pprust;
3738

@@ -57,7 +58,7 @@ mod operators;
5758
mod pointers;
5859
mod simd;
5960
mod structs_unions;
60-
mod variadic;
61+
pub(crate) mod variadic;
6162

6263
pub use crate::diagnostics::{TranslationError, TranslationErrorKind};
6364
use crate::CrateSet;
@@ -1532,11 +1533,7 @@ impl<'c> Translation<'c> {
15321533
main_file: &Path,
15331534
) -> Self {
15341535
let comment_context = CommentContext::new(&mut ast_context);
1535-
let mut type_converter = TypeConverter::new();
1536-
1537-
if tcfg.translate_valist {
1538-
type_converter.translate_valist = true
1539-
}
1536+
let type_converter = TypeConverter::new(tcfg);
15401537

15411538
let main_file = ast_context
15421539
.find_file_id(main_file)
@@ -2897,9 +2894,9 @@ impl<'c> Translation<'c> {
28972894
.unwrap_or_else(|| panic!("Failed to insert variable '{}'", ident));
28982895

28992896
if self.ast_context.is_va_list(typ.ctype) {
2900-
// translate `va_list` variables to `VaListImpl`s and omit the initializer.
2897+
// Translate `va_list` variables to the current Rust `va_list` type and omit the initializer.
29012898
let pat_mut = mk().mutbl().ident_pat(rust_name);
2902-
let ty = mk().abs_path_ty(vec!["core", "ffi", "VaListImpl"]);
2899+
let ty = mk_va_list_ty(self.tcfg.edition, None);
29032900
let local_mut = mk().local(pat_mut, Some(ty), None);
29042901

29052902
return Ok(cfg::DeclStmtInfo::new(
@@ -4139,7 +4136,7 @@ impl<'c> Translation<'c> {
41394136
{
41404137
// No `override_ty` to avoid unwanted casting.
41414138
val = self.convert_expr(ctx, expr_id, None)?;
4142-
val = val.map(|val| mk().method_call_expr(val, "as_va_list", Vec::new()));
4139+
val = val.map(|val| mk_va_list_copy(self.tcfg.edition, val));
41434140
} else {
41444141
val = self.convert_expr(ctx, expr_id, override_ty)?;
41454142
}

c2rust-transpile/src/translator/structs_unions.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::c_ast::{
1212
MemberKind,
1313
};
1414
use crate::diagnostics::TranslationResult;
15+
use crate::translator::variadic::mk_va_list_ty;
1516
use crate::translator::{ConvertedDecl, ExprContext, Translation, PADDING_SUFFIX};
1617
use crate::with_stmts::WithStmts;
1718
use crate::ExternCrate;
@@ -858,15 +859,7 @@ impl<'a> Translation<'a> {
858859
// TODO: handle or panic on structs with more than one va_list?
859860
let is_va_list = self.ast_context.is_va_list(ctype);
860861
let mut ty = if is_va_list {
861-
let path = vec![
862-
mk().path_segment("core"),
863-
mk().path_segment("ffi"),
864-
mk().path_segment_with_args(
865-
"VaListImpl",
866-
mk().angle_bracketed_args(vec![mk().lifetime("a")]),
867-
),
868-
];
869-
mk().abs_path_ty(path)
862+
mk_va_list_ty(self.tcfg.edition, Some("a"))
870863
} else {
871864
self.convert_type(ctype)?
872865
};

c2rust-transpile/src/translator/variadic.rs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,38 @@ macro_rules! match_or {
2222
};
2323
}
2424

25+
pub fn mk_va_list_ty(edition: RustEdition, lifetime: Option<&str>) -> Box<Type> {
26+
// This was updated in <https://github.com/rust-lang/rust/pull/141980>,
27+
// first changed in `nightly-2025-12-07`.
28+
// `VaListImpl` was removed.
29+
let name = if edition < Edition2024 {
30+
"VaListImpl"
31+
} else {
32+
"VaList"
33+
};
34+
let lifetime_args = match lifetime {
35+
None => vec![],
36+
Some(lifetime) => vec![mk().lifetime(lifetime)],
37+
};
38+
mk().abs_path_ty(vec![
39+
mk().path_segment("core"),
40+
mk().path_segment("ffi"),
41+
mk().path_segment_with_args(name, mk().angle_bracketed_args(lifetime_args)),
42+
])
43+
}
44+
45+
pub fn mk_va_list_copy(edition: RustEdition, va_list: Box<Expr>) -> Box<Expr> {
46+
// This was updated in <https://github.com/rust-lang/rust/pull/141980>,
47+
// first changed in `nightly-2025-12-07`.
48+
// `VaListImpl` was removed and `.as_va_list()` was replaced with `.clone()`.
49+
let name = if edition < Edition2024 {
50+
"as_va_list"
51+
} else {
52+
"clone"
53+
};
54+
mk().method_call_expr(va_list, name, vec![])
55+
}
56+
2557
impl<'c> Translation<'c> {
2658
/// Returns true iff `va_start`, `va_end`, or `va_copy` may be called on `decl_id`.
2759
pub fn is_va_decl(&self, decl_id: CDeclId) -> bool {
@@ -229,10 +261,12 @@ impl<'c> Translation<'c> {
229261
}
230262
}
231263

232-
/// Update the current function context by i) enabling the C variadics feature, ii) naming the
233-
/// Rust function argument that corresponds to the ellipsis in the original C function, and iii)
234-
/// building a list of variable declarations to be translated into `VaListImpl`s. Returns the
235-
/// name of the `VaList` function argument for convenience.
264+
/// Update the current function context by
265+
/// * enabling the C variadics feature
266+
/// * naming the Rust function argument that corresponds to the ellipsis in the original C function
267+
/// * building a list of variable declarations to be translated into the current Rust `va_list` type.
268+
///
269+
/// Returns the name of the `VaList` function argument for convenience.
236270
pub fn register_va_decls(&self, body: CStmtId) -> String {
237271
self.use_feature("c_variadic");
238272

c2rust-transpile/tests/snapshots.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,6 @@ fn test_wide_strings() {
468468
#[test]
469469
fn test_varargs() {
470470
transpile("varargs.c")
471-
.expect_compile_error_edition_2024(cfg!(target_os = "linux"))
472471
.arch_specific(true)
473472
.os_specific(true)
474473
.run();

c2rust-transpile/tests/snapshots/snapshots__transpile@varargs.c.2024.x86_64.linux.snap

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub type size_t = usize;
3333
#[derive()]
3434
#[repr(C)]
3535
pub struct vastruct<'a> {
36-
pub args: ::core::ffi::VaListImpl<'a>,
36+
pub args: ::core::ffi::VaList<'a>,
3737
}
3838
#[unsafe(no_mangle)]
3939
pub unsafe extern "C" fn call_printf() {
@@ -48,20 +48,20 @@ pub unsafe extern "C" fn my_vprintf(
4848
mut format: *const ::core::ffi::c_char,
4949
mut ap: ::core::ffi::VaList,
5050
) {
51-
vprintf(format, ap.as_va_list());
51+
vprintf(format, ap.clone());
5252
}
5353
#[unsafe(no_mangle)]
5454
pub unsafe extern "C" fn call_vprintf(
5555
mut format: *const ::core::ffi::c_char,
5656
mut c2rust_args: ...
5757
) {
58-
let mut ap: ::core::ffi::VaListImpl;
58+
let mut ap: ::core::ffi::VaList;
5959
ap = c2rust_args.clone();
60-
my_vprintf(format, ap.as_va_list());
60+
my_vprintf(format, ap.clone());
6161
}
6262
#[unsafe(no_mangle)]
6363
pub unsafe extern "C" fn my_printf(mut fmt: *const ::core::ffi::c_char, mut c2rust_args: ...) {
64-
let mut ap: ::core::ffi::VaListImpl;
64+
let mut ap: ::core::ffi::VaList;
6565
ap = c2rust_args.clone();
6666
while *fmt != 0 {
6767
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
100100
}
101101
#[unsafe(no_mangle)]
102102
pub unsafe extern "C" fn simple_vacopy(mut fmt: *const ::core::ffi::c_char, mut c2rust_args: ...) {
103-
let mut ap: ::core::ffi::VaListImpl;
104-
let mut aq: ::core::ffi::VaListImpl;
103+
let mut ap: ::core::ffi::VaList;
104+
let mut aq: ::core::ffi::VaList;
105105
ap = c2rust_args.clone();
106106
aq = ap.clone();
107-
vprintf(fmt, ap.as_va_list());
108-
vprintf(fmt, aq.as_va_list());
107+
vprintf(fmt, ap.clone());
108+
vprintf(fmt, aq.clone());
109109
}
110110
#[unsafe(no_mangle)]
111111
pub unsafe extern "C" fn valist_struct_member(
@@ -120,8 +120,8 @@ pub unsafe extern "C" fn valist_struct_member(
120120
};
121121
a.args = c2rust_args.clone();
122122
b.args = a.args.clone();
123-
vprintf(fmt, a.args.as_va_list());
124-
vprintf(fmt, b.args.as_va_list());
123+
vprintf(fmt, a.args.clone());
124+
vprintf(fmt, b.args.clone());
125125
}
126126
#[unsafe(no_mangle)]
127127
pub unsafe extern "C" fn valist_struct_pointer_member(
@@ -138,27 +138,27 @@ pub unsafe extern "C" fn valist_struct_pointer_member(
138138
let mut q: *mut vastruct = &raw mut b;
139139
(*p).args = c2rust_args.clone();
140140
(*q).args = (*p).args.clone();
141-
vprintf(fmt, (*p).args.as_va_list());
142-
vprintf(fmt, (*q).args.as_va_list());
141+
vprintf(fmt, (*p).args.clone());
142+
vprintf(fmt, (*q).args.clone());
143143
}
144144
#[unsafe(no_mangle)]
145145
pub unsafe extern "C" fn restart_valist(mut fmt: *const ::core::ffi::c_char, mut c2rust_args: ...) {
146-
let mut ap: ::core::ffi::VaListImpl;
146+
let mut ap: ::core::ffi::VaList;
147147
ap = c2rust_args.clone();
148-
vprintf(fmt, ap.as_va_list());
148+
vprintf(fmt, ap.clone());
149149
ap = c2rust_args.clone();
150-
vprintf(fmt, ap.as_va_list());
150+
vprintf(fmt, ap.clone());
151151
}
152152
#[unsafe(no_mangle)]
153-
pub unsafe extern "C" fn print_int(mut ap: *mut ::core::ffi::VaListImpl) {
153+
pub unsafe extern "C" fn print_int(mut ap: *mut ::core::ffi::VaList) {
154154
printf(
155155
b"%d\0".as_ptr() as *const ::core::ffi::c_char,
156156
(*ap).arg::<::core::ffi::c_int>(),
157157
);
158158
}
159159
#[unsafe(no_mangle)]
160160
pub unsafe extern "C" fn borrowed_valist(mut count: size_t, mut c2rust_args: ...) {
161-
let mut ap: ::core::ffi::VaListImpl;
161+
let mut ap: ::core::ffi::VaList;
162162
ap = c2rust_args.clone();
163163
while count > 0 as size_t {
164164
print_int(&raw mut ap);

tests/unit/items/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "items-tests"
33
version = "0.1.0"
4-
edition = "2021"
4+
edition = "2024"
55

66
[dependencies]

tests/unit/items/src/test_fn_attrs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ unsafe extern "C" fn rust_gnu_inline_non_canonical_definition_extern
164164
let aliased_fn_syntax = |public| {
165165
format!(
166166
r#"
167-
extern "C" {{
167+
unsafe extern "C" {{
168168
#[link_name = "inline_extern"]
169169
{}fn aliased_fn();
170170
"#,

tests/unit/items/src/test_functions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::functions::rust_coreutils_static_assert;
22

33
#[link(name = "test")]
4-
extern "C" {
4+
unsafe extern "C" {
55
fn coreutils_static_assert();
66
}
77

0 commit comments

Comments
 (0)