From db2097b69b63278bb9d74d39818791d72edcecce Mon Sep 17 00:00:00 2001 From: David Herzka Date: Tue, 17 Oct 2023 17:30:58 -0400 Subject: [PATCH 01/14] [WIP] derive(Debug) --- c2rust-transpile/src/translator/mod.rs | 14 +++++++++++++- c2rust-transpile/src/translator/structs.rs | 16 ++++++++++++++-- tests/structs/src/debug_derive.c | 17 +++++++++++++++++ tests/structs/src/test_debug_derive.rs | 16 ++++++++++++++++ 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 tests/structs/src/debug_derive.c create mode 100644 tests/structs/src/test_debug_derive.rs diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 6c32f7cf68..2e9ebc2a34 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -1631,10 +1631,22 @@ impl<'c> Translation<'c> { let (field_entries, contains_va_list) = self.convert_struct_fields(decl_id, fields, platform_byte_size)?; + println!("field_entries = {field_entries:#?}"); + println!("ast_context = {:#?}", self.ast_context); + + + // for f in field_entries { + // self.ast_context.resolve_type(f.ty) + // } + let mut derives = vec![]; if !contains_va_list { derives.push("Copy"); derives.push("Clone"); + // WIP: only if possible + // - no unions + // - any other reqs? + derives.push("Debug"); }; let has_bitfields = fields @@ -1700,7 +1712,7 @@ impl<'c> Translation<'c> { let outer_struct = mk() .span(span) .pub_() - .call_attr("derive", vec!["Copy", "Clone"]) + .call_attr("derive", vec!["Copy", "Clone", "Debug"]) .meta_item_attr(AttrStyle::Outer, repr_attr) .struct_item(name, vec![outer_field], true); diff --git a/c2rust-transpile/src/translator/structs.rs b/c2rust-transpile/src/translator/structs.rs index fae47c4d15..fae37e1b92 100644 --- a/c2rust-transpile/src/translator/structs.rs +++ b/c2rust-transpile/src/translator/structs.rs @@ -7,7 +7,7 @@ use std::ops::Index; use super::named_references::NamedReference; use super::TranslationError; -use crate::c_ast::{BinOp, CDeclId, CDeclKind, CExprId, CRecordId, CTypeId}; +use crate::c_ast::{BinOp, CDeclId, CDeclKind, CExprId, CRecordId, CTypeId, CTypeKind}; use crate::diagnostics::TranslationResult; use crate::translator::{ExprContext, Translation, PADDING_SUFFIX}; use crate::with_stmts::WithStmts; @@ -377,7 +377,19 @@ impl<'a> Translation<'a> { field_entries.push(field); } - FieldType::Regular { field, .. } => field_entries.push(*field), + FieldType::Regular { field, ctype, .. } => { + let t = self.ast_context.resolve_type(ctype); + match t.kind { + CTypeKind::Struct(s) => { + // WIP: find struct members and recursively check if any are unions + } + CTypeKind::Union(..) => { + println!("found a union. field: {field:?}"); + } + _ => {} + } + field_entries.push(*field) + }, } } Ok((field_entries, contains_va_list)) diff --git a/tests/structs/src/debug_derive.c b/tests/structs/src/debug_derive.c new file mode 100644 index 0000000000..0cf804c6aa --- /dev/null +++ b/tests/structs/src/debug_derive.c @@ -0,0 +1,17 @@ +typedef struct { + struct { + struct { + union { + int d; + float e; + } c; + } b; + } a; +} S1; + +typedef struct { + int a; +} S2; + +S1 kS1; +S2 kS2; \ No newline at end of file diff --git a/tests/structs/src/test_debug_derive.rs b/tests/structs/src/test_debug_derive.rs new file mode 100644 index 0000000000..4d095fc880 --- /dev/null +++ b/tests/structs/src/test_debug_derive.rs @@ -0,0 +1,16 @@ +use crate::debug_derive::{rust_kS1, rust_kS2}; + +// WIP: if we make this derive optional, how can we run the test with it enabled? + +// xfail +pub fn test_union() { + unsafe { + format!("{rust_kS1:?}"); + } +} + +pub fn test_no_union() { + unsafe { + format!("{rust_kS2:?}"); + } +} From a3d33b9bb76eeeb94167938fc7d983353bf9e30c Mon Sep 17 00:00:00 2001 From: David Herzka Date: Tue, 17 Oct 2023 20:09:16 -0400 Subject: [PATCH 02/14] More work on derive(Debug) --- c2rust-transpile/src/translator/mod.rs | 17 ++--- c2rust-transpile/src/translator/structs.rs | 67 +++++++++++++++---- .../{debug_derive.c => debug_derive_bad.c} | 6 +- tests/structs/src/debug_derive_good.c | 5 ++ ...bug_derive.rs => test_debug_derive_bad.rs} | 12 ++-- tests/structs/src/test_debug_derive_good.rs | 9 +++ 6 files changed, 78 insertions(+), 38 deletions(-) rename tests/structs/src/{debug_derive.c => debug_derive_bad.c} (77%) create mode 100644 tests/structs/src/debug_derive_good.c rename tests/structs/src/{test_debug_derive.rs => test_debug_derive_bad.rs} (54%) create mode 100644 tests/structs/src/test_debug_derive_good.rs diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 2e9ebc2a34..85c6484475 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -1628,26 +1628,19 @@ impl<'c> Translation<'c> { } // Gather up all the field names and field types - let (field_entries, contains_va_list) = + let (field_entries, contains_va_list, debuggable) = self.convert_struct_fields(decl_id, fields, platform_byte_size)?; - println!("field_entries = {field_entries:#?}"); - println!("ast_context = {:#?}", self.ast_context); - - - // for f in field_entries { - // self.ast_context.resolve_type(f.ty) - // } + println!("{name} debuggable = {debuggable}"); let mut derives = vec![]; if !contains_va_list { derives.push("Copy"); derives.push("Clone"); - // WIP: only if possible - // - no unions - // - any other reqs? - derives.push("Debug"); }; + if debuggable { + derives.push("Debug"); + } let has_bitfields = fields .iter() diff --git a/c2rust-transpile/src/translator/structs.rs b/c2rust-transpile/src/translator/structs.rs index fae37e1b92..bba1b280a2 100644 --- a/c2rust-transpile/src/translator/structs.rs +++ b/c2rust-transpile/src/translator/structs.rs @@ -13,6 +13,7 @@ use crate::translator::{ExprContext, Translation, PADDING_SUFFIX}; use crate::with_stmts::WithStmts; use c2rust_ast_builder::mk; use c2rust_ast_printer::pprust; +use failure::format_err; use syn::{ self, AttrStyle, BinOp as RBinOp, Expr, ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprCast, ExprMethodCall, ExprUnary, Field, Meta, NestedMeta, Stmt, Type, @@ -276,6 +277,51 @@ impl<'a> Translation<'a> { Ok(reorganized_fields) } + // WIP: debuggable if: + // - no unions + // - any other reqs? + fn is_struct_field_debuggable(&self, ctype: CTypeId) -> bool { + let ty = self.ast_context.resolve_type(ctype); + let debuggable = match ty.kind { + CTypeKind::Struct(decl_id) => { + let decl = self + .ast_context + .get_decl(&decl_id) + .ok_or_else(|| format_err!("Missing decl {:?}", decl_id)).unwrap(); + match &decl.kind { + CDeclKind::Struct { fields, .. } => { + // println!("found struct {name:?} with fields {fields:?}"); + for field_decl_id in fields.as_ref().unwrap_or(&vec![]) { + let field_decl = self + .ast_context + .get_decl(&field_decl_id) + .ok_or_else(|| format_err!("Missing decl {:?}", field_decl_id)).unwrap(); + match &field_decl.kind { + CDeclKind::Field { typ, .. } => { + // println!("found field {name}"); + let debuggable = self.is_struct_field_debuggable(typ.ctype); + // println!("{name} debuggable = {debuggable}"); + if !debuggable { + return false; + } + } + _ => panic!("not a field"), + } + } + true + } + _ => panic!("not a struct") + } + } + CTypeKind::Union(..) => { + false + } + _ => true + }; + + debuggable + } + /// Here we output a struct derive to generate bitfield data that looks like this: /// /// ```no_run @@ -296,7 +342,7 @@ impl<'a> Translation<'a> { struct_id: CRecordId, field_ids: &[CDeclId], platform_byte_size: u64, - ) -> TranslationResult<(Vec, bool)> { + ) -> TranslationResult<(Vec, bool, bool)> { let mut field_entries = Vec::with_capacity(field_ids.len()); // We need to clobber bitfields in consecutive bytes together (leaving // regular fields alone) and add in padding as necessary @@ -317,6 +363,7 @@ impl<'a> Translation<'a> { field_name }; + let mut debuggable = true; for field_type in reorganized_fields { match field_type { FieldType::BitfieldGroup { @@ -377,22 +424,16 @@ impl<'a> Translation<'a> { field_entries.push(field); } - FieldType::Regular { field, ctype, .. } => { - let t = self.ast_context.resolve_type(ctype); - match t.kind { - CTypeKind::Struct(s) => { - // WIP: find struct members and recursively check if any are unions - } - CTypeKind::Union(..) => { - println!("found a union. field: {field:?}"); - } - _ => {} - } + FieldType::Regular { field, ctype, .. } => { + let field_debuggable = self.is_struct_field_debuggable(ctype); + // println!("field_debuggable = {field_debuggable}: {field:?}"); + debuggable &= field_debuggable; field_entries.push(*field) }, } } - Ok((field_entries, contains_va_list)) + println!("debuggable = {debuggable}"); + Ok((field_entries, contains_va_list, debuggable)) } /// Here we output a block to generate a struct literal initializer in. diff --git a/tests/structs/src/debug_derive.c b/tests/structs/src/debug_derive_bad.c similarity index 77% rename from tests/structs/src/debug_derive.c rename to tests/structs/src/debug_derive_bad.c index 0cf804c6aa..e163025ada 100644 --- a/tests/structs/src/debug_derive.c +++ b/tests/structs/src/debug_derive_bad.c @@ -1,3 +1,4 @@ +// A struct containing a union is not debuggable typedef struct { struct { struct { @@ -7,11 +8,6 @@ typedef struct { } c; } b; } a; -} S1; - -typedef struct { - int a; } S2; -S1 kS1; S2 kS2; \ No newline at end of file diff --git a/tests/structs/src/debug_derive_good.c b/tests/structs/src/debug_derive_good.c new file mode 100644 index 0000000000..a6897aa1da --- /dev/null +++ b/tests/structs/src/debug_derive_good.c @@ -0,0 +1,5 @@ +typedef struct { + int a; +} S1; + +S1 kS1; \ No newline at end of file diff --git a/tests/structs/src/test_debug_derive.rs b/tests/structs/src/test_debug_derive_bad.rs similarity index 54% rename from tests/structs/src/test_debug_derive.rs rename to tests/structs/src/test_debug_derive_bad.rs index 4d095fc880..4bc3c445a2 100644 --- a/tests/structs/src/test_debug_derive.rs +++ b/tests/structs/src/test_debug_derive_bad.rs @@ -1,15 +1,11 @@ -use crate::debug_derive::{rust_kS1, rust_kS2}; +//! xfail + +mod debug_derive_bad; +use debug_derive_bad::rust_kS2; // WIP: if we make this derive optional, how can we run the test with it enabled? -// xfail pub fn test_union() { - unsafe { - format!("{rust_kS1:?}"); - } -} - -pub fn test_no_union() { unsafe { format!("{rust_kS2:?}"); } diff --git a/tests/structs/src/test_debug_derive_good.rs b/tests/structs/src/test_debug_derive_good.rs new file mode 100644 index 0000000000..87cc91e5ce --- /dev/null +++ b/tests/structs/src/test_debug_derive_good.rs @@ -0,0 +1,9 @@ +use crate::debug_derive_good::rust_kS1; + +// WIP: if we make this derive optional, how can we run the test with it enabled? + +pub fn test_simple_struct() { + unsafe { + format!("{rust_kS1:?}"); + } +} From 124cf0e102e87a1059ba28b16ee6d19cccf52dc2 Mon Sep 17 00:00:00 2001 From: David Herzka Date: Fri, 3 Nov 2023 14:24:32 -0400 Subject: [PATCH 03/14] Some cleanup, renaming --- c2rust-transpile/src/translator/mod.rs | 6 +-- c2rust-transpile/src/translator/structs.rs | 53 +++++++++++----------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 85c6484475..96692c2ce9 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -1628,17 +1628,15 @@ impl<'c> Translation<'c> { } // Gather up all the field names and field types - let (field_entries, contains_va_list, debuggable) = + let (field_entries, contains_va_list, can_derive_debug) = self.convert_struct_fields(decl_id, fields, platform_byte_size)?; - println!("{name} debuggable = {debuggable}"); - let mut derives = vec![]; if !contains_va_list { derives.push("Copy"); derives.push("Clone"); }; - if debuggable { + if can_derive_debug { derives.push("Debug"); } let has_bitfields = diff --git a/c2rust-transpile/src/translator/structs.rs b/c2rust-transpile/src/translator/structs.rs index bba1b280a2..6f1a5341c7 100644 --- a/c2rust-transpile/src/translator/structs.rs +++ b/c2rust-transpile/src/translator/structs.rs @@ -277,31 +277,30 @@ impl<'a> Translation<'a> { Ok(reorganized_fields) } - // WIP: debuggable if: - // - no unions - // - any other reqs? - fn is_struct_field_debuggable(&self, ctype: CTypeId) -> bool { + /// Returns true if a struct field with this type can be part of a struct that derives `Debug` + fn can_struct_field_derive_debug(&self, ctype: CTypeId) -> bool { let ty = self.ast_context.resolve_type(ctype); - let debuggable = match ty.kind { + let can_derive_debug = match ty.kind { + + // Recurse into struct fields. A struct is debuggable iff all of its fields are CTypeKind::Struct(decl_id) => { let decl = self .ast_context .get_decl(&decl_id) - .ok_or_else(|| format_err!("Missing decl {:?}", decl_id)).unwrap(); + .ok_or_else(|| format_err!("Missing decl {:?}", decl_id)) + .unwrap(); match &decl.kind { CDeclKind::Struct { fields, .. } => { - // println!("found struct {name:?} with fields {fields:?}"); for field_decl_id in fields.as_ref().unwrap_or(&vec![]) { let field_decl = self .ast_context .get_decl(&field_decl_id) - .ok_or_else(|| format_err!("Missing decl {:?}", field_decl_id)).unwrap(); + .ok_or_else(|| format_err!("Missing decl {:?}", field_decl_id)) + .unwrap(); match &field_decl.kind { CDeclKind::Field { typ, .. } => { - // println!("found field {name}"); - let debuggable = self.is_struct_field_debuggable(typ.ctype); - // println!("{name} debuggable = {debuggable}"); - if !debuggable { + let can_derive_debug = self.can_struct_field_derive_debug(typ.ctype); + if !can_derive_debug { return false; } } @@ -310,16 +309,18 @@ impl<'a> Translation<'a> { } true } - _ => panic!("not a struct") + _ => panic!("not a struct"), } } - CTypeKind::Union(..) => { - false - } - _ => true + + // A union or struct containing a union cannot derive Debug + CTypeKind::Union(..) => false, + + // All translated non-union C types can derive Debug + _ => true, }; - debuggable + can_derive_debug } /// Here we output a struct derive to generate bitfield data that looks like this: @@ -363,7 +364,7 @@ impl<'a> Translation<'a> { field_name }; - let mut debuggable = true; + let mut can_derive_debug = true; for field_type in reorganized_fields { match field_type { FieldType::BitfieldGroup { @@ -424,16 +425,16 @@ impl<'a> Translation<'a> { field_entries.push(field); } - FieldType::Regular { field, ctype, .. } => { - let field_debuggable = self.is_struct_field_debuggable(ctype); - // println!("field_debuggable = {field_debuggable}: {field:?}"); - debuggable &= field_debuggable; + FieldType::Regular { field, ctype, .. } => { + // Struct is only debuggable if all regular fields are + // debuggable. Assume any fields added by the translator + // such as padding are debuggable + can_derive_debug &= self.can_struct_field_derive_debug(ctype); field_entries.push(*field) - }, + } } } - println!("debuggable = {debuggable}"); - Ok((field_entries, contains_va_list, debuggable)) + Ok((field_entries, contains_va_list, can_derive_debug)) } /// Here we output a block to generate a struct literal initializer in. From afca80ff5134f14c97d64ff9f21ef9e3f73805a3 Mon Sep 17 00:00:00 2001 From: David Herzka Date: Fri, 3 Nov 2023 15:51:54 -0400 Subject: [PATCH 04/14] Test that va_list can be debugged --- tests/structs/src/debug_derive_good.c | 9 ++++++++- tests/structs/src/test_debug_derive_bad.rs | 2 +- tests/structs/src/test_debug_derive_good.rs | 10 ++++++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/tests/structs/src/debug_derive_good.c b/tests/structs/src/debug_derive_good.c index a6897aa1da..b6b5724bd0 100644 --- a/tests/structs/src/debug_derive_good.c +++ b/tests/structs/src/debug_derive_good.c @@ -1,5 +1,12 @@ +#include + typedef struct { int a; } S1; -S1 kS1; \ No newline at end of file +typedef struct { + va_list v; +} S3; + +S1 kS1; +S3 kS3; \ No newline at end of file diff --git a/tests/structs/src/test_debug_derive_bad.rs b/tests/structs/src/test_debug_derive_bad.rs index 4bc3c445a2..144c5052cc 100644 --- a/tests/structs/src/test_debug_derive_bad.rs +++ b/tests/structs/src/test_debug_derive_bad.rs @@ -3,7 +3,7 @@ mod debug_derive_bad; use debug_derive_bad::rust_kS2; -// WIP: if we make this derive optional, how can we run the test with it enabled? +// TODO: if we make this derive optional, how can we run the test with it enabled? pub fn test_union() { unsafe { diff --git a/tests/structs/src/test_debug_derive_good.rs b/tests/structs/src/test_debug_derive_good.rs index 87cc91e5ce..1142f0e1a8 100644 --- a/tests/structs/src/test_debug_derive_good.rs +++ b/tests/structs/src/test_debug_derive_good.rs @@ -1,9 +1,15 @@ -use crate::debug_derive_good::rust_kS1; +use crate::debug_derive_good::{rust_kS1, rust_kS3}; -// WIP: if we make this derive optional, how can we run the test with it enabled? +// TODO: if we make this derive optional, how can we run the test with it enabled? pub fn test_simple_struct() { unsafe { format!("{rust_kS1:?}"); } } + +pub fn test_struct_containing_va_list() { + unsafe { + format!("{rust_kS3:?}"); + } +} \ No newline at end of file From 26159864fd5c392dcf35fc857cbe22a8dcd79143 Mon Sep 17 00:00:00 2001 From: David Herzka Date: Fri, 3 Nov 2023 16:02:20 -0400 Subject: [PATCH 05/14] Make derive(Debug) optional --- c2rust-transpile/src/lib.rs | 1 + c2rust-transpile/src/translator/mod.rs | 10 ++++++++-- c2rust/src/bin/c2rust-transpile.rs | 5 +++++ scripts/test_translator.py | 3 +++ tests/structs/src/debug_derive_bad.c | 2 ++ tests/structs/src/debug_derive_good.c | 2 ++ tests/structs/src/test_debug_derive_bad.rs | 2 -- tests/structs/src/test_debug_derive_good.rs | 2 -- 8 files changed, 21 insertions(+), 6 deletions(-) diff --git a/c2rust-transpile/src/lib.rs b/c2rust-transpile/src/lib.rs index 9421232c9d..9b67fa3680 100644 --- a/c2rust-transpile/src/lib.rs +++ b/c2rust-transpile/src/lib.rs @@ -82,6 +82,7 @@ pub struct TranspilerConfig { pub disable_refactoring: bool, pub preserve_unused_functions: bool, pub log_level: log::LevelFilter, + pub derive_debug: bool, // Options that control build files /// Emit `Cargo.toml` and `lib.rs` diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 96692c2ce9..2be77db73c 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -1636,7 +1636,7 @@ impl<'c> Translation<'c> { derives.push("Copy"); derives.push("Clone"); }; - if can_derive_debug { + if self.tcfg.derive_debug && can_derive_debug { derives.push("Debug"); } let has_bitfields = @@ -1700,10 +1700,16 @@ impl<'c> Translation<'c> { ]; let repr_attr = mk().meta_list("repr", outer_reprs); let outer_field = mk().pub_().enum_field(mk().ident_ty(inner_name)); + + let mut outer_struct_derives = vec!["Copy", "Clone"]; + if self.tcfg.derive_debug { + outer_struct_derives.push("Debug"); + } + let outer_struct = mk() .span(span) .pub_() - .call_attr("derive", vec!["Copy", "Clone", "Debug"]) + .call_attr("derive", outer_struct_derives) .meta_item_attr(AttrStyle::Outer, repr_attr) .struct_item(name, vec![outer_field], true); diff --git a/c2rust/src/bin/c2rust-transpile.rs b/c2rust/src/bin/c2rust-transpile.rs index ecc1dfe970..1baa6f2131 100644 --- a/c2rust/src/bin/c2rust-transpile.rs +++ b/c2rust/src/bin/c2rust-transpile.rs @@ -155,6 +155,10 @@ struct Args { /// Fail when the control-flow graph generates branching constructs #[clap(long)] fail_on_multiple: bool, + + /// Derive `Debug` trait for any structs that allow it (i.e., do not recursively contain any unions) + #[clap(long)] + derive_debug: bool, } #[derive(Debug, PartialEq, Eq, ValueEnum, Clone)] @@ -216,6 +220,7 @@ fn main() { emit_no_std: args.emit_no_std, enabled_warnings: args.warn.into_iter().collect(), log_level: args.log_level, + derive_debug: args.derive_debug, }; // binaries imply emit-build-files if !tcfg.binaries.is_empty() { diff --git a/scripts/test_translator.py b/scripts/test_translator.py index ef1c8d675e..6d71ad5b48 100755 --- a/scripts/test_translator.py +++ b/scripts/test_translator.py @@ -72,6 +72,7 @@ def __init__(self, log_level: str, path: str, flags: Set[str] = set()) -> None: self.translate_const_macros = "translate_const_macros" in flags self.reorganize_definitions = "reorganize_definitions" in flags self.emit_build_files = "emit_build_files" in flags + self.derive_debug = "derive_debug" in flags def translate(self, cc_db: str, ld_lib_path: str, extra_args: List[str] = []) -> RustFile: extensionless_file, _ = os.path.splitext(self.path) @@ -96,6 +97,8 @@ def translate(self, cc_db: str, ld_lib_path: str, extra_args: List[str] = []) -> args.append("--reorganize-definitions") if self.emit_build_files: args.append("--emit-build-files") + if self.derive_debug: + args.append("--derive-debug") if self.log_level == 'DEBUG': args.append("--log-level=debug") diff --git a/tests/structs/src/debug_derive_bad.c b/tests/structs/src/debug_derive_bad.c index e163025ada..32858b6294 100644 --- a/tests/structs/src/debug_derive_bad.c +++ b/tests/structs/src/debug_derive_bad.c @@ -1,3 +1,5 @@ +//! derive_debug + // A struct containing a union is not debuggable typedef struct { struct { diff --git a/tests/structs/src/debug_derive_good.c b/tests/structs/src/debug_derive_good.c index b6b5724bd0..8e802b84e3 100644 --- a/tests/structs/src/debug_derive_good.c +++ b/tests/structs/src/debug_derive_good.c @@ -1,3 +1,5 @@ +//! derive_debug + #include typedef struct { diff --git a/tests/structs/src/test_debug_derive_bad.rs b/tests/structs/src/test_debug_derive_bad.rs index 144c5052cc..8dbc19be66 100644 --- a/tests/structs/src/test_debug_derive_bad.rs +++ b/tests/structs/src/test_debug_derive_bad.rs @@ -3,8 +3,6 @@ mod debug_derive_bad; use debug_derive_bad::rust_kS2; -// TODO: if we make this derive optional, how can we run the test with it enabled? - pub fn test_union() { unsafe { format!("{rust_kS2:?}"); diff --git a/tests/structs/src/test_debug_derive_good.rs b/tests/structs/src/test_debug_derive_good.rs index 1142f0e1a8..f453bba6ec 100644 --- a/tests/structs/src/test_debug_derive_good.rs +++ b/tests/structs/src/test_debug_derive_good.rs @@ -1,7 +1,5 @@ use crate::debug_derive_good::{rust_kS1, rust_kS3}; -// TODO: if we make this derive optional, how can we run the test with it enabled? - pub fn test_simple_struct() { unsafe { format!("{rust_kS1:?}"); From cafde458f9d27c6681e07d53eecce721781c7dce Mon Sep 17 00:00:00 2001 From: David Herzka Date: Fri, 3 Nov 2023 16:07:22 -0400 Subject: [PATCH 06/14] cargo fmt --- c2rust-transpile/src/translator/structs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/c2rust-transpile/src/translator/structs.rs b/c2rust-transpile/src/translator/structs.rs index 6f1a5341c7..e86ca822bc 100644 --- a/c2rust-transpile/src/translator/structs.rs +++ b/c2rust-transpile/src/translator/structs.rs @@ -281,7 +281,6 @@ impl<'a> Translation<'a> { fn can_struct_field_derive_debug(&self, ctype: CTypeId) -> bool { let ty = self.ast_context.resolve_type(ctype); let can_derive_debug = match ty.kind { - // Recurse into struct fields. A struct is debuggable iff all of its fields are CTypeKind::Struct(decl_id) => { let decl = self @@ -299,7 +298,8 @@ impl<'a> Translation<'a> { .unwrap(); match &field_decl.kind { CDeclKind::Field { typ, .. } => { - let can_derive_debug = self.can_struct_field_derive_debug(typ.ctype); + let can_derive_debug = + self.can_struct_field_derive_debug(typ.ctype); if !can_derive_debug { return false; } From 4637bb17b9a5c3e90e516848eff30fbce920ecce Mon Sep 17 00:00:00 2001 From: David Herzka Date: Fri, 3 Nov 2023 16:38:24 -0400 Subject: [PATCH 07/14] Add feature_c_variadic to test --- tests/structs/src/test_debug_derive_good.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/structs/src/test_debug_derive_good.rs b/tests/structs/src/test_debug_derive_good.rs index f453bba6ec..2c17864460 100644 --- a/tests/structs/src/test_debug_derive_good.rs +++ b/tests/structs/src/test_debug_derive_good.rs @@ -1,3 +1,5 @@ +//! feature_c_variadic, + use crate::debug_derive_good::{rust_kS1, rust_kS3}; pub fn test_simple_struct() { From 52ddae30cf763c7425c62163722aed73e58cc036 Mon Sep 17 00:00:00 2001 From: David Herzka Date: Fri, 3 Nov 2023 17:53:59 -0400 Subject: [PATCH 08/14] Work around unsafe static constructor for struct with va_list field --- tests/structs/src/debug_derive_good.c | 8 ++++++-- tests/structs/src/test_debug_derive_good.rs | 5 +++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/structs/src/debug_derive_good.c b/tests/structs/src/debug_derive_good.c index 8e802b84e3..f240e2130a 100644 --- a/tests/structs/src/debug_derive_good.c +++ b/tests/structs/src/debug_derive_good.c @@ -6,9 +6,13 @@ typedef struct { int a; } S1; +S1 kS1; + typedef struct { va_list v; } S3; -S1 kS1; -S3 kS3; \ No newline at end of file +S3 get_struct_containing_va_list() { + S3 s; + return s; +} \ No newline at end of file diff --git a/tests/structs/src/test_debug_derive_good.rs b/tests/structs/src/test_debug_derive_good.rs index 2c17864460..baefcb6783 100644 --- a/tests/structs/src/test_debug_derive_good.rs +++ b/tests/structs/src/test_debug_derive_good.rs @@ -1,6 +1,6 @@ //! feature_c_variadic, -use crate::debug_derive_good::{rust_kS1, rust_kS3}; +use crate::debug_derive_good::{rust_kS1, rust_get_struct_containing_va_list}; pub fn test_simple_struct() { unsafe { @@ -10,6 +10,7 @@ pub fn test_simple_struct() { pub fn test_struct_containing_va_list() { unsafe { - format!("{rust_kS3:?}"); + let s = rust_get_struct_containing_va_list(); + format!("{s:?}"); } } \ No newline at end of file From 670708d6c1ddfd72ef2736253e0e5c04ec75f963 Mon Sep 17 00:00:00 2001 From: David Herzka Date: Fri, 3 Nov 2023 19:17:24 -0400 Subject: [PATCH 09/14] Try to fix tests again --- tests/structs/src/debug_derive_bad.c | 4 ++-- tests/structs/src/debug_derive_good.c | 10 +++------- tests/structs/src/test_debug_derive_bad.rs | 4 ++-- tests/structs/src/test_debug_derive_good.rs | 17 ++++++----------- 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/tests/structs/src/debug_derive_bad.c b/tests/structs/src/debug_derive_bad.c index 32858b6294..65ea83ad67 100644 --- a/tests/structs/src/debug_derive_bad.c +++ b/tests/structs/src/debug_derive_bad.c @@ -10,6 +10,6 @@ typedef struct { } c; } b; } a; -} S2; +} StructWithUnion; -S2 kS2; \ No newline at end of file +StructWithUnion kStructWithUnion; \ No newline at end of file diff --git a/tests/structs/src/debug_derive_good.c b/tests/structs/src/debug_derive_good.c index f240e2130a..8c9bbb48a1 100644 --- a/tests/structs/src/debug_derive_good.c +++ b/tests/structs/src/debug_derive_good.c @@ -6,13 +6,9 @@ typedef struct { int a; } S1; -S1 kS1; - typedef struct { va_list v; -} S3; +} S2; -S3 get_struct_containing_va_list() { - S3 s; - return s; -} \ No newline at end of file +S1 *kS1; +S2 *kS2; diff --git a/tests/structs/src/test_debug_derive_bad.rs b/tests/structs/src/test_debug_derive_bad.rs index 8dbc19be66..bb09029fab 100644 --- a/tests/structs/src/test_debug_derive_bad.rs +++ b/tests/structs/src/test_debug_derive_bad.rs @@ -1,10 +1,10 @@ //! xfail mod debug_derive_bad; -use debug_derive_bad::rust_kS2; +use debug_derive_bad::rust_kStructWithUnion; pub fn test_union() { unsafe { - format!("{rust_kS2:?}"); + format!("{rust_kStructWithUnion:?}"); } } diff --git a/tests/structs/src/test_debug_derive_good.rs b/tests/structs/src/test_debug_derive_good.rs index baefcb6783..cdf558d69c 100644 --- a/tests/structs/src/test_debug_derive_good.rs +++ b/tests/structs/src/test_debug_derive_good.rs @@ -1,16 +1,11 @@ -//! feature_c_variadic, +//! feature_c_variadic -use crate::debug_derive_good::{rust_kS1, rust_get_struct_containing_va_list}; +use crate::debug_derive_good::{rust_kS1, rust_kS2}; +use std::fmt::Debug; -pub fn test_simple_struct() { +pub fn test_debuggable() { unsafe { - format!("{rust_kS1:?}"); + // Make sure each struct implements `Debug` + let _debuggable: Vec<*mut dyn Debug> = vec![rust_kS1, rust_kS2]; } } - -pub fn test_struct_containing_va_list() { - unsafe { - let s = rust_get_struct_containing_va_list(); - format!("{s:?}"); - } -} \ No newline at end of file From beca631998cac57b6a5283a3bd9a22862d505db4 Mon Sep 17 00:00:00 2001 From: David Herzka Date: Tue, 7 Nov 2023 11:17:19 -0500 Subject: [PATCH 10/14] Return struct fields in struct --- c2rust-transpile/src/translator/mod.rs | 13 +++++++++++-- c2rust-transpile/src/translator/structs.rs | 10 +++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 2be77db73c..c6c00d9321 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -1194,6 +1194,12 @@ struct ConvertedVariable { pub init: TranslationResult>>, } +pub struct ConvertedStructFields { + pub field_entries: Vec, + pub contains_va_list: bool, + pub can_derive_debug: bool, +} + impl<'c> Translation<'c> { pub fn new( mut ast_context: TypedAstContext, @@ -1628,8 +1634,11 @@ impl<'c> Translation<'c> { } // Gather up all the field names and field types - let (field_entries, contains_va_list, can_derive_debug) = - self.convert_struct_fields(decl_id, fields, platform_byte_size)?; + let ConvertedStructFields { + field_entries, + contains_va_list, + can_derive_debug, + } = self.convert_struct_fields(decl_id, fields, platform_byte_size)?; let mut derives = vec![]; if !contains_va_list { diff --git a/c2rust-transpile/src/translator/structs.rs b/c2rust-transpile/src/translator/structs.rs index e86ca822bc..b1832129d6 100644 --- a/c2rust-transpile/src/translator/structs.rs +++ b/c2rust-transpile/src/translator/structs.rs @@ -6,7 +6,7 @@ use std::collections::HashSet; use std::ops::Index; use super::named_references::NamedReference; -use super::TranslationError; +use super::{ConvertedStructFields, TranslationError}; use crate::c_ast::{BinOp, CDeclId, CDeclKind, CExprId, CRecordId, CTypeId, CTypeKind}; use crate::diagnostics::TranslationResult; use crate::translator::{ExprContext, Translation, PADDING_SUFFIX}; @@ -343,7 +343,7 @@ impl<'a> Translation<'a> { struct_id: CRecordId, field_ids: &[CDeclId], platform_byte_size: u64, - ) -> TranslationResult<(Vec, bool, bool)> { + ) -> TranslationResult { let mut field_entries = Vec::with_capacity(field_ids.len()); // We need to clobber bitfields in consecutive bytes together (leaving // regular fields alone) and add in padding as necessary @@ -434,7 +434,11 @@ impl<'a> Translation<'a> { } } } - Ok((field_entries, contains_va_list, can_derive_debug)) + Ok(ConvertedStructFields { + field_entries, + contains_va_list, + can_derive_debug, + }) } /// Here we output a block to generate a struct literal initializer in. From 501ec52df4142c6cb9665c92baf9551734caba20 Mon Sep 17 00:00:00 2001 From: David Herzka Date: Tue, 28 Nov 2023 19:29:26 +0100 Subject: [PATCH 11/14] Change CLI argument to --derive --- c2rust-transpile/src/lib.rs | 11 +++++- c2rust-transpile/src/translator/mod.rs | 50 ++++++++++++++++++-------- c2rust/src/bin/c2rust-transpile.rs | 39 +++++++++++++++++--- scripts/test_translator.py | 12 +++++-- tests/structs/src/debug_derive_bad.c | 2 +- tests/structs/src/debug_derive_good.c | 2 +- 6 files changed, 91 insertions(+), 25 deletions(-) diff --git a/c2rust-transpile/src/lib.rs b/c2rust-transpile/src/lib.rs index 9b67fa3680..7631f22ba8 100644 --- a/c2rust-transpile/src/lib.rs +++ b/c2rust-transpile/src/lib.rs @@ -25,6 +25,7 @@ use itertools::Itertools; use log::{info, warn}; use regex::Regex; use serde_derive::Serialize; +use strum_macros::Display; use crate::c_ast::Printer; use crate::c_ast::*; @@ -82,7 +83,7 @@ pub struct TranspilerConfig { pub disable_refactoring: bool, pub preserve_unused_functions: bool, pub log_level: log::LevelFilter, - pub derive_debug: bool, + pub derives: HashSet, // Options that control build files /// Emit `Cargo.toml` and `lib.rs` @@ -92,6 +93,14 @@ pub struct TranspilerConfig { pub binaries: Vec, } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Display)] +pub enum Derive { + Clone, + Copy, + Debug, + BitfieldStruct, +} + impl TranspilerConfig { fn binary_name_from_path(file: &Path) -> String { let file = Path::new(file.file_stem().unwrap()); diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index c6c00d9321..2391b665d2 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -11,6 +11,7 @@ use dtoa; use failure::{err_msg, format_err, Fail}; use indexmap::indexmap; use indexmap::{IndexMap, IndexSet}; +use itertools::Itertools; use log::{error, info, trace, warn}; use proc_macro2::{Punct, Spacing::*, Span, TokenStream, TokenTree}; use syn::spanned::Spanned as _; @@ -27,12 +28,12 @@ use c2rust_ast_builder::{mk, properties::*, Builder}; use c2rust_ast_printer::pprust::{self}; use crate::c_ast::iterators::{DFExpr, SomeId}; -use crate::c_ast::*; use crate::cfg; use crate::convert_type::TypeConverter; use crate::renamer::Renamer; use crate::with_stmts::WithStmts; use crate::{c_ast, format_translation_err}; +use crate::{c_ast::*, Derive}; use crate::{ExternCrate, ExternCrateDetails, TranspilerConfig}; use c2rust_ast_exporter::clang_ast::LRValue; @@ -1640,14 +1641,6 @@ impl<'c> Translation<'c> { can_derive_debug, } = self.convert_struct_fields(decl_id, fields, platform_byte_size)?; - let mut derives = vec![]; - if !contains_va_list { - derives.push("Copy"); - derives.push("Clone"); - }; - if self.tcfg.derive_debug && can_derive_debug { - derives.push("Debug"); - } let has_bitfields = fields .iter() @@ -1656,10 +1649,27 @@ impl<'c> Translation<'c> { _ => unreachable!("Found non-field in record field list"), }); if has_bitfields { - derives.push("BitfieldStruct"); self.use_crate(ExternCrate::C2RustBitfields); } + let derives = self + .tcfg + .derives + .iter() + .flat_map(|derive| { + let can_derive = match derive { + Derive::Clone | Derive::Copy => !contains_va_list, + Derive::Debug => can_derive_debug, + Derive::BitfieldStruct => has_bitfields, + }; + if can_derive { + Some(derive.to_string()) + } else { + None + } + }) + .collect_vec(); + let mut reprs = vec![simple_metaitem("C")]; let max_field_alignment = if is_packed { // `__attribute__((packed))` forces a max alignment of 1, @@ -1710,10 +1720,22 @@ impl<'c> Translation<'c> { let repr_attr = mk().meta_list("repr", outer_reprs); let outer_field = mk().pub_().enum_field(mk().ident_ty(inner_name)); - let mut outer_struct_derives = vec!["Copy", "Clone"]; - if self.tcfg.derive_debug { - outer_struct_derives.push("Debug"); - } + let outer_struct_derives = self + .tcfg + .derives + .iter() + .flat_map(|derive| { + let can_derive = match derive { + Derive::Clone | Derive::Copy | Derive::Debug => true, + Derive::BitfieldStruct => false, + }; + if can_derive { + Some(derive.to_string()) + } else { + None + } + }) + .collect_vec(); let outer_struct = mk() .span(span) diff --git a/c2rust/src/bin/c2rust-transpile.rs b/c2rust/src/bin/c2rust-transpile.rs index 1baa6f2131..1bd7b955e3 100644 --- a/c2rust/src/bin/c2rust-transpile.rs +++ b/c2rust/src/bin/c2rust-transpile.rs @@ -3,7 +3,9 @@ use log::LevelFilter; use regex::Regex; use std::{fs, path::PathBuf}; -use c2rust_transpile::{Diagnostic, ReplaceMode, TranspilerConfig}; +use c2rust_transpile::{Derive, Diagnostic, ReplaceMode, TranspilerConfig}; + +const DEFAULT_DERIVES: &[Derive] = &[Derive::Clone, Derive::Copy, Derive::BitfieldStruct]; #[derive(Debug, Parser)] #[clap( @@ -156,9 +158,16 @@ struct Args { #[clap(long)] fail_on_multiple: bool, - /// Derive `Debug` trait for any structs that allow it (i.e., do not recursively contain any unions) - #[clap(long)] - derive_debug: bool, + /// Add extra derived traits to generated structs in addition to the default + /// set of derives (Copy, Clone, BitfieldStruct). Specify multiple times to + /// add more than one derive. A struct will derive all traits in the set for + /// which it is eligible. + /// + /// For example, a struct containing a union cannot derive Debug, so + /// `#[derive(Debug)]` will not be added to that struct regardless of + /// whether `--derive Debug` is specified. + #[clap(long = "derive", value_enum, value_name = "TRAIT")] + extra_derives: Vec, } #[derive(Debug, PartialEq, Eq, ValueEnum, Clone)] @@ -168,9 +177,29 @@ enum InvalidCodes { CompileError, } +#[derive(Clone, Copy, Debug, ValueEnum)] +#[clap(rename_all = "PascalCase")] +enum ExtraDerive { + Debug, +} + +impl ExtraDerive { + fn to_transpiler_derive(&self) -> Derive { + match self { + Self::Debug => Derive::Debug, + } + } +} + fn main() { let args = Args::parse(); + let derives = DEFAULT_DERIVES + .iter() + .cloned() + .chain(args.extra_derives.iter().map(|d| d.to_transpiler_derive())) + .collect(); + // Build a TranspilerConfig from the command line let mut tcfg = TranspilerConfig { dump_untyped_context: args.dump_untyped_clang_ast, @@ -220,7 +249,7 @@ fn main() { emit_no_std: args.emit_no_std, enabled_warnings: args.warn.into_iter().collect(), log_level: args.log_level, - derive_debug: args.derive_debug, + derives, }; // binaries imply emit-build-files if !tcfg.binaries.is_empty() { diff --git a/scripts/test_translator.py b/scripts/test_translator.py index 6d71ad5b48..6c880457e1 100755 --- a/scripts/test_translator.py +++ b/scripts/test_translator.py @@ -72,7 +72,12 @@ def __init__(self, log_level: str, path: str, flags: Set[str] = set()) -> None: self.translate_const_macros = "translate_const_macros" in flags self.reorganize_definitions = "reorganize_definitions" in flags self.emit_build_files = "emit_build_files" in flags - self.derive_debug = "derive_debug" in flags + + derive_flag_prefix = "derive:" + derives = set() + for derive_flag in filter(lambda f: f.startswith(derive_flag_prefix), flags): + derives.add(derive_flag.removeprefix(derive_flag_prefix)) + self.derives = derives def translate(self, cc_db: str, ld_lib_path: str, extra_args: List[str] = []) -> RustFile: extensionless_file, _ = os.path.splitext(self.path) @@ -97,8 +102,9 @@ def translate(self, cc_db: str, ld_lib_path: str, extra_args: List[str] = []) -> args.append("--reorganize-definitions") if self.emit_build_files: args.append("--emit-build-files") - if self.derive_debug: - args.append("--derive-debug") + + for derive in self.derives: + args.extend(["--derive", derive]) if self.log_level == 'DEBUG': args.append("--log-level=debug") diff --git a/tests/structs/src/debug_derive_bad.c b/tests/structs/src/debug_derive_bad.c index 65ea83ad67..fa62720424 100644 --- a/tests/structs/src/debug_derive_bad.c +++ b/tests/structs/src/debug_derive_bad.c @@ -1,4 +1,4 @@ -//! derive_debug +//! derive:Debug // A struct containing a union is not debuggable typedef struct { diff --git a/tests/structs/src/debug_derive_good.c b/tests/structs/src/debug_derive_good.c index 8c9bbb48a1..bdbf71ea03 100644 --- a/tests/structs/src/debug_derive_good.c +++ b/tests/structs/src/debug_derive_good.c @@ -1,4 +1,4 @@ -//! derive_debug +//! derive:Debug #include From ceb8c2836702af2f4c5679109b6de5d49ffcff97 Mon Sep 17 00:00:00 2001 From: David Herzka Date: Tue, 28 Nov 2023 19:36:40 +0100 Subject: [PATCH 12/14] Make tests actually make sense --- tests/structs/src/debug_derive.c | 29 +++++++++++++++++++++ tests/structs/src/debug_derive_bad.c | 15 ----------- tests/structs/src/debug_derive_good.c | 14 ---------- tests/structs/src/test_debug_derive.rs | 11 ++++++++ tests/structs/src/test_debug_derive_bad.rs | 10 ------- tests/structs/src/test_debug_derive_good.rs | 11 -------- 6 files changed, 40 insertions(+), 50 deletions(-) create mode 100644 tests/structs/src/debug_derive.c delete mode 100644 tests/structs/src/debug_derive_bad.c delete mode 100644 tests/structs/src/debug_derive_good.c create mode 100644 tests/structs/src/test_debug_derive.rs delete mode 100644 tests/structs/src/test_debug_derive_bad.rs delete mode 100644 tests/structs/src/test_debug_derive_good.rs diff --git a/tests/structs/src/debug_derive.c b/tests/structs/src/debug_derive.c new file mode 100644 index 0000000000..562b20810e --- /dev/null +++ b/tests/structs/src/debug_derive.c @@ -0,0 +1,29 @@ +//! derive:Debug + +#include + +typedef struct { + int a; +} Debuggable1; + +typedef struct { + va_list v; +} Debuggable2; + +Debuggable1 *kDebuggable1; +Debuggable2 *kDebuggable2; + +// A struct containing a union cannot derive Debug, so make +// sure we don't generate a #[derive] for it +typedef struct { + struct { + struct { + union { + int d; + float e; + } c; + } b; + } a; +} NotDebuggable1; + +NotDebuggable1 kNotDebuggable1; diff --git a/tests/structs/src/debug_derive_bad.c b/tests/structs/src/debug_derive_bad.c deleted file mode 100644 index fa62720424..0000000000 --- a/tests/structs/src/debug_derive_bad.c +++ /dev/null @@ -1,15 +0,0 @@ -//! derive:Debug - -// A struct containing a union is not debuggable -typedef struct { - struct { - struct { - union { - int d; - float e; - } c; - } b; - } a; -} StructWithUnion; - -StructWithUnion kStructWithUnion; \ No newline at end of file diff --git a/tests/structs/src/debug_derive_good.c b/tests/structs/src/debug_derive_good.c deleted file mode 100644 index bdbf71ea03..0000000000 --- a/tests/structs/src/debug_derive_good.c +++ /dev/null @@ -1,14 +0,0 @@ -//! derive:Debug - -#include - -typedef struct { - int a; -} S1; - -typedef struct { - va_list v; -} S2; - -S1 *kS1; -S2 *kS2; diff --git a/tests/structs/src/test_debug_derive.rs b/tests/structs/src/test_debug_derive.rs new file mode 100644 index 0000000000..bd5b7a1a3e --- /dev/null +++ b/tests/structs/src/test_debug_derive.rs @@ -0,0 +1,11 @@ +//! feature_c_variadic + +use crate::debug_derive::{rust_kDebuggable1, rust_kDebuggable2}; +use std::fmt::Debug; + +pub fn test_debuggable() { + unsafe { + // Make sure all debuggable structs implement `Debug` + let _debuggable: Vec<*mut dyn Debug> = vec![rust_kDebuggable1, rust_kDebuggable2]; + } +} diff --git a/tests/structs/src/test_debug_derive_bad.rs b/tests/structs/src/test_debug_derive_bad.rs deleted file mode 100644 index bb09029fab..0000000000 --- a/tests/structs/src/test_debug_derive_bad.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! xfail - -mod debug_derive_bad; -use debug_derive_bad::rust_kStructWithUnion; - -pub fn test_union() { - unsafe { - format!("{rust_kStructWithUnion:?}"); - } -} diff --git a/tests/structs/src/test_debug_derive_good.rs b/tests/structs/src/test_debug_derive_good.rs deleted file mode 100644 index cdf558d69c..0000000000 --- a/tests/structs/src/test_debug_derive_good.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! feature_c_variadic - -use crate::debug_derive_good::{rust_kS1, rust_kS2}; -use std::fmt::Debug; - -pub fn test_debuggable() { - unsafe { - // Make sure each struct implements `Debug` - let _debuggable: Vec<*mut dyn Debug> = vec![rust_kS1, rust_kS2]; - } -} From 3b3e1c094fa42446449548bd4faccb469db27f0d Mon Sep 17 00:00:00 2001 From: David Herzka Date: Wed, 29 Nov 2023 13:30:37 -0500 Subject: [PATCH 13/14] Don't use removeprefix --- scripts/test_translator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test_translator.py b/scripts/test_translator.py index 6c880457e1..ca2d1925e3 100755 --- a/scripts/test_translator.py +++ b/scripts/test_translator.py @@ -76,7 +76,7 @@ def __init__(self, log_level: str, path: str, flags: Set[str] = set()) -> None: derive_flag_prefix = "derive:" derives = set() for derive_flag in filter(lambda f: f.startswith(derive_flag_prefix), flags): - derives.add(derive_flag.removeprefix(derive_flag_prefix)) + derives.add(derive_flag[len(derive_flag_prefix):]) self.derives = derives def translate(self, cc_db: str, ld_lib_path: str, extra_args: List[str] = []) -> RustFile: From 191e2f384a1fe45ffe1d42e3caea33b9d2dd7917 Mon Sep 17 00:00:00 2001 From: David Herzka Date: Thu, 14 Dec 2023 13:14:20 -0500 Subject: [PATCH 14/14] Make derive order stable --- c2rust-transpile/src/lib.rs | 4 ++-- c2rust/src/bin/c2rust-transpile.rs | 22 ++++++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/c2rust-transpile/src/lib.rs b/c2rust-transpile/src/lib.rs index 7631f22ba8..1a22899eda 100644 --- a/c2rust-transpile/src/lib.rs +++ b/c2rust-transpile/src/lib.rs @@ -83,7 +83,7 @@ pub struct TranspilerConfig { pub disable_refactoring: bool, pub preserve_unused_functions: bool, pub log_level: log::LevelFilter, - pub derives: HashSet, + pub derives: Vec, // Options that control build files /// Emit `Cargo.toml` and `lib.rs` @@ -93,7 +93,7 @@ pub struct TranspilerConfig { pub binaries: Vec, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Display)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Display, PartialOrd, Ord)] pub enum Derive { Clone, Copy, diff --git a/c2rust/src/bin/c2rust-transpile.rs b/c2rust/src/bin/c2rust-transpile.rs index 1bd7b955e3..4dd28641eb 100644 --- a/c2rust/src/bin/c2rust-transpile.rs +++ b/c2rust/src/bin/c2rust-transpile.rs @@ -1,7 +1,7 @@ use clap::{Parser, ValueEnum}; use log::LevelFilter; use regex::Regex; -use std::{fs, path::PathBuf}; +use std::{fs, path::PathBuf, collections::HashSet}; use c2rust_transpile::{Derive, Diagnostic, ReplaceMode, TranspilerConfig}; @@ -194,12 +194,6 @@ impl ExtraDerive { fn main() { let args = Args::parse(); - let derives = DEFAULT_DERIVES - .iter() - .cloned() - .chain(args.extra_derives.iter().map(|d| d.to_transpiler_derive())) - .collect(); - // Build a TranspilerConfig from the command line let mut tcfg = TranspilerConfig { dump_untyped_context: args.dump_untyped_clang_ast, @@ -249,7 +243,7 @@ fn main() { emit_no_std: args.emit_no_std, enabled_warnings: args.warn.into_iter().collect(), log_level: args.log_level, - derives, + derives: get_derives(&args.extra_derives), }; // binaries imply emit-build-files if !tcfg.binaries.is_empty() { @@ -295,3 +289,15 @@ fn main() { .expect("Failed to remove temporary compile_commands.json"); } } + +fn get_derives(extra_derives: &[ExtraDerive]) -> Vec { + // Make sure there are no dupes and sort so the derives are always in the same order + let derives_set: HashSet<_> = DEFAULT_DERIVES + .iter() + .cloned() + .chain(extra_derives.iter().map(|d| d.to_transpiler_derive())) + .collect(); + let mut derives: Vec = derives_set.into_iter().collect(); + derives.sort(); + derives +}