Skip to content

Commit 57df7d1

Browse files
committed
rewrite function calls that pass keyword arguments (not kwargs)
1 parent e7f77b3 commit 57df7d1

File tree

4 files changed

+1082
-24
lines changed

4 files changed

+1082
-24
lines changed

src/runtime/rearrange_arguments.cpp

Lines changed: 122 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,14 @@ namespace pyston {
2727
// The logic is already complex, and the function also deals with rewriting
2828
// the logic for ICs, so it gets pretty hairy.
2929

30-
enum class KeywordDest {
30+
enum class KeywordDestType {
3131
POSITIONAL,
3232
KWARGS,
3333
};
34+
struct KeywordDest {
35+
KeywordDestType type;
36+
int param_idx;
37+
};
3438
static KeywordDest placeKeyword(const ParamNames* param_names, llvm::SmallVector<bool, 8>& params_filled,
3539
BoxedString* kw_name, Box* kw_val, Box*& oarg1, Box*& oarg2, Box*& oarg3, Box** oargs,
3640
BoxedDict* okwargs, const char* func_name) {
@@ -49,7 +53,7 @@ static KeywordDest placeKeyword(const ParamNames* param_names, llvm::SmallVector
4953
getArg(j, oarg1, oarg2, oarg3, oargs) = kw_val;
5054
params_filled[j] = true;
5155

52-
return KeywordDest::POSITIONAL;
56+
return { KeywordDestType::POSITIONAL, j };
5357
}
5458
}
5559

@@ -60,7 +64,7 @@ static KeywordDest placeKeyword(const ParamNames* param_names, llvm::SmallVector
6064
kw_name->c_str());
6165
}
6266
v = kw_val;
63-
return KeywordDest::KWARGS;
67+
return { KeywordDestType::KWARGS, 0 };
6468
} else {
6569
raiseExcHelper(TypeError, "%.200s() got an unexpected keyword argument '%s'", func_name, kw_name->c_str());
6670
}
@@ -146,6 +150,10 @@ extern "C" BoxedTuple* makeVarArgsFromArgsAndStarArgs(Box* arg1, Box* arg2, Box*
146150
return starParam;
147151
}
148152

153+
extern "C" void insertInDict(BoxedDict* d, Box* key, Box* val) {
154+
d->d.insert(std::make_pair(key, val));
155+
}
156+
149157
void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_names, const char* func_name,
150158
Box** defaults, CallRewriteArgs* rewrite_args, bool& rewrite_success, ArgPassSpec argspec,
151159
Box* arg1, Box* arg2, Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names,
@@ -240,6 +248,8 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
240248
}
241249

242250
llvm::SmallVector<bool, 8> params_filled(num_output_args);
251+
llvm::SmallVector<KeywordDest, 8> kw_arg_idx_to_param_idx(argspec.num_keywords);
252+
243253
for (int i = 0; i < positional_to_positional + varargs_to_positional; i++) {
244254
params_filled[i] = true;
245255
}
@@ -286,11 +296,14 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
286296
if (!param_names || !param_names->takes_param_names) {
287297
assert(okwargs);
288298
okwargs->d[(*keyword_names)[i]] = kw_val;
299+
300+
kw_arg_idx_to_param_idx[i] = { KeywordDestType::KWARGS, 0 };
289301
continue;
290302
}
291303

292-
auto dest = placeKeyword(param_names, params_filled, (*keyword_names)[i], kw_val, oarg1, oarg2, oarg3, oargs,
293-
okwargs, func_name);
304+
KeywordDest dest = placeKeyword(param_names, params_filled, (*keyword_names)[i], kw_val, oarg1, oarg2, oarg3,
305+
oargs, okwargs, func_name);
306+
kw_arg_idx_to_param_idx[i] = dest;
294307
}
295308

