Skip to content
Merged
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
19 changes: 8 additions & 11 deletions c2rust-transpile/src/convert_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -17,6 +20,7 @@ enum FieldKey {
}

pub struct TypeConverter {
pub edition: RustEdition,
pub translate_valist: bool,
renamer: Renamer<CDeclId>,
fields: HashMap<CDeclId, Renamer<FieldKey>>,
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -318,8 +316,7 @@ impl TypeConverter {
ctype: CTypeId,
) -> TranslationResult<Box<Type>> {
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 {
Expand Down
2 changes: 1 addition & 1 deletion c2rust-transpile/src/translator/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")));
}
}
Expand Down
15 changes: 6 additions & 9 deletions c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)?;
}
Expand Down
11 changes: 2 additions & 9 deletions c2rust-transpile/src/translator/structs_unions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)?
};
Expand Down
42 changes: 38 additions & 4 deletions c2rust-transpile/src/translator/variadic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,38 @@ macro_rules! match_or {
};
}

pub fn mk_va_list_ty(edition: RustEdition, lifetime: Option<&str>) -> Box<Type> {
// This was updated in <https://github.com/rust-lang/rust/pull/141980>,
// 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<Expr>) -> Box<Expr> {
// This was updated in <https://github.com/rust-lang/rust/pull/141980>,
// 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 {
Expand Down Expand Up @@ -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");

Expand Down
1 change: 0 additions & 1 deletion c2rust-transpile/tests/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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 {
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -138,27 +138,27 @@ 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>(),
);
}
#[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);
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/items/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "items-tests"
version = "0.1.0"
edition = "2021"
edition = "2024"

[dependencies]
2 changes: 1 addition & 1 deletion tests/unit/items/src/test_fn_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
"#,
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/items/src/test_functions.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::functions::rust_coreutils_static_assert;

#[link(name = "test")]
extern "C" {
unsafe extern "C" {
fn coreutils_static_assert();
}

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/items/src/test_linking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/items/src/test_noop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/items/src/test_varargs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, ...);
Expand Down
Loading