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
23 changes: 14 additions & 9 deletions c2rust-ast-builder/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,17 @@ impl Builder {
)))
}

/// A convenience function for calling multiple methods in a chain.
pub fn method_chain_expr(
self,
expr: Box<Expr>,
calls: Vec<(PathSegment, Vec<Box<Expr>>)>,
) -> Box<Expr> {
calls.into_iter().fold(expr, |expr, (seg, args)| {
mk().method_call_expr(expr, seg, args)
})
}

pub fn tuple_expr(self, exprs: Vec<Box<Expr>>) -> Box<Expr> {
Box::new(Expr::Tuple(ExprTuple {
attrs: self.attrs,
Expand Down Expand Up @@ -2088,17 +2099,11 @@ impl Builder {
self,
capture: CaptureBy,
mov: Movability,
decl: FnDecl,
inputs: Vec<Pat>,
output: ReturnType,
body: Box<Expr>,
) -> Box<Expr> {
let (_name, inputs, _variadic, output) = decl;
let inputs = inputs
.into_iter()
.map(|e| match e {
FnArg::Receiver(_s) => panic!("found 'self' in closure arguments"),
FnArg::Typed(PatType { pat, .. }) => *pat,
})
.collect();
let inputs = inputs.into_iter().collect();
let capture = match capture {
CaptureBy::Ref => None,
CaptureBy::Value => Some(Default::default()),
Expand Down
2 changes: 1 addition & 1 deletion c2rust-ast-builder/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
mod builder;
pub use crate::builder::{mk, properties, Builder, Make};
pub use crate::builder::{mk, properties, Builder, CaptureBy, Make};
128 changes: 90 additions & 38 deletions c2rust-transpile/src/translator/main_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//! Rust.

use super::*;
use c2rust_ast_builder::CaptureBy;
use failure::format_err;
use proc_macro2::{TokenStream, TokenTree};

Expand Down Expand Up @@ -53,45 +54,97 @@ impl<'c> Translation<'c> {
if n >= 2 {
// `argv` and `argc`

stmts.push(mk().local_stmt(Box::new(mk().local(
mk().mutbl().ident_pat("args"),
Some(mk().path_ty(vec![mk().path_segment_with_args(
stmts.push(mk().local_stmt(Box::new({
// ty = Vec<Vec<u8>>
let ty = mk().path_ty(vec![mk().path_segment_with_args(
"Vec",
mk().angle_bracketed_args(vec![
mk().mutbl().ptr_ty(mk().abs_path_ty(vec!["core", "ffi", "c_char"])),
]),
)])),
Some(mk().call_expr(mk().path_expr(vec!["Vec", "new"]), vec![])),
))));
stmts.push(mk().semi_stmt(mk().for_expr(
mk().ident_pat("arg"),
mk().call_expr(args_fn, vec![]),
mk().block(vec![mk().semi_stmt(mk().method_call_expr(
mk().path_expr(vec!["args"]),
"push",
vec![mk().method_call_expr(
mk().method_call_expr(
mk().call_expr(
// TODO(kkysen) change `"std"` to `"alloc"` after `#![feature(alloc_c_string)]` is stabilized in `1.63.0`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you keep this comment?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think we can switch to it now. rust-lang/rust#99277 was merged on 2022-07-16, before 2022-08-08.

Copy link
Contributor Author

@Rua Rua Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing std to alloc doesn't seem to work:

failed to resolve: use of unresolved module or unlinked crate alloc

I guess that it's not as simple as just swapping out one for the other.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I think we also need to add extern crate alloc;. We can do that separately then, and just leave the comment here for now.

mk().abs_path_expr(vec!["std", "ffi", "CString", "new"]),
vec![mk().path_expr(vec!["arg"])],
),
"expect",
vec![mk().lit_expr("Failed to convert argument into CString.")],
),
"into_raw",
mk().angle_bracketed_args(vec![mk().path_ty(vec![mk()
.path_segment_with_args(
"Vec",
mk().angle_bracketed_args(vec![mk().ident_ty("u8")]),
)])]),
)]);
// map_arg = |arg| {
// (::std::ffi::CString::new(arg))
// .expect("Failed to convert argument into CString.")
// .into_bytes_with_nul()
// }
let cstring_call = mk().call_expr(
// TODO(kkysen) change `"std"` to `"alloc"` after `#![feature(alloc_c_string)]` is stabilized in `1.63.0`
mk().abs_path_expr(vec!["std", "ffi", "CString", "new"]),
vec![mk().path_expr(vec!["arg"])],
);
let expect_arg = mk().lit_expr("Failed to convert argument into CString.");
let map_arg = mk().closure_expr(
CaptureBy::Ref,
Movability::Movable,
vec![mk().ident_pat("arg")],
ReturnType::Default,
mk().method_chain_expr(
cstring_call,
vec![
(mk().path_segment("expect"), vec![expect_arg]),
(mk().path_segment("into_bytes_with_nul"), vec![]),
],
),
);
// init = args_fn
// .map(map_arg)
// .collect();
let init = mk().method_chain_expr(
mk().call_expr(args_fn, vec![]),
vec![
(mk().path_segment("map"), vec![map_arg]),
(mk().path_segment("collect"), vec![]),
],
);
mk().local(mk().mutbl().ident_pat("args_strings"), Some(ty), Some(init))
})));

stmts.push(mk().local_stmt(Box::new({
// ty = Vec<*mut ::core::ffi::c_char>
let ty = mk().path_ty(vec![mk().path_segment_with_args(
"Vec",
mk().angle_bracketed_args(vec![mk()
.mutbl()
.ptr_ty(mk().abs_path_ty(vec!["core", "ffi", "c_char"]))]),
)]);
// map_arg = |arg| arg.as_mut_ptr() as *mut ::core::ffi::c_char
let map_arg = mk().closure_expr(
CaptureBy::Ref,
Movability::Movable,
vec![mk().ident_pat("arg")],
ReturnType::Default,
mk().cast_expr(
mk().method_call_expr(mk().ident_expr("arg"), "as_mut_ptr", vec![]),
mk().mutbl()
.ptr_ty(mk().abs_path_ty(vec!["core", "ffi", "c_char"])),
),
);
// chain_arg = ::core::iter::once(::core::ptr::null_mut())
let chain_arg = mk().call_expr(
mk().abs_path_expr(vec!["core", "iter", "once"]),
vec![mk().call_expr(
mk().abs_path_expr(vec!["core", "ptr", "null_mut"]),
vec![],
)],
))]),
None::<Ident>,
)));
stmts.push(mk().semi_stmt(mk().method_call_expr(
mk().path_expr(vec!["args"]),
"push",
vec![
mk().call_expr(mk().abs_path_expr(vec!["core", "ptr", "null_mut"]), vec![]),
],
)));
);
// init = args_strings
// .iter_mut()
// .map(map_arg)
// .chain(chain_arg)
// .collect()
let init = mk().method_chain_expr(
mk().ident_expr("args_strings"),
vec![
(mk().path_segment("iter_mut"), vec![]),
(mk().path_segment("map"), vec![map_arg]),
(mk().path_segment("chain"), vec![chain_arg]),
(mk().path_segment("collect"), vec![]),
],
);
mk().local(mk().mutbl().ident_pat("args_ptrs"), Some(ty), Some(init))
})));

let argc_ty: Box<Type> = match self.ast_context.index(parameters[0]).kind {
CDeclKind::Variable { ref typ, .. } => self.convert_type(typ.ctype),
Expand All @@ -105,8 +158,7 @@ impl<'c> Translation<'c> {
"Cannot find type of 'argv' argument in main function",
)),
}?;

let args = mk().ident_expr("args");
let args = mk().ident_expr("args_ptrs");
let argc = mk().binary_expr(
BinOp::Sub(Default::default()),
mk().method_call_expr(args.clone(), "len", no_args.clone()),
Expand Down
3 changes: 3 additions & 0 deletions c2rust-transpile/tests/snapshots/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int main(int argc, char **argv, char **envp) {
return 0;
}
51 changes: 51 additions & 0 deletions c2rust-transpile/tests/snapshots/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
source: c2rust-transpile/tests/snapshots.rs
expression: cat tests/snapshots/main.rs
input_file: c2rust-transpile/tests/snapshots/main.c
---
#![allow(
dead_code,
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unused_assignments,
unused_mut
)]
unsafe fn main_0(
mut argc: ::core::ffi::c_int,
mut argv: *mut *mut ::core::ffi::c_char,
mut envp: *mut *mut ::core::ffi::c_char,
) -> ::core::ffi::c_int {
return 0 as ::core::ffi::c_int;
}
pub fn main() {
let mut args_strings: Vec<Vec<u8>> = ::std::env::args()
.map(|arg| {
::std::ffi::CString::new(arg)
.expect("Failed to convert argument into CString.")
.into_bytes_with_nul()
})
.collect();
let mut args_ptrs: Vec<*mut ::core::ffi::c_char> = args_strings
.iter_mut()
.map(|arg| arg.as_mut_ptr() as *mut ::core::ffi::c_char)
.chain(::core::iter::once(::core::ptr::null_mut()))
.collect();
let mut vars: Vec<*mut ::core::ffi::c_char> = Vec::new();
for (var_name, var_value) in ::std::env::vars() {
let var: String = format!("{}={}", var_name, var_value);
vars.push(
::std::ffi::CString::new(var)
.expect("Failed to convert environment variable into CString.")
.into_raw(),
);
}
vars.push(::core::ptr::null_mut());
unsafe {
::std::process::exit(main_0(
(args_ptrs.len() - 1) as ::core::ffi::c_int,
args_ptrs.as_mut_ptr() as *mut *mut ::core::ffi::c_char,
vars.as_mut_ptr() as *mut *mut ::core::ffi::c_char,
) as i32)
}
}