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
6 changes: 5 additions & 1 deletion c2rust-ast-exporter/src/AstExporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3084,6 +3084,11 @@ class TranslateConsumer : public clang::ASTConsumer {
cbor_encoder_close_container(&outer, &array);

// 3. Encode all of the visited file names
// A file containing only comments has no AST nodes to register its
// file ID. Register it before emitting the table, since encoding
// comments below must not introduce file IDs missing from it.
const SourceManager& sourceMgr = Context.getSourceManager();
visitor.getExporterFileId(sourceMgr.getMainFileID(), false);
auto files = visitor.getFiles();
cbor_encoder_create_array(&outer, &array, files.size());
for (auto const &file : files) {
Expand All @@ -3107,7 +3112,6 @@ class TranslateConsumer : public clang::ASTConsumer {
//
// Getting all comments requires -fparse-all-comments (see
// augment_argv())!
const SourceManager& sourceMgr = Context.getSourceManager();
#if CLANG_VERSION_MAJOR < 10
auto comments = Context.getRawCommentList().getComments();
cbor_encoder_create_array(&outer, &array, comments.size());
Expand Down
97 changes: 90 additions & 7 deletions c2rust-transpile/src/translator/atomics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,67 @@ pub(crate) fn order_ty_name(order: Ordering) -> &'static str {
}
}

// Rust represents nullable C function pointers as Option<fn>, which atomic
// intrinsics reject. Both AtomicExpr and legacy builtin calls must operate on
// the raw pointer representation, restoring the function pointer on results.
pub(crate) struct AtomicValue {
function_pointer: Option<Box<Type>>,
}

impl AtomicValue {
fn raw_pointer_type() -> Box<Type> {
mk().mutbl().ptr_ty(mk().tuple_ty(vec![]))
}

pub(crate) fn storage(&self, ptr: Box<Expr>) -> Box<Expr> {
if self.function_pointer.is_some() {
mk().cast_expr(ptr, mk().mutbl().ptr_ty(Self::raw_pointer_type()))
} else {
ptr
}
}

pub(crate) fn lower(&self, val: Box<Expr>) -> Box<Expr> {
match &self.function_pointer {
Some(ty) => transmute_expr(ty.clone(), Self::raw_pointer_type(), val),
None => val,
}
}

pub(crate) fn restore(&self, val: Box<Expr>) -> Box<Expr> {
match &self.function_pointer {
Some(ty) => transmute_expr(Self::raw_pointer_type(), ty.clone(), val),
None => val,
}
}

pub(crate) fn zero(&self) -> Box<Expr> {
if self.function_pointer.is_some() {
mk().call_expr(mk().abs_path_expr(vec!["core", "ptr", "null_mut"]), vec![])
} else {
mk().lit_expr(mk().int_lit(0, ""))
}
}
}

impl<'c> Translation<'c> {
pub(crate) fn atomic_value(&self, ptr_id: CExprId) -> TranslationResult<AtomicValue> {
let ptr_ty = self.ast_context[ptr_id].kind.get_qual_type().unwrap();
let mut value_ty = self
.ast_context
.get_pointee_qual_type(ptr_ty.ctype)
.unwrap();
if let CTypeKind::Atomic(inner) = self.ast_context.resolve_type(value_ty.ctype).kind {
value_ty = inner;
}
let function_pointer = if self.ast_context.is_function_pointer(value_ty.ctype) {
Some(self.convert_type(value_ty.ctype)?)
} else {
None
};
Ok(AtomicValue { function_pointer })
}

fn atomic_intrinsic_expr_edition_2021(
&self,
base_name: &str,
Expand Down Expand Up @@ -155,6 +215,13 @@ impl<'c> Translation<'c> {
.transpose()?;
let weak = weak_id.and_then(|x| self.convert_constant_bool(x));

let value = self.atomic_value(ptr_id)?;
let ptr = if name != "__c11_atomic_init" {
ptr.map(|ptr| value.storage(ptr))
} else {
ptr
};

fn static_order<T>(order: Option<T>) -> T {
order.unwrap_or_else(|| {
// We have to select which intrinsic to use at runtime
Expand All @@ -167,7 +234,7 @@ impl<'c> Translation<'c> {
let order = static_order(order);

let atomic_load = self.atomic_intrinsic_expr("load", &[order]);
let call = mk().call_expr(atomic_load, vec![ptr]);
let call = value.restore(mk().call_expr(atomic_load, vec![ptr]));
if name == "__atomic_load" {
let ret = val1.expect("__atomic_load should have a ret argument");
Ok(ret.and_then(|ret| {
Expand Down Expand Up @@ -200,7 +267,7 @@ impl<'c> Translation<'c> {
} else {
val
};
let call = mk().call_expr(atomic_store, vec![ptr, val]);
let call = mk().call_expr(atomic_store, vec![ptr, value.lower(val)]);
self.convert_side_effects_expr(
ctx,
WithStmts::new_val(call),
Expand Down Expand Up @@ -233,7 +300,7 @@ impl<'c> Translation<'c> {
} else {
val
};
let call = mk().call_expr(fn_path, vec![ptr, val]);
let call = value.restore(mk().call_expr(fn_path, vec![ptr, value.lower(val)]));
if name == "__atomic_exchange" {
// LLVM stores the ret pointer in the order_fail slot
Ok(order_fail_id
Expand Down Expand Up @@ -315,8 +382,10 @@ impl<'c> Translation<'c> {

let atomic_cxchg =
self.atomic_intrinsic_cxchg_expr(weak, order, order_fail);
let call =
mk().call_expr(atomic_cxchg, vec![ptr, expected.clone(), desired]);
let call = mk().call_expr(
atomic_cxchg,
vec![ptr, value.lower(expected.clone()), value.lower(desired)],
);
let res_name = self
.renamer
.borrow_mut()
Expand All @@ -328,7 +397,7 @@ impl<'c> Translation<'c> {
)));
let assignment = mk().semi_stmt(mk().assign_expr(
expected,
mk().anon_field_expr(mk().ident_expr(&res_name), 0),
value.restore(mk().anon_field_expr(mk().ident_expr(&res_name), 0)),
));
let return_value = mk().anon_field_expr(mk().ident_expr(&res_name), 1);
Ok(self.convert_side_effects_expr(
Expand Down Expand Up @@ -366,16 +435,30 @@ impl<'c> Translation<'c> {
weak: bool,
order_succ: Ordering,
order_fail: Ordering,
ptr_id: CExprId,
dst: Box<Expr>,
old_val: Box<Expr>,
src_val: Box<Expr>,
returns_val: bool,
) -> TranslationResult<WithStmts<Box<Expr>>> {
// Emit `atomic_cxchg(a0, a1, a2).idx`
let atomic_cxchg = self.atomic_intrinsic_cxchg_expr(weak, order_succ, order_fail);
let call = mk().call_expr(atomic_cxchg, vec![dst, old_val, src_val]);
let value = self.atomic_value(ptr_id)?;
let call = mk().call_expr(
atomic_cxchg,
vec![
value.storage(dst),
value.lower(old_val),
value.lower(src_val),
],
);
let field_idx = if returns_val { 0 } else { 1 };
let call_expr = mk().anon_field_expr(call, field_idx);
let call_expr = if returns_val {
value.restore(call_expr)
} else {
call_expr
};
Ok(self.convert_side_effects_expr(
ctx,
WithStmts::new_val(call_expr),
Expand Down
11 changes: 8 additions & 3 deletions c2rust-transpile/src/translator/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ impl<'c> Translation<'c> {
false,
SeqCst,
SeqCst,
args[0],
arg0,
arg1,
arg2,
Expand Down Expand Up @@ -570,10 +571,13 @@ impl<'c> Translation<'c> {
| "__sync_lock_test_and_set_16" => {
// Emit `atomic_xchg_acquire(arg0, arg1)`
let atomic_func = self.atomic_intrinsic_expr("xchg", &[Acquire]);
let value = self.atomic_value(args[0])?;
let arg0 = self.convert_expr(ctx.used(), args[0], None)?;
let arg1 = self.convert_expr(ctx.used(), args[1], None)?;
Ok(arg0.zip(arg1).and_then(|(arg0, arg1)| {
let call_expr = mk().call_expr(atomic_func, vec![arg0, arg1]);
let call_expr = value.restore(
mk().call_expr(atomic_func, vec![value.storage(arg0), value.lower(arg1)]),
);
self.convert_side_effects_expr(
ctx,
WithStmts::new_val(call_expr),
Expand All @@ -589,10 +593,11 @@ impl<'c> Translation<'c> {
| "__sync_lock_release_16" => {
// Emit `atomic_store_release(arg0, 0)`
let atomic_func = self.atomic_intrinsic_expr("store", &[Release]);
let value = self.atomic_value(args[0])?;
let arg0 = self.convert_expr(ctx.used(), args[0], None)?;
Ok(arg0.and_then(|arg0| {
let zero = mk().lit_expr(mk().int_lit(0, ""));
let call_expr = mk().call_expr(atomic_func, vec![arg0, zero]);
let call_expr =
mk().call_expr(atomic_func, vec![value.storage(arg0), value.zero()]);
self.convert_side_effects_expr(
ctx,
WithStmts::new_val(call_expr),
Expand Down
2 changes: 2 additions & 0 deletions c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4468,6 +4468,8 @@ impl<'c> Translation<'c> {
mk().lit_expr(mk().float_unsuffixed_lit("0.")),
)),
}
} else if let &CTypeKind::Atomic(inner) = resolved_ty {
self.implicit_default_expr(ctx, inner.ctype)
} else if let &CTypeKind::Pointer(_) = resolved_ty {
self.null_ptr(resolved_ty_id).map(WithStmts::new_val)
} else if let &CTypeKind::ConstantArray(elt, sz) = resolved_ty {
Expand Down
21 changes: 19 additions & 2 deletions c2rust-transpile/src/translator/pointers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,14 +593,31 @@ impl<'c> Translation<'c> {
Mutability::Mutable => "with_exposed_provenance_mut",
},
};
let pointee_type_rs = self.convert_pointee_type(pointee_type_id.ctype)?;
// Extern types are unsized, but the exposed-provenance constructors
// require Sized pointees. Construct a c_void pointer first and cast
// it to the opaque pointer type, which is still a thin pointer.
let is_opaque = self
.ast_context
.is_forward_declared_type(pointee_type_id.ctype);
let pointee_type_rs = if is_opaque {
mk().abs_path_ty(vec!["core", "ffi", "c_void"])
} else {
self.convert_pointee_type(pointee_type_id.ctype)?
};
let type_args = mk().angle_bracketed_args(vec![pointee_type_rs]);
let fn_expr = mk().abs_path_expr(vec![
mk().path_segment("core"),
mk().path_segment("ptr"),
mk().path_segment_with_args(fn_name, type_args),
]);
let val = val.map(|val| mk().call_expr(fn_expr, vec![val]));
let val = val.map(|val| {
let ptr = mk().call_expr(fn_expr, vec![val]);
if is_opaque {
mk().cast_expr(ptr, target_ty)
} else {
ptr
}
});

Ok(val)
}
Expand Down
16 changes: 12 additions & 4 deletions c2rust-transpile/src/translator/variadic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ impl<'c> Translation<'c> {
enum VaArgCastKind {
Cast(Box<Type>),
Enum(CDeclId),
Transmute,
Transmute(Box<Type>),
}

let mut arg_ty: Option<Box<Type>> = None;
Expand Down Expand Up @@ -217,7 +217,7 @@ impl<'c> Translation<'c> {
is_variadic,
)?;

cast_kind = Some(VaArgCastKind::Transmute);
cast_kind = Some(VaArgCastKind::Transmute(self.convert_type(ty.ctype)?));
arg_ty = Some(mk().set_mutbl(p.mutability()).ptr_ty(fn_ty));
} else if self.ast_context.is_forward_declared_type(p.ctype) {
cast_kind = Some(VaArgCastKind::Cast(self.convert_type(ty.ctype).unwrap()));
Expand Down Expand Up @@ -250,8 +250,16 @@ impl<'c> Translation<'c> {
VaArgCastKind::Enum(enum_id) => {
self.enum_constructor_expr(enum_id, val, false)
}
VaArgCastKind::Transmute => {
transmute_expr(mk().infer_ty(), mk().infer_ty(), val)
VaArgCastKind::Transmute(ty) => {
// An enclosing cast or field access may prevent
// inference of the transmute's destination type.
Comment thread
thedataking marked this conversation as resolved.
// For `callbacks.fn = va_arg(arg, char_to_int_fp)`, emit
// `transmute::<_, char_to_int_fp>(arg.arg::<*mut Fn>())
// as Option<Fn>`, where Fn abbreviates the underlying
// `unsafe extern "C" fn(c_char) -> c_int` signature.
// With `transmute::<_, _>(...)`, inference fails:
// the `as` cast does not determine its input type.
transmute_expr(mk().infer_ty(), ty, val)
}
};
}
Expand Down
10 changes: 10 additions & 0 deletions c2rust-transpile/tests/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,11 @@ fn test_bool() {
transpile("bool.c").run();
}

#[test]
fn test_comments_only() {
transpile("comments_only.c").run();
}

#[test]
fn test_compound_literals() {
transpile("compound_literals.c").run();
Expand Down Expand Up @@ -410,6 +415,11 @@ fn test_main_fn() {
transpile("main_fn.c").run();
}

#[test]
fn test_opaque_pointer_casts() {
transpile("opaque_pointer_casts.c").run();
}

#[test]
fn test_out_of_range_lit() {
transpile("out_of_range_lit.c").run();
Expand Down
7 changes: 7 additions & 0 deletions c2rust-transpile/tests/snapshots/comments_only.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* A translation unit may contain comments without any declarations. */

#ifdef DISABLED_FEATURE
int feature(void) { return 42; }
#endif

/* Keep multiple comments to exercise source location sorting. */
11 changes: 11 additions & 0 deletions c2rust-transpile/tests/snapshots/opaque_pointer_casts.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
struct Opaque;
union OpaqueUnion;
typedef struct Opaque Opaque;

Opaque *sentinel(void) { return (Opaque *)8; }
const Opaque *const_sentinel(void) { return (const Opaque *)8; }
union OpaqueUnion *union_sentinel(void) { return (union OpaqueUnion *)8; }
Opaque *from_address(unsigned long address) { return (Opaque *)address; }

static const Opaque *static_sentinel = (const Opaque *)8;
const Opaque *get_static_sentinel(void) { return static_sentinel; }
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
source: c2rust-transpile/tests/snapshots.rs
expression: cat tests/snapshots/comments_only.2021.clang15.rs
---
#![allow(
clippy::missing_safety_doc,
dead_code,
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unused_assignments,
unused_mut
)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: c2rust-transpile/tests/snapshots.rs
expression: cat tests/snapshots/comments_only.2024.clang15.rs
---
#![allow(
clippy::missing_safety_doc,
dead_code,
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unsafe_op_in_unsafe_fn,
unused_assignments,
unused_mut
)]
Loading
Loading