diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 82d956693e..809f7a7710 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -4174,8 +4174,6 @@ impl<'c> Translation<'c> { decl_id: CDeclId, type_id: CTypeId, ) -> TranslationResult>> { - self.import_type(type_id); - let name = self.type_converter.borrow().resolve_decl_name(decl_id); let name = name.as_deref().unwrap_or("???"); @@ -4190,6 +4188,7 @@ impl<'c> Translation<'c> { let func_name = func_name.as_deref().unwrap_or(""); log::debug!("deferring imports to save them for {name} in {func_name}"); self.defer_imports(); + self.import_type(type_id); let name_decl_id = match self.ast_context.resolve_type_no_typedef(type_id).kind { CTypeKind::Typedef(decl_id) => decl_id, diff --git a/c2rust-transpile/tests/snapshots.rs b/c2rust-transpile/tests/snapshots.rs index a04b302d85..350ec1ffd2 100644 --- a/c2rust-transpile/tests/snapshots.rs +++ b/c2rust-transpile/tests/snapshots.rs @@ -617,6 +617,21 @@ fn test_wide_strings() { .run(); } +#[test] +fn test_zero_init_typedef_reorg_imports() { + let c_path = Path::new("tests/snapshots/zero_init_typedef_reorg.c"); + let mut cfg = config(Edition2021); + cfg.reorganize_definitions = true; + cfg.disable_refactoring = true; + compile_and_transpile_file(c_path, cfg); + + let rs_path = c_path.with_extension("rs"); + rustc(&rs_path) + .edition(Edition2021) + .crate_name("zero_init_typedef_reorg") + .run(); +} + // arch-os-specific #[test] diff --git a/c2rust-transpile/tests/snapshots/zero_init_typedef_reorg.c b/c2rust-transpile/tests/snapshots/zero_init_typedef_reorg.c new file mode 100644 index 0000000000..7495b2f21e --- /dev/null +++ b/c2rust-transpile/tests/snapshots/zero_init_typedef_reorg.c @@ -0,0 +1,25 @@ +typedef struct Stat { + int mode; + long size; +} STAT_T; + +/* The implicit zero-initializer for the `st` field is the first one created + * for `struct Stat`, so it populates the cache. Because the field is spelled + * with the typedef, the cached expression is rendered as `STAT_T { ... }`. */ +struct OuterMain { + int x; + STAT_T st; +}; + +static void before_include(void) { + struct OuterMain o = {0}; + (void)o; +} + +#include "zero_init_typedef_reorg.h" + +int main(void) { + before_include(); + after_include(); + return 0; +} diff --git a/c2rust-transpile/tests/snapshots/zero_init_typedef_reorg.h b/c2rust-transpile/tests/snapshots/zero_init_typedef_reorg.h new file mode 100644 index 0000000000..0b7a133753 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/zero_init_typedef_reorg.h @@ -0,0 +1,14 @@ +/* Zero-initializing this struct reuses the cached `STAT_T { ... }` + * initializer for the `st` field, even though the field is spelled with the + * struct tag. The `use STAT_T` this header module needs must therefore be + * replayed from the imports cached alongside the initializer; the imports + * derived from the type of the hit site only cover `Stat`. */ +struct OuterHeader { + int y; + struct Stat st; +}; + +static void after_include(void) { + struct OuterHeader o = {0}; + (void)o; +}