@@ -355,22 +355,42 @@ vm_comp_resolve_id(struct workspace *wk, obj id, enum vm_comp_resolve_mode mode)
355355}
356356
357357static struct local_binding *
358- vm_comp_declare_local (struct workspace * wk , struct node * n , obj id )
358+ vm_comp_lookup_local (struct workspace * wk , obj id , bool upvalues )
359359{
360+ // Try to find a local variable defined in the current scope or any higher
361+ // scope.
362+ //
363+ // If a matching variable is found at the current scope, then do not
364+ // declare a new local since it has already been declared.
365+ //
366+ // If a matching variable is found at a higher scope, only declare a new
367+ // local variable if it has been bound. This fixes behavior like:
368+ //
369+ // func foo()
370+ // name = 'foo'
371+ // endfunc
372+ //
373+ // name = 'bar'
374+ // foo()
375+ // assert(name == 'bar')
376+ //
377+ // Without this, `name` in foo will incorrectly resolve to the outer
378+ // scope's name.
360379 const uint32_t depth = vm_comp_current_depth (wk );
361380 for (int32_t i = wk -> vm .compiler_state .locals .len - 1 ; i >= 0 ; -- i ) {
362381 struct local_binding * l = arr_get (& wk -> vm .compiler_state .locals , i );
363382 if (l -> depth != depth ) {
364- break ;
383+ if (!upvalues ) {
384+ break ;
385+ } else if (!l -> bound ) {
386+ continue ;
387+ }
365388 }
366389 if (obj_equal (wk , id , l -> id )) {
367- vm_comp_error (wk , n , "duplicate variable name %s" , get_cstr (wk , id ));
368- vm_comp_note (wk , l -> n , "already declared here" );
369- return 0 ;
390+ return l ;
370391 }
371392 }
372-
373- return vm_comp_declare_local_unchecked (wk , n , id );
393+ return 0 ;
374394}
375395
376396static void
@@ -408,42 +428,51 @@ vm_comp_reserve_local_stack_slot(struct workspace *wk, obj id)
408428}
409429
410430static void
411- vm_comp_try_declare_block_local (struct workspace * wk , struct node * n , obj id )
431+ vm_comp_duplicate_local_error (struct workspace * wk , struct node * n , obj id , struct local_binding * prev )
412432{
413- // Try to find a local variable defined in the current scope or any higher
414- // scope.
415- //
416- // If a matching variable is found at the current scope, then do not
417- // declare a new local since it has already been declared.
418- //
419- // If a matching variable is found at a higher scope, only declare a new
420- // local variable if it has been bound. This fixes behavior like:
421- //
422- // func foo()
423- // name = 'foo'
424- // endfunc
425- //
426- // name = 'bar'
427- // foo()
428- // assert(name == 'bar')
429- //
430- // Without this, `name` in foo will incorrectly resolve to the outer
431- // scope's name.
432- const uint32_t depth = vm_comp_current_depth (wk );
433- for (int32_t i = wk -> vm .compiler_state .locals .len - 1 ; i >= 0 ; -- i ) {
434- struct local_binding * l = arr_get (& wk -> vm .compiler_state .locals , i );
435- if (obj_equal (wk , id , l -> id )) {
436- if (depth != l -> depth && !l -> bound ) {
437- continue ;
438- }
433+ vm_comp_error (wk , n , "duplicate variable name %s" , get_cstr (wk , id ));
434+ vm_comp_note (wk , prev -> n , "already declared here" );
435+ }
436+
437+ static void
438+ vm_comp_declare_local (struct workspace * wk , struct node * n , obj id )
439+ {
440+ struct local_binding * prev ;
441+ if ((prev = vm_comp_lookup_local (wk , id , false))) {
442+ vm_comp_duplicate_local_error (wk , n , id , prev );
443+ }
444+
445+ vm_comp_declare_local_unchecked (wk , n , id );
446+ }
447+
448+ static void
449+ vm_comp_force_declare_local (struct workspace * wk , struct node * n , obj id , bool reuse )
450+ {
451+ struct local_binding * prev ;
452+ if ((prev = vm_comp_lookup_local (wk , id , false))) {
453+ if (reuse ) {
439454 return ;
455+ } else {
456+ vm_comp_duplicate_local_error (wk , n , id , prev );
440457 }
441458 }
442459
443- vm_comp_declare_local_unchecked (wk , n , id );
460+ struct local_binding * l = vm_comp_declare_local_unchecked (wk , n , id );
461+ l -> forced_declaration = true;
444462 vm_comp_reserve_local_stack_slot (wk , id );
445463}
446464
465+ static void
466+ vm_comp_try_declare_local (struct workspace * wk ,
467+ struct node * n ,
468+ obj id )
469+ {
470+ if (!vm_comp_lookup_local (wk , id , true)) {
471+ vm_comp_declare_local_unchecked (wk , n , id );
472+ vm_comp_reserve_local_stack_slot (wk , id );
473+ }
474+ }
475+
447476static void
448477vm_comp_block_locals_visitor (struct workspace * wk , struct node * n )
449478{
@@ -454,29 +483,25 @@ vm_comp_block_locals_visitor(struct workspace *wk, struct node *n)
454483 assert (n -> l -> type == node_type_id_lit );
455484 obj id = n -> l -> data .str ;
456485 if (n -> data .type & node_assign_flag_force_declaration ) {
457- struct local_binding * l = vm_comp_declare_local (wk , n -> l , id );
458- vm_comp_reserve_local_stack_slot (wk , id );
459- if (l ) {
460- l -> forced_declaration = true;
461- }
486+ vm_comp_force_declare_local (wk , n -> l , id , false);
462487 } else {
463- vm_comp_try_declare_block_local (wk , n -> l , id );
488+ vm_comp_try_declare_local (wk , n -> l , id );
464489 }
465490 }
466491 break ;
467492 }
468493 case node_type_foreach : {
469494 struct node * ida = n -> l -> l -> l , * idb = n -> l -> l -> r ;
470- vm_comp_try_declare_block_local (wk , ida , ida -> data .str );
495+ vm_comp_force_declare_local (wk , ida , ida -> data .str , true );
471496 if (idb ) {
472- vm_comp_try_declare_block_local (wk , idb , idb -> data .str );
497+ vm_comp_force_declare_local (wk , idb , idb -> data .str , true );
473498 }
474499 break ;
475500 }
476501 case node_type_func_def : {
477502 struct node * id = n -> l -> l -> l ;
478503 if (id ) {
479- vm_comp_try_declare_block_local (wk , id , id -> data .str );
504+ vm_comp_try_declare_local (wk , id , id -> data .str );
480505 }
481506 break ;
482507 }
@@ -870,15 +895,15 @@ vm_comp_node(struct workspace *wk, struct node *n)
870895 push_constant (wk , 0 );
871896
872897 if (wk -> vm .compiler_state .mode & vm_compile_mode_locals ) {
873- vm_comp_assign_local (wk , ida , ida -> data .str , 0 );
898+ vm_comp_assign_local (wk , ida , ida -> data .str , node_assign_flag_force_declaration );
874899 } else {
875900 vm_comp_assign_global (wk , ida -> data .str , 0 );
876901 }
877902 push_code (wk , op_pop );
878903
879904 if (idb ) {
880905 if (wk -> vm .compiler_state .mode & vm_compile_mode_locals ) {
881- vm_comp_assign_local (wk , idb , idb -> data .str , 0 );
906+ vm_comp_assign_local (wk , idb , idb -> data .str , node_assign_flag_force_declaration );
882907 } else {
883908 vm_comp_assign_global (wk , idb -> data .str , 0 );
884909 }
0 commit comments