@@ -9,7 +9,7 @@ import arc/vm/opcode.{
99 IrArraySpread , IrAsyncYieldStarNext , IrAsyncYieldStarResume , IrAwait , IrBinOp ,
1010 IrCallApply , IrCallConstructor , IrCallConstructorApply , IrCallMethod ,
1111 IrCallMethodApply , IrCallSuper , IrCallSuperApply , IrCreateArguments ,
12- IrDeclareGlobalLex , IrDeclareGlobalVar , IrDefineAccessor ,
12+ IrCreateRestArray , IrDeclareGlobalLex , IrDeclareGlobalVar , IrDefineAccessor ,
1313 IrDefineAccessorComputed , IrDefineField , IrDefineFieldComputed , IrDefineMethod ,
1414 IrDefineMethodComputed , IrDeleteElem , IrDeleteField , IrDup , IrForInNext ,
1515 IrForInStart , IrGetAsyncIterator , IrGetElem , IrGetElem2 , IrGetField ,
@@ -1300,6 +1300,18 @@ fn class_body_references_arguments(body: List(ast.ClassElement)) -> Bool {
13001300 } )
13011301}
13021302
1303+ /// Split off a trailing rest parameter. Returns the fixed params (in order)
1304+ /// and the rest target pattern (the binding inside `...`), if present. A rest
1305+ /// element is only valid as the last parameter, so we only check the tail.
1306+ fn split_trailing_rest (
1307+ params : List ( ast . Pattern ) ,
1308+ ) -> # ( List ( ast . Pattern ) , Option ( ast . Pattern ) ) {
1309+ case list . reverse ( params ) {
1310+ [ ast . RestElement ( inner ) , .. rev_fixed ] -> # ( list . reverse ( rev_fixed ) , Some ( inner ) )
1311+ _ -> # ( params , None )
1312+ }
1313+ }
1314+
13031315/// Compile a function body into a CompiledChild.
13041316fn compile_function_body (
13051317 parent : Emitter ,
@@ -1342,9 +1354,17 @@ fn compile_function_body(
13421354 False -> emit_op ( e , DeclareThis )
13431355 }
13441356
1357+ // A trailing rest parameter (`...rest`) is bound separately from the fixed
1358+ // params: the fixed ones bind positionally (arity counts only them, so
1359+ // build_locals leaves the rest slot undefined), then IrCreateRestArray
1360+ // collects the leftover args into an Array. `arity` excludes the rest param,
1361+ // which also gives the correct `fn.length` (§15.1.5).
1362+ let # ( fixed_params , rest_param ) = split_trailing_rest ( params )
1363+ let arity = list . length ( fixed_params )
1364+
13451365 // Phase 1: Declare parameters (identifier or synthetic for destructuring)
13461366 let # ( e , destructured_params_rev ) =
1347- list . index_fold ( params , # ( e , [ ] ) , fn ( acc , param , idx ) {
1367+ list . index_fold ( fixed_params , # ( e , [ ] ) , fn ( acc , param , idx ) {
13481368 let # ( e , destr ) = acc
13491369 case param {
13501370 ast . IdentifierPattern ( pname ) -> # (
@@ -1393,6 +1413,17 @@ fn compile_function_body(
13931413 emit_destructuring_bind ( e , pattern , LetBinding ) |> result . unwrap ( e )
13941414 } )
13951415
1416+ // Phase 2b: Bind the trailing rest parameter, if any. Build the array from
1417+ // the args at `arity` and beyond, then bind it (an identifier, or a nested
1418+ // destructuring target like `...[a, b]`). ParamBinding declares the slot.
1419+ let e = case rest_param {
1420+ None -> e
1421+ Some ( rest_target ) -> {
1422+ let e = emit_ir ( e , IrCreateRestArray ( arity ) )
1423+ emit_destructuring_bind ( e , rest_target , ParamBinding ) |> result . unwrap ( e )
1424+ }
1425+ }
1426+
13961427 // Hoisting for the function body
13971428 let hoisted_vars = collect_hoisted_vars ( stmts )
13981429 let lex_names = collect_top_lex_names ( stmts )
@@ -1442,7 +1473,7 @@ fn compile_function_body(
14421473
14431474 CompiledChild (
14441475 name : ,
1445- arity : list . length ( params ) ,
1476+ arity : ,
14461477 code : ,
14471478 constants : ,
14481479 constants_map : ,
0 commit comments