Skip to content

Commit 9562097

Browse files
authored
Merge pull request #1436 from clasp-developers/fix-&key-defaulting
Fix issue with &optional/&key defaulting in bytecode
2 parents 3ea35cd + f342c73 commit 9562097

File tree

1 file changed

+44
-56
lines changed

1 file changed

+44
-56
lines changed

src/core/bytecode_compiler.cc

Lines changed: 44 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,25 +1171,37 @@ static T_sp extract_lambda_list_from_declares(List_sp declares, T_sp defaultll)
11711171
return defaultll;
11721172
}
11731173

1174-
Lexenv_sp compile_optional_or_key_item(Symbol_sp var, T_sp defaulting_form, size_t var_index, Symbol_sp supplied_var,
1174+
Lexenv_sp compile_optional_or_key_item(Symbol_sp var, T_sp defaulting_form, LexicalVarInfo_sp varinfo, Symbol_sp supplied_var,
11751175
Label_sp next_label, bool var_specialp, bool supplied_specialp, const Context context,
11761176
Lexenv_sp env) {
11771177
Label_sp supplied_label = Label_O::make();
1178-
T_sp varinfo = env->variableInfo(var);
11791178
T_sp supinfo = nil<T_O>();
1180-
if (supplied_var.notnilp()) {
1181-
env = env->bind1var(supplied_var, context);
1182-
supinfo = env->variableInfo(supplied_var);
1183-
}
1184-
context.emit_jump_if_supplied(supplied_label, var_index);
1179+
context.emit_jump_if_supplied(supplied_label, varinfo->frameIndex());
11851180
// Emit code for the case of the variable not being supplied:
11861181
// Bind the var to the default, and the suppliedvar to NIL if applicable.
11871182
compile_form(defaulting_form, env, Context(context, 1));
1183+
// Now that the default form is compiled, bind variables (for later vars)
1184+
if (var_specialp)
1185+
env = env->add_specials(Cons_O::createList(var));
1186+
else
1187+
// import the existing info.
1188+
env = Lexenv_O::make(Cons_O::create(Cons_O::create(var, varinfo),
1189+
env->vars()),
1190+
env->tags(), env->blocks(), env->funs(),
1191+
env->notinlines(), env->frameEnd());
1192+
if (supplied_var.notnilp()) {
1193+
if (supplied_specialp)
1194+
env = env->add_specials(Cons_O::createList(supplied_var));
1195+
else
1196+
env = env->bind1var(supplied_var, context);
1197+
supinfo = env->variableInfo(supplied_var);
1198+
}
1199+
// Actually generate the unsupplied case.
11881200
if (var_specialp)
11891201
context.emit_special_bind(var);
11901202
else {
1191-
context.maybe_emit_make_cell(gc::As_assert<LexicalVarInfo_sp>(varinfo));
1192-
context.assemble1(vm_set, var_index);
1203+
context.maybe_emit_make_cell(varinfo);
1204+
context.assemble1(vm_set, varinfo->frameIndex());
11931205
}
11941206
if (supplied_var.notnilp()) { // bind supplied_var to NIL
11951207
context.assemble0(vm_nil);
@@ -1205,10 +1217,10 @@ Lexenv_sp compile_optional_or_key_item(Symbol_sp var, T_sp defaulting_form, size
12051217
// Now for when the variable is supplied.
12061218
supplied_label->contextualize(context);
12071219
if (var_specialp) { // we have it in a reg, so rebind
1208-
context.assemble1(vm_ref, var_index);
1220+
context.assemble1(vm_ref, varinfo->frameIndex());
12091221
context.emit_special_bind(var);
12101222
} else { // in the reg already, but maybe needs a cell
1211-
context.maybe_emit_encage(gc::As_assert<LexicalVarInfo_sp>(varinfo));
1223+
context.maybe_emit_encage(varinfo);
12121224
}
12131225
if (supplied_var.notnilp()) {
12141226
compile_literal(cl::_sym_T_O, env, Context(context, 1));
@@ -1221,10 +1233,6 @@ Lexenv_sp compile_optional_or_key_item(Symbol_sp var, T_sp defaulting_form, size
12211233
}
12221234
}
12231235
// That's it for code generation. Now return the new environment.
1224-
if (var_specialp)
1225-
env = env->add_specials(Cons_O::createList(var));
1226-
if (supplied_specialp)
1227-
env = env->add_specials(Cons_O::createList(supplied_var));
12281236
return env;
12291237
}
12301238

@@ -1253,12 +1261,8 @@ void compile_with_lambda_list(T_sp lambda_list, List_sp body, Lexenv_sp env, con
12531261
for (auto &it : reqs)
12541262
lreqs << it._ArgTarget;
12551263
Lexenv_sp new_env = env->bind_vars(lreqs.cons(), context);
1264+
// This environment is only used for assigning indices to opt/key variables.
12561265
size_t special_binding_count = 0;
1257-
// An alist from optional and key variables to their local indices.
1258-
// This is needed so that we can properly mark any that are special as
1259-
// such while leaving them temporarily "lexically" bound during
1260-
// argument parsing.
1261-
List_sp opt_key_indices = nil<T_O>();
12621266

12631267
entry_point->contextualize(context);
12641268
// Generate argument count check.
@@ -1290,29 +1294,23 @@ void compile_with_lambda_list(T_sp lambda_list, List_sp body, Lexenv_sp env, con
12901294
}
12911295
new_env = new_env->add_specials(sreqs.cons());
12921296
}
1297+
Lexenv_sp optkey_env = new_env;
12931298
if (optional_count > 0) {
12941299
// Generate code to bind the provided optional args, unprovided args will
12951300
// be initialized with the unbound marker.
12961301
context.assemble2(vm_bind_optional_args, min_count, optional_count);
12971302
// Mark the locations of each optional. Note that we do this even if
1298-
// the variable will be specially bound.
1303+
// the variable will be specially bound, to match the placement by
1304+
// bind_optional_args.
12991305
ql::list opts;
13001306
for (auto &it : optionals)
13011307
opts << it._ArgTarget;
1302-
new_env = new_env->bind_vars(opts.cons(), context);
1303-
// Add everything to opt-key-indices.
1304-
for (auto &it : optionals) {
1305-
T_sp var = it._ArgTarget;
1306-
auto lvinfo = gc::As_assert<LexicalVarInfo_sp>(new_env->variableInfo(var));
1307-
opt_key_indices = Cons_O::create(Cons_O::create(var, clasp_make_fixnum(lvinfo->frameIndex())), opt_key_indices);
1308-
}
1309-
// Re-mark anything that's special in the outer context as such, so that
1310-
// default initforms properly treat them as special.
1311-
ql::list sopts;
1312-
for (auto &it : optionals)
1313-
if (std::holds_alternative<SpecialVarInfoV>(var_info_v(it._ArgTarget, env)))
1314-
sopts << it._ArgTarget;
1315-
new_env = new_env->add_specials(sopts.cons());
1308+
optkey_env = optkey_env->bind_vars(opts.cons(), context);
1309+
// new_env has enough space for the optional arguments, but without the
1310+
// variables actually bound, so that default forms can be compiled correctly
1311+
new_env = Lexenv_O::make(new_env->vars(), optkey_env->tags(),
1312+
optkey_env->blocks(), optkey_env->funs(),
1313+
optkey_env->notinlines(), optkey_env->frameEnd());
13161314
}
13171315
if (key_flag.notnilp()) {
13181316
// Generate code to parse the key args. As with optionals, we don't do
@@ -1330,24 +1328,16 @@ void compile_with_lambda_list(T_sp lambda_list, List_sp body, Lexenv_sp env, con
13301328
context.new_literal_index(it._Keyword);
13311329
}
13321330
// now the actual instruction
1333-
context.emit_parse_key_args(max_count, keys.size(), first_key_index, new_env->frameEnd(), aokp.notnilp());
1331+
context.emit_parse_key_args(max_count, keys.size(), first_key_index, optkey_env->frameEnd(), aokp.notnilp());
13341332
ql::list keyvars;
1335-
ql::list skeys;
1336-
for (auto &it : keys) {
1333+
for (auto &it : keys)
13371334
keyvars << it._ArgTarget;
1338-
if (std::holds_alternative<SpecialVarInfoV>(var_info_v(it._ArgTarget, env)))
1339-
skeys << it._ArgTarget;
1340-
}
1341-
new_env = new_env->bind_vars(keyvars.cons(), context);
1342-
for (auto &it : keys) {
1343-
T_sp var = it._ArgTarget;
1344-
auto lvinfo = gc::As_assert<LexicalVarInfo_sp>(new_env->variableInfo(var));
1345-
opt_key_indices = Cons_O::create(Cons_O::create(var, clasp_make_fixnum(lvinfo->frameIndex())), opt_key_indices);
1346-
}
1347-
new_env = new_env->add_specials(skeys.cons());
1335+
optkey_env = optkey_env->bind_vars(keyvars.cons(), context);
1336+
new_env = Lexenv_O::make(new_env->vars(), optkey_env->tags(),
1337+
optkey_env->blocks(), optkey_env->funs(),
1338+
optkey_env->notinlines(), optkey_env->frameEnd());
13481339
}
1349-
// Generate defaulting code for optional args, and special-bind them
1350-
// if necessary.
1340+
// Generate defaulting code for optional args, and bind them properly.
13511341
if (optional_count > 0) {
13521342
Label_sp optional_label = Label_O::make();
13531343
Label_sp next_optional_label = Label_O::make();
@@ -1357,10 +1347,9 @@ void compile_with_lambda_list(T_sp lambda_list, List_sp body, Lexenv_sp env, con
13571347
T_sp defaulting_form = it._Default;
13581348
T_sp supplied_var = it._Sensor._ArgTarget;
13591349
bool optional_special_p = special_binding_p(optional_var, specials, env);
1360-
T_sp pair = core__alist_assoc_eq(gc::As_assert<Cons_sp>(opt_key_indices), optional_var);
1361-
size_t index = oCdr(pair).unsafe_fixnum();
1350+
auto varinfo = gc::As_assert<LexicalVarInfo_sp>(var_info(optional_var, optkey_env));
13621351
bool supplied_special_p = supplied_var.notnilp() && special_binding_p(supplied_var, specials, env);
1363-
new_env = compile_optional_or_key_item(optional_var, defaulting_form, index, supplied_var, next_optional_label,
1352+
new_env = compile_optional_or_key_item(optional_var, defaulting_form, varinfo, supplied_var, next_optional_label,
13641353
optional_special_p, supplied_special_p, context, new_env);
13651354
if (optional_special_p)
13661355
++special_binding_count;
@@ -1401,10 +1390,9 @@ void compile_with_lambda_list(T_sp lambda_list, List_sp body, Lexenv_sp env, con
14011390
T_sp defaulting_form = it._Default;
14021391
T_sp supplied_var = it._Sensor._ArgTarget;
14031392
bool key_special_p = special_binding_p(key_var, specials, env);
1404-
T_sp pair = core__alist_assoc_eq(gc::As<Cons_sp>(opt_key_indices), key_var);
1405-
size_t index = oCdr(pair).unsafe_fixnum();
1393+
auto varinfo = gc::As_assert<LexicalVarInfo_sp>(var_info(key_var, optkey_env));
14061394
bool supplied_special_p = supplied_var.notnilp() && special_binding_p(supplied_var, specials, env);
1407-
new_env = compile_optional_or_key_item(key_var, defaulting_form, index, supplied_var, next_key_label, key_special_p,
1395+
new_env = compile_optional_or_key_item(key_var, defaulting_form, varinfo, supplied_var, next_key_label, key_special_p,
14081396
supplied_special_p, context, new_env);
14091397
if (key_special_p)
14101398
++special_binding_count;

0 commit comments

Comments
 (0)