Skip to content

Commit f4be910

Browse files
authored
transpile: defer+cache imports alongside zero initializers (#1455)
I think this is the right way to fix this. I tried adding a `WithStmtsAndImports` type instead, to avoid larger shared state, but it ended up infecting almost the entire transpiler.
2 parents 394ecb5 + 5f61068 commit f4be910

File tree

3 files changed

+139
-47
lines changed

3 files changed

+139
-47
lines changed

c2rust-transpile/src/translator/literals.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@ impl<'c> Translation<'c> {
4949
let name = self.renamer.borrow().get(&variant_id).unwrap();
5050

5151
// Import the enum variant if needed
52-
if let Some(cur_file) = self.cur_file.get() {
53-
self.add_import(cur_file, variant_id, &name);
54-
}
52+
self.add_import(variant_id, &name);
5553
return mk().path_expr(vec![name]);
5654
}
5755
}
@@ -334,10 +332,8 @@ impl<'c> Translation<'c> {
334332
.borrow()
335333
.resolve_decl_name(union_id)
336334
.unwrap();
337-
if let Some(cur_file) = self.cur_file.get() {
338-
log::debug!("in file {cur_file} importing union {union_name}, id {union_id:?}");
339-
self.add_import(cur_file, union_id, &union_name);
340-
}
335+
log::debug!("importing union {union_name}, id {union_id:?}");
336+
self.add_import(union_id, &union_name);
341337
match self.ast_context.index(union_field_id).kind {
342338
CDeclKind::Field { typ: field_ty, .. } => {
343339
let val = if ids.is_empty() {

c2rust-transpile/src/translator/mod.rs

Lines changed: 134 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ use crate::PragmaVec;
6060
pub const INNER_SUFFIX: &str = "_Inner";
6161
pub const PADDING_SUFFIX: &str = "_PADDING";
6262

63+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
64+
struct Import {
65+
decl_id: CDeclId,
66+
ident_name: String,
67+
}
68+
6369
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
6470
pub enum DecayRef {
6571
Yes,
@@ -261,10 +267,15 @@ pub struct Translation<'c> {
261267
// Translation state and utilities
262268
type_converter: RefCell<TypeConverter>,
263269
renamer: RefCell<Renamer<CDeclId>>,
264-
zero_inits: RefCell<IndexMap<CDeclId, WithStmts<Box<Expr>>>>,
270+
zero_inits: RefCell<IndexMap<CDeclId, (WithStmts<Box<Expr>>, IndexSet<Import>)>>,
265271
function_context: RefCell<FuncContext>,
266272
potential_flexible_array_members: RefCell<IndexSet<CDeclId>>,
267273
macro_expansions: RefCell<IndexMap<CDeclId, Option<MacroExpansion>>>,
274+
/// Sets of imports deferred while translating nested expressions for caching. Imports are
275+
/// deferred when caching translations to make them pure and thus cache the translation
276+
/// alongside its required imports. Each additional nested level of caching translation
277+
/// causes an additional set to be pushed onto the `deferred_imports` vector.
278+
deferred_imports: RefCell<Vec<IndexSet<Import>>>,
268279

269280
// Comment support
270281
pub comment_context: CommentContext, // Incoming comments
@@ -1299,6 +1310,7 @@ impl<'c> Translation<'c> {
12991310
function_context: RefCell::new(FuncContext::new()),
13001311
potential_flexible_array_members: RefCell::new(IndexSet::new()),
13011312
macro_expansions: RefCell::new(IndexMap::new()),
1313+
deferred_imports: RefCell::new(Vec::new()),
13021314
comment_context,
13031315
comment_store: RefCell::new(CommentStore::new()),
13041316
spans: HashMap::new(),
@@ -1877,9 +1889,8 @@ impl<'c> Translation<'c> {
18771889
.borrow()
18781890
.resolve_decl_name(enum_id)
18791891
.expect("Enums should already be renamed");
1880-
if let Some(cur_file) = self.cur_file.get() {
1881-
self.add_import(cur_file, enum_id, &enum_name);
1882-
}
1892+
self.add_import(enum_id, &enum_name);
1893+
18831894
let ty = mk().path_ty(vec![enum_name]);
18841895
let val = match value {
18851896
ConstIntExpr::I(value) => signed_int_expr(value),
@@ -3053,9 +3064,8 @@ impl<'c> Translation<'c> {
30533064
}
30543065

30553066
fn convert_type(&self, type_id: CTypeId) -> TranslationResult<Box<Type>> {
3056-
if let Some(cur_file) = self.cur_file.get() {
3057-
self.import_type(type_id, cur_file);
3058-
}
3067+
self.import_type(type_id);
3068+
30593069
self.type_converter
30603070
.borrow_mut()
30613071
.convert(&self.ast_context, type_id)
@@ -3476,13 +3486,11 @@ impl<'c> Translation<'c> {
34763486

34773487
// Import the referenced global decl into our submodule
34783488
if self.tcfg.reorganize_definitions {
3479-
if let Some(cur_file) = self.cur_file.get() {
3480-
self.add_import(cur_file, decl_id, &rustname);
3481-
// match decl {
3482-
// CDeclKind::Variable { is_defn: false, .. } => {}
3483-
// _ => self.add_import(cur_file, decl_id, &rustname),
3484-
// }
3485-
}
3489+
self.add_import(decl_id, &rustname);
3490+
// match decl {
3491+
// CDeclKind::Variable { is_defn: false, .. } => {}
3492+
// _ => self.add_import(decl_id, &rustname),
3493+
// }
34863494
}
34873495

34883496
let mut val = mk().path_expr(vec![rustname]);
@@ -3524,9 +3532,8 @@ impl<'c> Translation<'c> {
35243532
if let Some(actual_ty) = actual_ty {
35253533
// If we're casting a concrete function to
35263534
// a K&R function pointer type, use transmute
3527-
if let Some(cur_file) = self.cur_file.get() {
3528-
self.import_type(qual_ty.ctype, cur_file);
3529-
}
3535+
self.import_type(qual_ty.ctype);
3536+
35303537
val = transmute_expr(actual_ty, ty, val);
35313538
set_unsafe = true;
35323539
} else {
@@ -4306,9 +4313,7 @@ impl<'c> Translation<'c> {
43064313
.get(macro_id)
43074314
.ok_or_else(|| format_err!("Macro name not declared"))?;
43084315

4309-
if let Some(cur_file) = self.cur_file.get() {
4310-
self.add_import(cur_file, *macro_id, &rust_name);
4311-
}
4316+
self.add_import(*macro_id, &rust_name);
43124317

43134318
let val = WithStmts::new_val(mk().path_expr(vec![rust_name]));
43144319

@@ -4993,15 +4998,23 @@ impl<'c> Translation<'c> {
49934998
type_id: CTypeId,
49944999
is_static: bool,
49955000
) -> TranslationResult<WithStmts<Box<Expr>>> {
4996-
if let Some(file_id) = self.cur_file.get() {
4997-
self.import_type(type_id, file_id);
4998-
}
5001+
self.import_type(type_id);
5002+
5003+
let name = self.type_converter.borrow().resolve_decl_name(decl_id);
5004+
let name = name.as_deref().unwrap_or("???");
49995005

50005006
// Look up the decl in the cache and return what we find (if we find anything)
5001-
if let Some(init) = self.zero_inits.borrow().get(&decl_id) {
5007+
if let Some((init, imports)) = self.zero_inits.borrow().get(&decl_id) {
5008+
self.add_imports(imports);
5009+
log::debug!("looked up imports for {name}: {imports:?}");
50025010
return Ok(init.clone());
50035011
}
50045012

5013+
let func_name = &self.function_context.borrow().name;
5014+
let func_name = func_name.as_deref().unwrap_or("<none>");
5015+
log::debug!("deferring imports to save them for {name} in {func_name}");
5016+
self.defer_imports();
5017+
50055018
let name_decl_id = match self.ast_context.index(type_id).kind {
50065019
CTypeKind::Typedef(decl_id) => decl_id,
50075020
_ => decl_id,
@@ -5099,7 +5112,11 @@ impl<'c> Translation<'c> {
50995112

51005113
if init.is_pure() {
51015114
// Insert the initializer into the cache, then return it
5102-
self.zero_inits.borrow_mut().insert(decl_id, init.clone());
5115+
let imports = self.stop_deferring_imports();
5116+
log::debug!("saving imports for {name}: {imports:?}");
5117+
self.zero_inits
5118+
.borrow_mut()
5119+
.insert(decl_id, (init.clone(), imports));
51035120
Ok(init)
51045121
} else {
51055122
Err(TranslationError::generic(
@@ -5259,7 +5276,14 @@ impl<'c> Translation<'c> {
52595276
}
52605277
}
52615278

5262-
fn add_import(&self, decl_file_id: FileId, decl_id: CDeclId, ident_name: &str) {
5279+
/// Directly add a top-level `use` for the given decl.
5280+
///
5281+
/// Should not be directly called from any recursive translation steps. Instead,
5282+
/// call `add_import`, which supports deferring imports to allow caching translation
5283+
/// of subexpressions while tracking the requisite imports.
5284+
fn add_use_item(&self, decl_file_id: FileId, decl_id: CDeclId, ident_name: &str) {
5285+
assert!(!self.deferring_imports());
5286+
52635287
let decl = &self.ast_context[decl_id];
52645288
let import_file_id = self.ast_context.file_id(decl);
52655289

@@ -5290,9 +5314,69 @@ impl<'c> Translation<'c> {
52905314
.add_use(false, module_path, ident_name);
52915315
}
52925316

5293-
fn import_type(&self, ctype: CTypeId, decl_file_id: FileId) {
5317+
/// Mark an import as required by the translated code.
5318+
/// `decl_id` is the definition to import
5319+
fn add_import(&self, decl_id: CDeclId, ident_name: &str) {
5320+
// If deferring imports, store and return.
5321+
if self.deferring_imports() {
5322+
self.deferred_imports
5323+
.borrow_mut()
5324+
.last_mut()
5325+
.unwrap()
5326+
.insert(Import {
5327+
decl_id,
5328+
ident_name: ident_name.into(),
5329+
});
5330+
} else {
5331+
self.add_use_item(self.cur_file(), decl_id, ident_name)
5332+
}
5333+
}
5334+
5335+
fn add_imports(&self, imports: &IndexSet<Import>) {
5336+
for Import {
5337+
decl_id,
5338+
ident_name,
5339+
} in imports
5340+
{
5341+
self.add_import(*decl_id, &ident_name)
5342+
}
5343+
}
5344+
5345+
/// Delay the addition of imports until [`Self::stop_deferring_imports`] is called.
5346+
fn defer_imports(&self) {
5347+
self.deferred_imports.borrow_mut().push(Default::default())
5348+
}
5349+
5350+
/// Return whether imports are currently being deferred for later emission.
5351+
fn deferring_imports(&self) -> bool {
5352+
!self.deferred_imports.borrow().is_empty()
5353+
}
5354+
5355+
/// Apply and clear the last set of deferred imports. Returns the imports applied.
5356+
/// Must be called to match a previous call to [`Self::defer_imports`]; will panic
5357+
/// otherwise.
5358+
fn stop_deferring_imports(&self) -> IndexSet<Import> {
5359+
let mut deferred = self
5360+
.deferred_imports
5361+
.borrow_mut()
5362+
.pop()
5363+
.expect("`stop_deferring_imports` called without matching call to `defer_imports`");
5364+
for imports in self.deferred_imports.borrow().iter() {
5365+
deferred.extend(imports.clone());
5366+
}
5367+
self.add_imports(&deferred);
5368+
deferred
5369+
}
5370+
5371+
fn import_type(&self, ctype: CTypeId) {
5372+
self.add_imports(&self.imports_for_type(ctype))
5373+
}
5374+
5375+
fn imports_for_type(&self, ctype: CTypeId) -> IndexSet<Import> {
52945376
use self::CTypeKind::*;
52955377

5378+
let mut imports = IndexSet::default();
5379+
52965380
let type_kind = &self.ast_context[ctype].kind;
52975381
match type_kind {
52985382
// libc can be accessed from anywhere as of Rust 2019 by full path
@@ -5317,33 +5401,36 @@ impl<'c> Translation<'c> {
53175401
| Reference(CQualTypeId { ctype, .. })
53185402
| BlockPointer(CQualTypeId { ctype, .. })
53195403
| TypeOf(ctype)
5320-
| Complex(ctype) => self.import_type(*ctype, decl_file_id),
5404+
| Complex(ctype) => imports.extend(self.imports_for_type(*ctype)),
53215405
Enum(decl_id) | Typedef(decl_id) | Union(decl_id) | Struct(decl_id) => {
53225406
let mut decl_id = *decl_id;
53235407
// if the `decl` has been "squashed", get the corresponding `decl_id`
53245408
if self.ast_context.prenamed_decls.contains_key(&decl_id) {
53255409
decl_id = *self.ast_context.prenamed_decls.get(&decl_id).unwrap();
53265410
}
53275411

5328-
let ident_name = &self
5412+
let ident_name = self
53295413
.type_converter
53305414
.borrow()
53315415
.resolve_decl_name(decl_id)
53325416
.expect("Expected decl name");
5333-
self.add_import(decl_file_id, decl_id, ident_name);
5417+
imports.insert(Import {
5418+
decl_id,
5419+
ident_name,
5420+
});
53345421
}
53355422
Function(CQualTypeId { ctype, .. }, ref params, ..) => {
53365423
// Return Type
53375424
let type_kind = &self.ast_context[*ctype].kind;
53385425

53395426
// Rust doesn't use void for return type, so skip
53405427
if *type_kind != Void {
5341-
self.import_type(*ctype, decl_file_id);
5428+
imports.extend(self.imports_for_type(*ctype));
53425429
}
53435430

53445431
// Param Types
53455432
for param_id in params {
5346-
self.import_type(param_id.ctype, decl_file_id);
5433+
imports.extend(self.imports_for_type(param_id.ctype))
53475434
}
53485435
}
53495436
Vector(..) => {
@@ -5354,19 +5441,30 @@ impl<'c> Translation<'c> {
53545441
// TODO: handle SVE types
53555442
}
53565443
}
5444+
imports
53575445
}
53585446

53595447
fn generate_submodule_imports(&self, decl_id: CDeclId, decl_file_id: Option<FileId>) {
53605448
let decl_file_id = decl_file_id.expect("There should be a decl file path");
53615449
let decl = self.ast_context.get_decl(&decl_id).unwrap();
53625450

5451+
let add_use_items_for_type = |ctype| {
5452+
for Import {
5453+
decl_id,
5454+
ident_name,
5455+
} in self.imports_for_type(ctype)
5456+
{
5457+
self.add_use_item(decl_file_id, decl_id, &ident_name)
5458+
}
5459+
};
5460+
53635461
match decl.kind {
53645462
CDeclKind::Struct { ref fields, .. } | CDeclKind::Union { ref fields, .. } => {
53655463
let field_ids = fields.as_ref().map(|vec| vec.as_slice()).unwrap_or(&[]);
53665464

53675465
for field_id in field_ids.iter() {
53685466
match self.ast_context[*field_id].kind {
5369-
CDeclKind::Field { typ, .. } => self.import_type(typ.ctype, decl_file_id),
5467+
CDeclKind::Field { typ, .. } => add_use_items_for_type(typ.ctype),
53705468
_ => unreachable!("Found something in a struct other than a field"),
53715469
}
53725470
}
@@ -5385,16 +5483,16 @@ impl<'c> Translation<'c> {
53855483
typ,
53865484
..
53875485
}
5388-
| CDeclKind::Typedef { typ, .. } => self.import_type(typ.ctype, decl_file_id),
5486+
| CDeclKind::Typedef { typ, .. } => add_use_items_for_type(typ.ctype),
53895487
CDeclKind::Function {
53905488
is_global: true,
53915489
typ,
53925490
..
5393-
} => self.import_type(typ, decl_file_id),
5491+
} => add_use_items_for_type(typ),
53945492

53955493
CDeclKind::MacroObject { .. } => {
53965494
if let Some(Some(expansion)) = self.macro_expansions.borrow().get(&decl_id) {
5397-
self.import_type(expansion.ty, decl_file_id)
5495+
add_use_items_for_type(expansion.ty)
53985496
}
53995497
}
54005498

c2rust-transpile/src/translator/structs.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -398,10 +398,8 @@ impl<'a> Translation<'a> {
398398
field_expr_ids: &[CExprId],
399399
) -> TranslationResult<WithStmts<Box<Expr>>> {
400400
let name = self.resolve_decl_inner_name(struct_id);
401-
if let Some(cur_file) = self.cur_file.get() {
402-
log::debug!("in file {cur_file} importing struct {name}, id {struct_id:?}");
403-
self.add_import(cur_file, struct_id, &name);
404-
}
401+
log::debug!("importing struct {name}, id {struct_id:?}");
402+
self.add_import(struct_id, &name);
405403

406404
let (field_decl_ids, platform_byte_size) = match self.ast_context.index(struct_id).kind {
407405
CDeclKind::Struct {

0 commit comments

Comments
 (0)