@@ -78,7 +78,11 @@ impl ProgramReconstructor for TransformVisitor<'_> {
7878 }
7979 }
8080
81- // This is a sanity check to ensure that functions in the program scope have been processed.
81+ // Drop any library functions that were not reachable from this program (dead library code).
82+ // They are not needed and would fail the sanity check below.
83+ self . function_map . retain ( |loc, _| !self . state . symbol_table . is_library ( loc. program ) ) ;
84+
85+ // This is a sanity check to ensure that all non-library functions in the program scope have been processed.
8286 assert ! ( self . function_map. is_empty( ) , "All functions in the program should have been processed." ) ;
8387
8488 // Reconstruct the constructor.
@@ -87,11 +91,15 @@ impl ProgramReconstructor for TransformVisitor<'_> {
8791
8892 // Note that this intentionally clears `self.reconstructed_functions` for the next program scope.
8993 let functions = core:: mem:: take ( & mut self . reconstructed_functions )
90- . iter ( )
94+ . into_iter ( )
9195 . filter_map ( |( loc, f) | {
92- // Only consider functions defined at program scope. The rest are not relevant since they should all
93- // have been inlined by now.
94- loc. path . split_last ( ) . filter ( |( _, rest) | rest. is_empty ( ) ) . map ( |( last, _) | ( * last, f. clone ( ) ) )
96+ // Only emit functions that belong to the current program scope and have a single-segment
97+ // path (no module prefix). Library functions and module-nested functions have all been
98+ // inlined at their call sites and must not appear as standalone functions in output.
99+ if loc. program != self . program {
100+ return None ;
101+ }
102+ loc. path . split_last ( ) . filter ( |( _, rest) | rest. is_empty ( ) ) . map ( |( last, _) | ( * last, f) )
95103 } )
96104 . collect ( ) ;
97105
@@ -150,8 +158,8 @@ impl ProgramReconstructor for TransformVisitor<'_> {
150158 }
151159
152160 fn reconstruct_program ( & mut self , input : Program ) -> Program {
153- // Populate `self.function_map` using the functions in the program scopes and the modules
154- // Use full Location (program + path) as keys to avoid collisions between programs
161+ // Populate `self.function_map` using the functions in the program scopes and the modules.
162+ // Use full Location (program + path) as keys to avoid collisions between programs.
155163 input
156164 . modules
157165 . iter ( )
@@ -169,6 +177,16 @@ impl ProgramReconstructor for TransformVisitor<'_> {
169177 self . function_map . insert ( location, f) ;
170178 } ) ;
171179
180+ // Populate `function_map` with library functions from FromLibrary stubs.
181+ // Library functions are inlined at call sites just like module functions.
182+ input. stubs . iter ( ) . for_each ( |( _, stub) | {
183+ if let Stub :: FromLibrary { library, .. } = stub {
184+ library. functions . iter ( ) . for_each ( |( name, f) | {
185+ self . function_map . entry ( Location :: new ( library. name , vec ! [ * name] ) ) . or_insert_with ( || f. clone ( ) ) ;
186+ } ) ;
187+ }
188+ } ) ;
189+
172190 // Reconstruct program scopes. Inline functions defined in modules will be traversed
173191 // using the call graph and reconstructed in the right order.
174192 let program_scopes =
@@ -204,15 +222,17 @@ impl AstReconstructor for TransformVisitor<'_> {
204222
205223 /* Expressions */
206224 fn reconstruct_call ( & mut self , input : CallExpression , _additional : & ( ) ) -> ( Expression , Self :: AdditionalOutput ) {
207- // Type checking guarantees that only functions local to the program scope can be inlined.
208- if input. function . expect_global_location ( ) . program != self . program {
225+ let function_location = input. function . expect_global_location ( ) ;
226+
227+ // Pass through calls to external programs (non-library). Library functions and module
228+ // functions are always inlined; only true cross-program calls are left as-is.
229+ if function_location. program != self . program && !self . state . symbol_table . is_library ( function_location. program ) {
209230 return ( input. into ( ) , Default :: default ( ) ) ;
210231 }
211232
212233 // Lookup the reconstructed callee function.
213234 // Since this pass processes functions in post-order, the callee function is guaranteed to exist in `self.reconstructed_functions`
214235 // Use full Location (program + path) to ensure correct lookup across multiple programs.
215- let function_location = input. function . expect_global_location ( ) ;
216236 let ( _, callee) = self
217237 . reconstructed_functions
218238 . iter ( )
@@ -241,6 +261,10 @@ impl AstReconstructor for TransformVisitor<'_> {
241261 function_location. program == self . program && function_location. path . len ( ) > 1 ,
242262 "this is a module function" ,
243263 ) || match callee. variant {
264+ // Always inline library functions (they cannot exist as standalone Aleo functions).
265+ _ if self . state . symbol_table . is_library ( function_location. program ) => {
266+ mandatory_cond ( true , "this is a library function" )
267+ }
244268 Variant :: FinalFn => mandatory_cond ( true , "this is a final fn" ) ,
245269 Variant :: Fn => {
246270 mandatory_cond (
0 commit comments