Skip to content

Commit b03e543

Browse files
committed
transpile: Translate va_list with correct types
1 parent 38c9b36 commit b03e543

File tree

5 files changed

+94
-58
lines changed

5 files changed

+94
-58
lines changed

c2rust-transpile/src/convert_type.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,10 @@ impl TypeConverter {
250250
) -> TranslationResult<Box<Type>> {
251251
let barefn_inputs = params
252252
.iter()
253-
.map(|x| mk().bare_arg(self.convert(ctxt, x.ctype).unwrap(), None::<Box<Ident>>))
253+
.map(|x| {
254+
let ty = self.convert_function_param(ctxt, x.ctype).unwrap();
255+
mk().bare_arg(ty, None::<Box<Ident>>)
256+
})
254257
.collect::<Vec<_>>();
255258

256259
let output = match ret {
@@ -317,7 +320,7 @@ impl TypeConverter {
317320
ctype: CTypeId,
318321
) -> TranslationResult<Box<Type>> {
319322
if self.translate_valist && ctxt.is_va_list(ctype) {
320-
let ty = mk().abs_path_ty(vec!["core", "ffi", "VaList"]);
323+
let ty = mk().abs_path_ty(vec!["core", "ffi", "VaListImpl"]);
321324
return Ok(ty);
322325
}
323326

@@ -442,6 +445,22 @@ impl TypeConverter {
442445
}
443446
}
444447

448+
// Variant of `convert` that handles types that need to be converted differently if they
449+
// are the type of a function parameter.
450+
pub fn convert_function_param(
451+
&mut self,
452+
ctxt: &TypedAstContext,
453+
ctype: CTypeId,
454+
) -> TranslationResult<Box<Type>> {
455+
if ctxt.is_va_list(ctype) {
456+
// va_list parameters are translated as VaList rather than VaListImpl
457+
let ty = mk().abs_path_ty(vec!["core", "ffi", "VaList"]);
458+
return Ok(ty);
459+
}
460+
461+
self.convert(ctxt, ctype)
462+
}
463+
445464
/// Add the given parameters to a K&R function pointer type,
446465
/// returning a full signature or `None` if the function isn't K&R.
447466
pub fn knr_function_type_with_parameters(

c2rust-transpile/src/translator/builtins.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,7 @@ impl<'c> Translation<'c> {
319319
if ctx.is_unused() && args.len() == 2 {
320320
if let Some(va_id) = self.match_vastart(args[0]) {
321321
if self.ast_context.get_decl(&va_id).is_some() {
322-
let dst =
323-
self.convert_expr(ctx.expect_valistimpl().used(), args[0], None)?;
322+
let dst = self.convert_expr(ctx.used(), args[0], None)?;
324323
let fn_ctx = self.function_context.borrow();
325324
let src = fn_ctx.get_va_list_arg_name();
326325

@@ -341,10 +340,8 @@ impl<'c> Translation<'c> {
341340
"__builtin_va_copy" => {
342341
if ctx.is_unused() && args.len() == 2 {
343342
if let Some((_dst_va_id, _src_va_id)) = self.match_vacopy(args[0], args[1]) {
344-
let dst =
345-
self.convert_expr(ctx.expect_valistimpl().used(), args[0], None)?;
346-
let src =
347-
self.convert_expr(ctx.expect_valistimpl().used(), args[1], None)?;
343+
let dst = self.convert_expr(ctx.used(), args[0], None)?;
344+
let src = self.convert_expr(ctx.used(), args[1], None)?;
348345

349346
let call_expr = mk().method_call_expr(src.to_expr(), "clone", vec![]);
350347
let assign_expr = mk().assign_expr(dst.to_expr(), call_expr);

c2rust-transpile/src/translator/mod.rs

Lines changed: 66 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,6 @@ pub struct ExprContext {
126126
// address in function pointer literals.
127127
needs_address: bool,
128128

129-
/// Set to false if we should decay VaListImpl to VaList or true if we are
130-
/// expect a VaListImpl in this context.
131-
expecting_valistimpl: bool,
132-
133129
ternary_needs_parens: bool,
134130
expanding_macro: Option<CDeclId>,
135131
}
@@ -193,13 +189,6 @@ impl ExprContext {
193189
}
194190
}
195191

196-
pub fn expect_valistimpl(self) -> Self {
197-
ExprContext {
198-
expecting_valistimpl: true,
199-
..self
200-
}
201-
}
202-
203192
/// Are we expanding the given macro in the current context?
204193
pub fn expanding_macro(&self, mac: &CDeclId) -> bool {
205194
match self.expanding_macro {
@@ -505,7 +494,6 @@ pub fn translate(
505494
decay_ref: DecayRef::Default,
506495
is_bitfield_write: false,
507496
needs_address: false,
508-
expecting_valistimpl: false,
509497
ternary_needs_parens: false,
510498
expanding_macro: None,
511499
};
@@ -1272,6 +1260,11 @@ struct ConvertedVariable {
12721260
pub init: TranslationResult<WithStmts<Box<Expr>>>,
12731261
}
12741262

1263+
struct ConvertedFunctionParam {
1264+
pub ty: Box<Type>,
1265+
pub mutbl: Mutability,
1266+
}
1267+
12751268
impl<'c> Translation<'c> {
12761269
pub fn new(
12771270
mut ast_context: TypedAstContext,
@@ -2345,8 +2338,7 @@ impl<'c> Translation<'c> {
23452338

23462339
// handle regular (non-variadic) arguments
23472340
for &(decl_id, ref var, typ) in arguments {
2348-
let ConvertedVariable { ty, mutbl, init: _ } =
2349-
self.convert_variable(ctx, None, typ)?;
2341+
let ConvertedFunctionParam { ty, mutbl } = self.convert_function_param(ctx, typ)?;
23502342

23512343
let pat = if var.is_empty() {
23522344
mk().wild_pat()
@@ -3063,6 +3055,25 @@ impl<'c> Translation<'c> {
30633055
Ok(ConvertedVariable { ty, mutbl, init })
30643056
}
30653057

3058+
fn convert_function_param(
3059+
&self,
3060+
ctx: ExprContext,
3061+
typ: CQualTypeId,
3062+
) -> TranslationResult<ConvertedFunctionParam> {
3063+
if self.ast_context.is_va_list(typ.ctype) {
3064+
let mutbl = if typ.qualifiers.is_const {
3065+
Mutability::Immutable
3066+
} else {
3067+
Mutability::Mutable
3068+
};
3069+
let ty = mk().abs_path_ty(vec!["core", "ffi", "VaList"]);
3070+
return Ok(ConvertedFunctionParam { mutbl, ty });
3071+
}
3072+
3073+
self.convert_variable(ctx, None, typ)
3074+
.map(|ConvertedVariable { ty, mutbl, .. }| ConvertedFunctionParam { ty, mutbl })
3075+
}
3076+
30663077
fn convert_type(&self, type_id: CTypeId) -> TranslationResult<Box<Type>> {
30673078
self.import_type(type_id);
30683079

@@ -3339,19 +3350,31 @@ impl<'c> Translation<'c> {
33393350
.collect()
33403351
}
33413352

3342-
/// Variant of `convert_exprs` for when only a prefix of the expression types are known due to
3343-
/// the relevant signature being varargs
3353+
/// Variant of `convert_exprs` for the arguments of a function call.
3354+
/// Accounts for differences in translation for arguments, and for varargs where only a prefix
3355+
/// of the expression types are known.
33443356
#[allow(clippy::vec_box/*, reason = "not worth a substantial refactor"*/)]
3345-
fn convert_exprs_partial(
3357+
fn convert_call_args(
33463358
&self,
33473359
ctx: ExprContext,
33483360
exprs: &[CExprId],
3349-
arg_tys: &[CQualTypeId],
3361+
arg_tys: Option<&[CQualTypeId]>,
3362+
is_variadic: bool,
33503363
) -> TranslationResult<WithStmts<Vec<Box<Expr>>>> {
3364+
let arg_tys = if let Some(arg_tys) = arg_tys {
3365+
if !is_variadic {
3366+
assert!(arg_tys.len() == exprs.len());
3367+
}
3368+
3369+
arg_tys
3370+
} else {
3371+
&[]
3372+
};
3373+
33513374
exprs
33523375
.iter()
33533376
.enumerate()
3354-
.map(|(n, arg)| self.convert_expr(ctx, *arg, arg_tys.get(n).copied()))
3377+
.map(|(n, arg)| self.convert_call_arg(ctx, *arg, arg_tys.get(n).copied()))
33553378
.collect()
33563379
}
33573380

@@ -3509,12 +3532,6 @@ impl<'c> Translation<'c> {
35093532
val = mk().cast_expr(val, ty);
35103533
}
35113534

3512-
// Most references to the va_list should refer to the VaList
3513-
// type, not VaListImpl
3514-
if !ctx.expecting_valistimpl && self.ast_context.is_va_list(qual_ty.ctype) {
3515-
val = mk().method_call_expr(val, "as_va_list", Vec::new());
3516-
}
3517-
35183535
// If we are referring to a function and need its address, we
35193536
// need to cast it to fn() to ensure that it has a real address.
35203537
let mut set_unsafe = false;
@@ -4057,19 +4074,8 @@ impl<'c> Translation<'c> {
40574074
// We want to decay refs only when function is variadic
40584075
ctx.decay_ref = DecayRef::from(is_variadic);
40594076

4060-
let args = if is_variadic {
4061-
let arg_tys = arg_tys.unwrap_or_default();
4062-
self.convert_exprs_partial(ctx.used(), args, arg_tys.as_slice())?
4063-
} else {
4064-
let arg_tys = if let Some(ref arg_tys) = arg_tys {
4065-
assert!(arg_tys.len() == args.len());
4066-
Some(arg_tys.as_slice())
4067-
} else {
4068-
None
4069-
};
4070-
4071-
self.convert_exprs(ctx.used(), args, arg_tys)?
4072-
};
4077+
let args =
4078+
self.convert_call_args(ctx.used(), args, arg_tys.as_deref(), is_variadic)?;
40734079

40744080
let mut call_expr = args.map(|args| mk().call_expr(func, args));
40754081
if let Some(expected_ty) = override_ty {
@@ -4146,14 +4152,6 @@ impl<'c> Translation<'c> {
41464152
val = val.map(|v| mk().field_expr(v, field_name));
41474153
};
41484154

4149-
// Most references to the va_list should refer to the VaList
4150-
// type, not VaListImpl
4151-
if !ctx.expecting_valistimpl && self.ast_context.is_va_list(qual_ty.ctype) {
4152-
val = val.map(|v| {
4153-
mk().method_call_expr(v, "as_va_list", Vec::<Box<Expr>>::new())
4154-
});
4155-
}
4156-
41574155
// if the context wants a different type, add a cast
41584156
if let Some(expected_ty) = override_ty {
41594157
if expected_ty != qual_ty {
@@ -4268,6 +4266,28 @@ impl<'c> Translation<'c> {
42684266
Ok(expr)
42694267
}
42704268

4269+
/// Wrapper around `convert_expr` for the arguments of a function call.
4270+
pub fn convert_call_arg(
4271+
&self,
4272+
ctx: ExprContext,
4273+
expr_id: CExprId,
4274+
override_ty: Option<CQualTypeId>,
4275+
) -> TranslationResult<WithStmts<Box<Expr>>> {
4276+
let mut val;
4277+
4278+
if (self.ast_context.index(expr_id).kind.get_qual_type())
4279+
.map_or(false, |qtype| self.ast_context.is_va_list(qtype.ctype))
4280+
{
4281+
// No `override_ty` to avoid unwanted casting.
4282+
val = self.convert_expr(ctx, expr_id, None)?;
4283+
val = val.map(|val| mk().method_call_expr(val, "as_va_list", Vec::new()));
4284+
} else {
4285+
val = self.convert_expr(ctx, expr_id, override_ty)?;
4286+
}
4287+
4288+
Ok(val)
4289+
}
4290+
42714291
/// Convert the expansion of a const-like macro.
42724292
///
42734293
/// See [`TranspilerConfig::translate_const_macros`].

c2rust-transpile/src/translator/variadic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ impl<'c> Translation<'c> {
144144
val_id: CExprId,
145145
) -> TranslationResult<WithStmts<Box<Expr>>> {
146146
if self.tcfg.translate_valist {
147-
let val = self.convert_expr(ctx.expect_valistimpl().used(), val_id, None)?;
147+
let val = self.convert_expr(ctx.used(), val_id, None)?;
148148

149149
// The current implementation of the C-variadics feature doesn't allow us to
150150
// return `Option<fn(...) -> _>` from `VaList::arg`, so we detect function pointers

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub unsafe extern "C" fn my_vprintf(
4949
mut format: *const ::core::ffi::c_char,
5050
mut ap: ::core::ffi::VaList,
5151
) {
52-
vprintf(format, ap.as_va_list() as ::core::ffi::VaList);
52+
vprintf(format, ap.as_va_list());
5353
}
5454
#[no_mangle]
5555
pub unsafe extern "C" fn call_vprintf(mut format: *const ::core::ffi::c_char, mut args: ...) {
@@ -145,7 +145,7 @@ pub unsafe extern "C" fn restart_valist(mut fmt: *const ::core::ffi::c_char, mut
145145
vprintf(fmt, ap.as_va_list());
146146
}
147147
#[no_mangle]
148-
pub unsafe extern "C" fn print_int(mut ap: *mut ::core::ffi::VaList) {
148+
pub unsafe extern "C" fn print_int(mut ap: *mut ::core::ffi::VaListImpl) {
149149
printf(
150150
b"%d\0" as *const u8 as *const ::core::ffi::c_char,
151151
(*ap).arg::<::core::ffi::c_int>(),
@@ -156,7 +156,7 @@ pub unsafe extern "C" fn borrowed_valist(mut count: size_t, mut args: ...) {
156156
let mut ap: ::core::ffi::VaListImpl;
157157
ap = args.clone();
158158
while count > 0 as size_t {
159-
print_int(&mut ap.as_va_list());
159+
print_int(&mut ap);
160160
count = count.wrapping_sub(1);
161161
}
162162
}

0 commit comments

Comments
 (0)