@@ -63,6 +63,8 @@ pub struct Compiler {
6363 self_name : Option < String > ,
6464 /// Number of parameters of the current function (for loop optimization)
6565 self_arity : usize ,
66+ /// Stack of functions currently being inlined (for mutual recursion detection)
67+ inline_stack : Vec < String > ,
6668}
6769
6870/// Check if an expression is pure (no side effects)
@@ -207,6 +209,30 @@ fn contains_call_to(expr: &Value, name: &str) -> bool {
207209 false
208210}
209211
212+ /// Check if expression contains a call to any function in the given list (mutual recursion check)
213+ fn contains_call_to_any ( expr : & Value , names : & [ String ] ) -> bool {
214+ if names. is_empty ( ) {
215+ return false ;
216+ }
217+ if let Some ( items) = expr. as_list ( ) {
218+ if !items. is_empty ( ) {
219+ // Check if this is a call to any of the functions
220+ if let Some ( first) = items[ 0 ] . as_symbol ( ) {
221+ if names. iter ( ) . any ( |n| n == first) {
222+ return true ;
223+ }
224+ }
225+ // Recursively check all sub-expressions
226+ for item in items. iter ( ) {
227+ if contains_call_to_any ( item, names) {
228+ return true ;
229+ }
230+ }
231+ }
232+ }
233+ false
234+ }
235+
210236/// Check if a function body is small enough to inline.
211237/// We inline if the body is a single expression that is:
212238/// - A simple call to another function (thin wrapper)
@@ -443,6 +469,7 @@ impl Compiler {
443469 inline_candidates : InlineCandidates :: new ( ) ,
444470 self_name : None ,
445471 self_arity : 0 ,
472+ inline_stack : Vec :: new ( ) ,
446473 }
447474 }
448475
@@ -1094,11 +1121,18 @@ impl Compiler {
10941121 if args. len ( ) == fn_def. params . len ( ) {
10951122 // Check: function is small enough to inline
10961123 if is_small_body ( & fn_def. body ) {
1097- // Check: function doesn't call itself (non-recursive )
1124+ // Check: function doesn't call itself (direct recursion )
10981125 if !contains_call_to ( & fn_def. body , op) {
1099- // Inline: substitute parameters with arguments and compile
1100- let inlined = substitute ( & fn_def. body , & fn_def. params , args) ;
1101- return self . compile_expr ( & inlined, dest, tail_pos) ;
1126+ // Check: function doesn't call any function in the inline stack (mutual recursion)
1127+ if !contains_call_to_any ( & fn_def. body , & self . inline_stack ) {
1128+ // Inline: substitute parameters with arguments and compile
1129+ let inlined = substitute ( & fn_def. body , & fn_def. params , args) ;
1130+ // Track that we're inlining this function (for mutual recursion detection)
1131+ self . inline_stack . push ( op. to_string ( ) ) ;
1132+ let result = self . compile_expr ( & inlined, dest, tail_pos) ;
1133+ self . inline_stack . pop ( ) ;
1134+ return result;
1135+ }
11021136 }
11031137 }
11041138 }
0 commit comments