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
8 changes: 8 additions & 0 deletions c2rust-transpile/src/translator/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ impl<'c> Translation<'c> {

/// Determine if we're able to convert this const macro expansion.
fn can_convert_const_macro_expansion(&self, expr_id: CExprId) -> TranslationResult<()> {
// Bitfield struct initializers are lowered to non-`const` setter calls,
// so they can't be emitted as a `const` item.
if self.expr_initializes_bitfield(expr_id) {
Err(format_err!(
"macro initializes a bitfield, which is not const"
))?;
}

match self.tcfg.translate_const_macros {
TranslateMacros::None => Err(format_err!("translate_const_macros is None"))?,
TranslateMacros::Conservative => {
Expand Down
39 changes: 39 additions & 0 deletions c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1768,6 +1768,45 @@ impl<'c> Translation<'c> {
false
}

/// Does this expression initialize a struct that contains a bitfield?
///
/// Bitfield initialization is lowered to non-`const` setter method calls
/// Such an initializer cannot appear in a `const` or `static` item.
/// Const-like macros that expand to one must be left inlined at use sites.
pub(crate) fn expr_initializes_bitfield(&self, expr_id: CExprId) -> bool {
for i in DFExpr::new(&self.ast_context, expr_id.into()) {
let expr_id = match i {
SomeId::Expr(expr_id) => expr_id,
_ => continue,
};
if let CExprKind::InitList(qtype, ..) =
self.ast_context.index_unwrap_parens(expr_id).kind
{
if let CTypeKind::Struct(decl_id) = self.ast_context.resolve_type(qtype.ctype).kind
{
if let CDeclKind::Struct {
fields: Some(fields),
..
} = &self.ast_context[decl_id].kind
{
if fields.iter().any(|field_id| {
matches!(
self.ast_context[*field_id].kind,
CDeclKind::Field {
bitfield_width: Some(_),
..
}
)
}) {
return true;
}
}
}
}
}
false
}

fn add_static_initializer_to_section(
&self,
ctx: ExprContext,
Expand Down
7 changes: 7 additions & 0 deletions c2rust-transpile/tests/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,13 @@ fn test_compound_literals() {
transpile("compound_literals.c").run();
}

#[test]
fn test_const_macro_bitfield() {
transpile("const_macro_bitfield.c")
.expect_compile_error(true)
.run();
}

#[test]
fn test_empty_init() {
transpile("empty_init.c").run();
Expand Down
14 changes: 14 additions & 0 deletions c2rust-transpile/tests/snapshots/const_macro_bitfield.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// A const-like macro initializing a bitfield struct must be inlined at each
// use site, not emitted as a `const` (bitfield setters aren't `const`).

struct indexwriter {
unsigned int should_write : 1;
int fd;
};

#define INDEXWRITER_INIT { 0, 0 }

struct indexwriter make_writer(void) {
struct indexwriter writer = INDEXWRITER_INIT;
return writer;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
source: c2rust-transpile/tests/snapshots.rs
expression: cat tests/snapshots/const_macro_bitfield.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
)]
#[derive(Copy, Clone, ::c2rust_bitfields::BitfieldStruct)]
#[repr(C)]
pub struct indexwriter {
#[bitfield(name = "should_write", ty = "::core::ffi::c_uint", bits = "0..=0")]
pub should_write: [u8; 1],
#[bitfield(padding)]
pub c2rust_padding: [u8; 3],
pub fd: ::core::ffi::c_int,
}
#[no_mangle]
pub unsafe extern "C" fn make_writer() -> indexwriter {
let mut writer: indexwriter = {
let mut init = indexwriter {
should_write: [0; 1],
c2rust_padding: [0; 3],
fd: 0 as ::core::ffi::c_int,
};
init.set_should_write(0 as ::core::ffi::c_uint);
init
};
return writer;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
source: c2rust-transpile/tests/snapshots.rs
expression: cat tests/snapshots/const_macro_bitfield.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
)]
#[derive(Copy, Clone, ::c2rust_bitfields::BitfieldStruct)]
#[repr(C)]
pub struct indexwriter {
#[bitfield(name = "should_write", ty = "::core::ffi::c_uint", bits = "0..=0")]
pub should_write: [u8; 1],
#[bitfield(padding)]
pub c2rust_padding: [u8; 3],
pub fd: ::core::ffi::c_int,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn make_writer() -> indexwriter {
let mut writer: indexwriter = {
let mut init = indexwriter {
should_write: [0; 1],
c2rust_padding: [0; 3],
fd: 0 as ::core::ffi::c_int,
};
init.set_should_write(0 as ::core::ffi::c_uint);
init
};
return writer;
}
6 changes: 4 additions & 2 deletions tests/unit/cleanup_attr/src/cleanup.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
static int trace_buf[16];
static int trace_n;
/* Thread-local so the parallel test harness gives each test its own trace
* instead of racing on shared state (both here and in the transpiled Rust). */
static _Thread_local int trace_buf[16];
static _Thread_local int trace_n;

static void reset(void) {
trace_n = 0;
Expand Down
1 change: 1 addition & 0 deletions tests/unit/cleanup_attr/src/test_cleanup.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! feature_thread_local
use crate::cleanup::{
rust_run_early_return, rust_run_goto, rust_run_multiple, rust_run_nested, rust_run_single,
rust_run_typedef,
Expand Down
Loading