Skip to content

Commit 8bb1ac9

Browse files
committed
Improve &key parsing errors
1) report all unknown keywords, not just the first. This conses but it's a slow error path so who cares? 2) Detect and report odd keyword arguments in the bytecode. This was causing weird memory leak problems: the unrecognized keyword slot of the signaled unrecognized-keyword condition was junk.
1 parent 8615a91 commit 8bb1ac9

File tree

7 files changed

+47
-34
lines changed

7 files changed

+47
-34
lines changed

include/clasp/core/exceptions.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ void throwTooFewArgumentsError(core::T_sp closure,size_t given, size_t required)
261261

262262
void throwTooManyArgumentsError(core::T_sp closure, size_t given, size_t required);
263263

264-
void throwUnrecognizedKeywordArgumentError(core::T_sp closure, T_sp kw);
264+
void throwOddKeywordsError(core::T_sp closure);
265+
void throwUnrecognizedKeywordArgumentError(core::T_sp closure, core::T_sp kw);
265266

266267
void wrongNumberOfArguments(core::T_sp closure, size_t givenNumberOfArguments, size_t requiredNumberOfArguments);
267268

src/core/bytecode.cc

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

src/core/corePackage.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,7 @@ SYMBOL_EXPORT_SC_(KeywordPkg, print);
592592
SYMBOL_EXPORT_SC_(KeywordPkg, pathname);
593593
SYMBOL_SC_(CorePkg, setThrowPosition);
594594
SYMBOL_EXPORT_SC_(CorePkg, wrongNumberOfArguments);
595+
SYMBOL_EXPORT_SC_(CorePkg, oddKeywords);
595596
SYMBOL_EXPORT_SC_(KeywordPkg, object);
596597
SYMBOL_EXPORT_SC_(KeywordPkg, format_control);
597598
SYMBOL_EXPORT_SC_(KeywordPkg, format_arguments);

src/core/exceptions.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,14 @@ void throwTooManyArgumentsError(core::T_sp closure, size_t given, size_t require
9494
kw::_sym_maxNargs, make_fixnum(required)));
9595
}
9696

97-
void throwUnrecognizedKeywordArgumentError(core::T_sp closure, T_sp kw) {
97+
void throwOddKeywordsError(core::T_sp closure) {
98+
lisp_error(core::_sym_oddKeywords,
99+
lisp_createList(kw::_sym_called_function, closure));
100+
}
101+
102+
void throwUnrecognizedKeywordArgumentError(core::T_sp closure, core::T_sp kws) {
98103
lisp_error(core::_sym_unrecognizedKeywordArgumentError,
99-
lisp_createList(kw::_sym_unrecognizedKeyword, kw,
104+
lisp_createList(kw::_sym_unrecognizedKeywords, kws,
100105
kw::_sym_called_function,closure));
101106
}
102107

src/core/lambdaListHandler.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ SYMBOL_EXPORT_SC_(KeywordPkg, calledFunction);
4545
SYMBOL_EXPORT_SC_(KeywordPkg, givenNargs);
4646
SYMBOL_EXPORT_SC_(KeywordPkg, minNargs);
4747
SYMBOL_EXPORT_SC_(KeywordPkg, maxNargs);
48-
SYMBOL_EXPORT_SC_(KeywordPkg, unrecognizedKeyword);
48+
SYMBOL_EXPORT_SC_(KeywordPkg, unrecognizedKeywords);
4949
SYMBOL_EXPORT_SC_(CorePkg, AMPva_rest );
5050
/*! Return true if the form represents a type
5151
*/

src/lisp/kernel/clos/conditions.lisp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,12 +1122,19 @@ The conflict resolver must be one of ~s" chosen-symbol candidates))
11221122
((:too-few) "Too few arguments"))
11231123
(arguments condition) (lambda-list condition)))))))
11241124

1125+
(define-condition core:odd-keywords (program-error)
1126+
((%called-function :initarg :called-function :reader called-function))
1127+
(:report (lambda (condition stream)
1128+
(format stream "Odd number of keyword arguments~:[~; for ~s~]."
1129+
(called-function condition)
1130+
(core:function-name (called-function condition))))))
1131+
11251132
(define-condition core:unrecognized-keyword-argument-error (program-error)
11261133
((called-function :initarg :called-function :reader called-function :initform nil)
1127-
(unrecognized-keyword :initarg :unrecognized-keyword :reader unrecognized-keyword))
1134+
(unrecognized-keywords :initarg :unrecognized-keywords :reader unrecognized-keywords))
11281135
(:report (lambda (condition stream)
1129-
(format stream "Unrecognized keyword argument ~S~:[~; for ~S~]."
1130-
(unrecognized-keyword condition)
1136+
(format stream "Unrecognized keyword arguments ~S~:[~; for ~S~]."
1137+
(unrecognized-keywords condition)
11311138
(called-function condition)
11321139
(core:function-name (called-function condition))))))
11331140

src/llvmo/link_intrinsics.cc

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,11 +1210,7 @@ T_O* cc_mvcGatherRest2(T_O** values, size_t nvalues) {
12101210

12111211
void cc_oddKeywordException(core::T_O* tclosure) {
12121212
core::Function_sp closure((gc::Tagged)tclosure);
1213-
T_sp functionName = closure->functionName();
1214-
if (functionName.nilp())
1215-
SIMPLE_ERROR(("Odd number of keyword arguments"));
1216-
else
1217-
SIMPLE_ERROR(("In call to %s with lambda-list %s - got odd number of keyword arguments") , _rep_(functionName) , _rep_(closure->lambdaList()));
1213+
throwOddKeywordsError(closure);
12181214
}
12191215

12201216
T_O **cc_multipleValuesArrayAddress()

0 commit comments

Comments
 (0)