@@ -484,14 +484,18 @@ static gctools::return_type bytecode_vm(VirtualMachine& vm,
484484 more_start, key_count_info, key_literal_start, key_frame_start);
485485 uint8_t key_count = key_count_info & 0x7f ;
486486 bool ll_aokp = key_count_info & 0x80 ;
487- bool seen_aokp = false ;
488487 bool aokp = false ;
489- bool unknown_key_p = false ;
490- T_O* unknown_key = unbound<T_O>().raw_ ();
488+ T_sp unknown_keys = nil<T_O>();
491489 // Set keyword arguments to unbound.
492490 vm.fillreg (fp, unbound<T_O>().raw_ (), key_count, key_frame_start);
493491 if (lcc_nargs > more_start) {
494- // FIXME: Check for odd keyword portion
492+ if (((lcc_nargs - more_start) % 2 ) != 0 ) {
493+ T_sp tclosure ((gctools::Tagged)gctools::tag_general (closure));
494+ throwOddKeywordsError (tclosure);
495+ }
496+ // We grab keyword arguments from the end to the beginning.
497+ // This means that earlier arguments are put in their variables
498+ // last, matching the CL semantics.
495499 // KLUDGE: We use a signed type so that if more_start is zero we don't
496500 // wrap arg_index around. There's probably a cleverer solution.
497501 ptrdiff_t arg_index;
@@ -502,7 +506,7 @@ static gctools::return_type bytecode_vm(VirtualMachine& vm,
502506 if (key == kw::_sym_allow_other_keys.raw_ ()) {
503507 valid_key_p = true ; // aok is always valid.
504508 T_sp value ((gctools::Tagged)(lcc_args[arg_index]));
505- if (!seen_aokp) aokp = value.notnilp ();
509+ aokp = value.notnilp ();
506510 }
507511 for (size_t key_id = 0 ; key_id < key_count; ++key_id) {
508512 T_O* ckey = literals[key_id + key_literal_start];
@@ -512,16 +516,15 @@ static gctools::return_type bytecode_vm(VirtualMachine& vm,
512516 break ;
513517 }
514518 }
515- if (!valid_key_p) {
516- if (!unknown_key_p) unknown_key = key ;
517- unknown_key_p = true ;
519+ if (!valid_key_p & !ll_aokp ) {
520+ T_sp tunknown ((gctools::Tagged)(lcc_args[arg_index - 1 ])) ;
521+ unknown_keys = Cons_O::create (tunknown, unknown_keys) ;
518522 }
519523 }
520524 }
521- if (unknown_key_p && !aokp && !ll_aokp ) {
525+ if (unknown_keys. notnilp () && !aokp) {
522526 T_sp tclosure ((gctools::Tagged)gctools::tag_general (closure));
523- T_sp tunknown ((gctools::Tagged)unknown_key);
524- throwUnrecognizedKeywordArgumentError (tclosure, tunknown);
527+ throwUnrecognizedKeywordArgumentError (tclosure, unknown_keys);
525528 }
526529 pc++;
527530 break ;
@@ -1081,14 +1084,15 @@ static unsigned char *long_dispatch(VirtualMachine& vm,
10811084 more_start, key_count_info, key_literal_start, key_frame_start);
10821085 uint16_t key_count = key_count_info & 0x7fff ;
10831086 bool ll_aokp = key_count_info & 0x8000 ;
1084- bool seen_aokp = false ;
10851087 bool aokp = false ;
1086- bool unknown_key_p = false ;
1087- T_O* unknown_key = unbound<T_O>().raw_ ();
1088+ T_sp unknown_keys = nil<T_O>();
10881089 // Set keyword arguments to unbound.
10891090 vm.fillreg (fp, unbound<T_O>().raw_ (), key_count, key_frame_start);
10901091 if (lcc_nargs > more_start) {
1091- // FIXME: Check for odd keyword portion
1092+ if (((lcc_nargs - more_start) % 2 ) != 0 ) {
1093+ T_sp tclosure ((gctools::Tagged)gctools::tag_general (closure));
1094+ throwOddKeywordsError (tclosure);
1095+ }
10921096 // KLUDGE: We use a signed type so that if more_start is zero we don't
10931097 // wrap arg_index around. There's probably a cleverer solution.
10941098 ptrdiff_t arg_index;
@@ -1099,7 +1103,7 @@ static unsigned char *long_dispatch(VirtualMachine& vm,
10991103 if (key == kw::_sym_allow_other_keys.raw_ ()) {
11001104 valid_key_p = true ; // aok is always valid.
11011105 T_sp value ((gctools::Tagged)(lcc_args[arg_index]));
1102- if (!seen_aokp) aokp = value.notnilp ();
1106+ aokp = value.notnilp ();
11031107 }
11041108 for (size_t key_id = 0 ; key_id < key_count; ++key_id) {
11051109 T_O* ckey = literals[key_id + key_literal_start];
@@ -1109,16 +1113,15 @@ static unsigned char *long_dispatch(VirtualMachine& vm,
11091113 break ;
11101114 }
11111115 }
1112- if (!valid_key_p) {
1113- if (!unknown_key_p) unknown_key = key;
1114- unknown_key_p = true ;
1116+ if (!valid_key_p && !ll_aokp ) {
1117+ T_sp tunknown ((gctools::Tagged) key) ;
1118+ unknown_keys = Cons_O::create (tunknown, unknown_keys) ;
11151119 }
11161120 }
11171121 }
1118- if (unknown_key_p && !aokp && !ll_aokp ) {
1122+ if (unknown_keys. notnilp () && !aokp) {
11191123 T_sp tclosure ((gctools::Tagged)gctools::tag_general (closure));
1120- T_sp tunknown ((gctools::Tagged)unknown_key);
1121- throwUnrecognizedKeywordArgumentError (tclosure, tunknown);
1124+ throwUnrecognizedKeywordArgumentError (tclosure, unknown_keys);
11221125 }
11231126 pc += 9 ;
11241127 }
0 commit comments