@@ -76,14 +76,60 @@ pub enum Ordering {
7676 Loose ,
7777}
7878
79+ /// Tracking where the current function is a module function or an anonymous function.
80+ #[ derive( Debug ) ]
81+ enum CurrentFunction {
82+ /// The current function is a module function
83+ ///
84+ /// ```gleam
85+ /// pub fn main() -> Nil {
86+ /// // we are here
87+ /// }
88+ /// ```
89+ Module ,
90+
91+ /// The current function is a module function, but one of its arguments shadows
92+ /// the reference to itself so it cannot recurse.
93+ ///
94+ /// ```gleam
95+ /// pub fn main(main: fn() -> Nil) -> Nil {
96+ /// // we are here
97+ /// }
98+ /// ```
99+ ModuleWithShadowingArgument ,
100+
101+ /// The current function is an anonymous function
102+ ///
103+ /// ```gleam
104+ /// pub fn main() -> Nil {
105+ /// fn() {
106+ /// // we are here
107+ /// }
108+ /// }
109+ /// ```
110+ Anonymous ,
111+ }
112+
113+ impl CurrentFunction {
114+ #[ inline]
115+ fn can_recurse ( & self ) -> bool {
116+ match self {
117+ CurrentFunction :: Module => true ,
118+ CurrentFunction :: ModuleWithShadowingArgument => false ,
119+ CurrentFunction :: Anonymous => false ,
120+ }
121+ }
122+ }
123+
79124#[ derive( Debug ) ]
80125pub ( crate ) struct Generator < ' module , ' ast > {
81126 module_name : EcoString ,
82127 src_path : & ' module Utf8Path ,
83128 project_root : & ' module Utf8Path ,
84129 line_numbers : & ' module LineNumbers ,
85- function_name : Option < EcoString > ,
130+ function_name : EcoString ,
86131 function_arguments : Vec < Option < & ' module EcoString > > ,
132+ current_function : CurrentFunction ,
87133 pub current_scope_vars : im:: HashMap < EcoString , usize > ,
88134 pub function_position : Position ,
89135 pub scope_position : Position ,
@@ -137,15 +183,15 @@ impl<'module, 'a> Generator<'module, 'a> {
137183 tracker : & ' module mut UsageTracker ,
138184 mut current_scope_vars : im:: HashMap < EcoString , usize > ,
139185 ) -> Self {
140- let mut function_name = Some ( function_name ) ;
186+ let mut current_function = CurrentFunction :: Module ;
141187 for & name in function_arguments. iter ( ) . flatten ( ) {
142188 // Initialise the function arguments
143189 let _ = current_scope_vars. insert ( name. clone ( ) , 0 ) ;
144190
145191 // If any of the function arguments shadow the current function then
146192 // recursion is no longer possible.
147- if function_name. as_ref ( ) == Some ( name) {
148- function_name = None ;
193+ if function_name. as_ref ( ) == name {
194+ current_function = CurrentFunction :: ModuleWithShadowingArgument ;
149195 }
150196 }
151197 Self {
@@ -158,6 +204,7 @@ impl<'module, 'a> Generator<'module, 'a> {
158204 function_arguments,
159205 tail_recursion_used : false ,
160206 current_scope_vars,
207+ current_function,
161208 function_position : Position :: Tail ,
162209 scope_position : Position :: Tail ,
163210 statement_level : Vec :: new ( ) ,
@@ -1307,7 +1354,8 @@ impl<'module, 'a> Generator<'module, 'a> {
13071354 // and we are in tail position we can avoid creating a new stack
13081355 // frame, enabling recursion with constant memory usage.
13091356 TypedExpr :: Var { name, .. }
1310- if self . function_name . as_ref ( ) == Some ( name)
1357+ if self . function_name == * name
1358+ && self . current_function . can_recurse ( )
13111359 && self . function_position . is_tail ( )
13121360 && self . current_scope_vars . get ( name) == Some ( & 0 ) =>
13131361 {
@@ -1366,10 +1414,10 @@ impl<'module, 'a> Generator<'module, 'a> {
13661414 let _ = self . current_scope_vars . insert ( name. clone ( ) , 0 ) ;
13671415 }
13681416
1369- // This is a new function so unset the recorded name so that we don't
1417+ // This is a new function so track that so that we don't
13701418 // mistakenly trigger tail call optimisation
1371- let mut name = None ;
1372- std:: mem:: swap ( & mut self . function_name , & mut name ) ;
1419+ let mut current_function = CurrentFunction :: Anonymous ;
1420+ std:: mem:: swap ( & mut self . current_function , & mut current_function ) ;
13731421
13741422 // Generate the function body
13751423 let result = self . statements ( body) ;
@@ -1378,7 +1426,7 @@ impl<'module, 'a> Generator<'module, 'a> {
13781426 self . function_position = function_position;
13791427 self . scope_position = scope_position;
13801428 self . current_scope_vars = scope;
1381- std:: mem:: swap ( & mut self . function_name , & mut name ) ;
1429+ std:: mem:: swap ( & mut self . current_function , & mut current_function ) ;
13821430
13831431 Ok ( docvec ! [
13841432 docvec![
@@ -1610,12 +1658,7 @@ impl<'module, 'a> Generator<'module, 'a> {
16101658 {
16111659 self . tracker . make_error_used = true ;
16121660 let module = self . module_name . clone ( ) . to_doc ( ) . surround ( '"' , '"' ) ;
1613- let function = self
1614- . function_name
1615- . clone ( )
1616- . unwrap_or_default ( )
1617- . to_doc ( )
1618- . surround ( "\" " , "\" " ) ;
1661+ let function = self . function_name . clone ( ) . to_doc ( ) . surround ( "\" " , "\" " ) ;
16191662 let line = self . line_numbers . line_number ( location. start ) . to_doc ( ) ;
16201663 let fields = wrap_object ( fields. into_iter ( ) . map ( |( k, v) | ( k. to_doc ( ) , Some ( v) ) ) ) ;
16211664
0 commit comments