296309
if (argspec.has_kwargs) {
@@ -361,10 +374,15 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
361374
// If we need to make modifications, we have to copy it first.
362375

363376
// Right now we don't handle either of these
364-
if (argspec.has_kwargs || argspec.num_keywords)
377+
if (argspec.has_kwargs)
365378
return;
366379

367-
if (argspec.has_starargs && !paramspec.num_defaults) {
380+
RewriterVar* r_original_arg1 = rewrite_args->arg1;
381+
RewriterVar* r_original_arg2 = rewrite_args->arg2;
382+
RewriterVar* r_original_arg3 = rewrite_args->arg3;
383+
RewriterVar* r_original_args = rewrite_args->args;
384+
385+
if (argspec.has_starargs && !paramspec.num_defaults && !argspec.num_keywords) {
368386
assert(!argspec.has_kwargs);
369387
assert(!argspec.num_keywords);
370388
// We just dispatch to a helper function to copy the args and call pyElements
@@ -468,6 +486,14 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
468486
}
469487

470488
if (!(paramspec.takes_varargs && argspec.num_args > paramspec.num_args + 3) && !argspec.has_starargs) {
489+
// Most general case handled here is
490+
// def f(a, b, c, d=default, e=default, *args, **kwargs):
491+
//
492+
// f(1,2,...,keyword=blah,...)
493+
// (i.e. no starargs or kwargs passed in)
494+
assert(!argspec.has_starargs);
495+
assert(!argspec.has_kwargs);
496+
471497
// We might have trouble if we have more output args than input args,
472498
// such as if we need more space to pass defaults.
473499
bool did_copy = false;
@@ -543,40 +569,112 @@ void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_name
543569
}
544570
}
545571

572+
RewriterVar* r_output_kwargs = NULL;
546573
if (paramspec.takes_kwargs) {
547-
assert(!argspec.num_keywords && !argspec.has_kwargs);
574+
assert(!argspec.has_kwargs);
548575

549576
int kwargs_idx = paramspec.num_args + (paramspec.takes_varargs ? 1 : 0);
550-
RewriterVar* r_kwargs = rewrite_args->rewriter->call(true, (void*)createDict);
577+
r_output_kwargs = rewrite_args->rewriter->call(true, (void*)createDict);
551578

552579
if (kwargs_idx == 0)
553-
rewrite_args->arg1 = r_kwargs;
580+
rewrite_args->arg1 = r_output_kwargs;
554581
if (kwargs_idx == 1)
555-
rewrite_args->arg2 = r_kwargs;
582+
rewrite_args->arg2 = r_output_kwargs;
556583
if (kwargs_idx == 2)
557-
rewrite_args->arg3 = r_kwargs;
584+
rewrite_args->arg3 = r_output_kwargs;
558585
if (kwargs_idx >= 3) {
559-
assert(did_copy);
560-
rewrite_args->args->setAttr((kwargs_idx - 3) * sizeof(Box*), r_kwargs);
586+
if (!did_copy) {
587+
if (num_passed_args <= 3) {
588+
// we weren't passed args
589+
rewrite_args->args = rewrite_args->rewriter->allocate(num_output_args - 3);
590+
} else {
591+
rewrite_args->args
592+
= rewrite_args->rewriter->allocateAndCopy(rewrite_args->args, num_output_args - 3);
593+
}
594+
did_copy = true;
595+
}
596+
rewrite_args->args->setAttr((kwargs_idx - 3) * sizeof(Box*), r_output_kwargs);
561597
}
562598
}
563599

564-
for (int arg_idx = std::max((int)(paramspec.num_args - paramspec.num_defaults), (int)argspec.num_args);
600+
// Here we loop through all the positional parameters which are past the last positional arg.
601+
// In each case, we check if we can fill it with either a given keyword arg (done below).
602+
// If not, we fill it with the default.
603+
for (int arg_idx = std::max((int)argspec.num_args, paramspec.num_args - paramspec.num_defaults);
565604
arg_idx < paramspec.num_args; arg_idx++) {
566-
int default_idx = arg_idx + paramspec.num_defaults - paramspec.num_args;
605+
// Do we have a keyword for this?
606+
if (!params_filled[arg_idx]) {
607+
// No keyword arg, use the default
608+
int default_idx = arg_idx + paramspec.num_defaults - paramspec.num_args;
609+
Box* default_obj = defaults[default_idx];
610+
RewriterVar* r_default = rewrite_args->rewriter->loadConst((intptr_t)default_obj);
611+
612+
if (arg_idx == 0)
613+
rewrite_args->arg1 = r_default;
614+
else if (arg_idx == 1)
615+
rewrite_args->arg2 = r_default;
616+
else if (arg_idx == 2)
617+
rewrite_args->arg3 = r_default;
618+
else {
619+
if (!did_copy) {
620+
if (num_passed_args <= 3) {
621+
// we weren't passed args
622+
rewrite_args->args = rewrite_args->rewriter->allocate(num_output_args - 3);
623+
} else {
624+
rewrite_args->args
625+
= rewrite_args->rewriter->allocateAndCopy(rewrite_args->args, num_output_args - 3);
626+
}
627+
did_copy = true;
628+
}
567629

568-
Box* default_obj = defaults[default_idx];
630+
rewrite_args->args->setAttr((arg_idx - 3) * sizeof(Box*), r_default);
631+
}
632+
}
633+
}
634+
635+
for (int i = 0; i < argspec.num_keywords; i++) {
636+
int arg_idx = argspec.num_args + i;
569637

638+
RewriterVar* r_arg;
570639
if (arg_idx == 0)
571-
rewrite_args->arg1 = rewrite_args->rewriter->loadConst((intptr_t)default_obj, Location::forArg(0));
640+
r_arg = r_original_arg1;
572641
else if (arg_idx == 1)
573-
rewrite_args->arg2 = rewrite_args->rewriter->loadConst((intptr_t)default_obj, Location::forArg(1));
642+
r_arg = r_original_arg2;
574643
else if (arg_idx == 2)
575-
rewrite_args->arg3 = rewrite_args->rewriter->loadConst((intptr_t)default_obj, Location::forArg(2));
576-
else {
577-
assert(did_copy);
578-
rewrite_args->args->setAttr((arg_idx - 3) * sizeof(Box*),
579-
rewrite_args->rewriter->loadConst((intptr_t)default_obj));
644+
r_arg = r_original_arg3;
645+
else
646+
r_arg = r_original_args->getAttr(sizeof(Box*) * (arg_idx - 3));
647+
648+
KeywordDest dest = kw_arg_idx_to_param_idx[i];
649+
if (dest.type == KeywordDestType::POSITIONAL) {
650+
// Move keyword arg into its keyword position
651+
if (dest.param_idx == 0)
652+
rewrite_args->arg1 = r_arg;
653+
else if (dest.param_idx == 1)
654+
rewrite_args->arg2 = r_arg;
655+
else if (dest.param_idx == 2)
656+
rewrite_args->arg3 = r_arg;
657+
else {
658+
if (!did_copy) {
659+
if (num_passed_args <= 3) {
660+
// we weren't passed args
661+
rewrite_args->args = rewrite_args->rewriter->allocate(num_output_args - 3);
662+
} else {
663+
rewrite_args->args
664+
= rewrite_args->rewriter->allocateAndCopy(rewrite_args->args, num_output_args - 3);
665+
}
666+
did_copy = true;
667+
}
668+
669+
rewrite_args->args->setAttr((dest.param_idx - 3) * sizeof(Box*), r_arg);
670+
}
671+
} else {
672+
// TODO we should be able to inline a lot more of the dict creation
673+
// (e.g. just have a "template" dict object and copy all the attributes into the
674+
// right offsets)
675+
assert(r_output_kwargs != NULL);
676+
rewrite_args->rewriter->call(false, (void*)insertInDict, r_output_kwargs,
677+
rewrite_args->rewriter->loadConst((int64_t)(*keyword_names)[i]), r_arg);
580678
}
581679
}
582680

0 commit comments

Comments
 (0)