@@ -60,6 +60,12 @@ use crate::PragmaVec;
6060pub const INNER_SUFFIX : & str = "_Inner" ;
6161pub 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 ) ]
6470pub 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
0 commit comments