Skip to content

Commit

Permalink
If a multi-method dispatch fails, also dump the list of candidates, s…
Browse files Browse the repository at this point in the history
…orted as the dispatcher would sort them, like we do for failed multi-sub dispatches.
  • Loading branch information
jnthn committed Jul 16, 2010
1 parent 7b26327 commit b19a44e
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 14 deletions.
54 changes: 40 additions & 14 deletions src/pmc/p6invocation.pmc
Expand Up @@ -46,7 +46,8 @@ static STRING *CANDIDATE_LIST_str;

/* This does the grunt work of working out what the next candidate is. Takes
* account of us maybe needing to look into multi variants and all that lot. */
static PMC *get_next_candidate(PARROT_INTERP, PMC *SELF, int check_only, INTVAL *is_multi_dispatch) {
static PMC *get_next_candidate(PARROT_INTERP, PMC *SELF, int check_only,
INTVAL *is_multi_dispatch, PMC **guilty_multi) {
PMC *candidates, *current, *search_list;
STRING *name;
INTVAL position, resume_point;
Expand Down Expand Up @@ -88,7 +89,11 @@ static PMC *get_next_candidate(PARROT_INTERP, PMC *SELF, int check_only, INTVAL
if (VTABLE_isa(interp, current, CONST_STRING(interp, "Perl6MultiSub"))) {
/* Multi. Ask the multi-dispatcher for all possible variants that we
* could call with the current argument, unless we have none in
* which we're just gonna have to leave the multi here in the list. */
* which we're just gonna have to leave the multi here in the list.
* We may also mark the first multi we run into as being "guilty"
* for the dispatch failure so we can give a better error. */
if (guilty_multi && PMC_IS_NULL(*guilty_multi))
*guilty_multi = current;
if (!PMC_IS_NULL(Parrot_pcc_get_signature(interp, CURRENT_CONTEXT(interp)))) {
PMC *possibles = get_all_candidates_with_cur_args(interp, current);
if (VTABLE_elements(interp, possibles) == 0) {
Expand Down Expand Up @@ -212,7 +217,7 @@ pmclass P6Invocation dynpmc group perl6_group auto_attrs {

/* If not, then we see if the dispatcher can potentially find more. */
Parrot_pcc_set_signature(interp, CURRENT_CONTEXT(interp), NULL);
return !PMC_IS_NULL(get_next_candidate(interp, SELF, P6I_MODE_CHECK, NULL));
return !PMC_IS_NULL(get_next_candidate(interp, SELF, P6I_MODE_CHECK, NULL, NULL));
}

VTABLE INTVAL get_integer() {
Expand All @@ -225,7 +230,7 @@ pmclass P6Invocation dynpmc group perl6_group auto_attrs {
GETATTR_P6Invocation_first_candidate(interp, clone, first_candidate);
if (PMC_IS_NULL(first_candidate)) {
Parrot_pcc_set_signature(interp, CURRENT_CONTEXT(interp), NULL);
first_candidate = get_next_candidate(interp, clone, P6I_MODE_CHECK, NULL);
first_candidate = get_next_candidate(interp, clone, P6I_MODE_CHECK, NULL, NULL);
}
return first_candidate;
}
Expand All @@ -239,13 +244,15 @@ pmclass P6Invocation dynpmc group perl6_group auto_attrs {
PMC *lexpad, *first_candidate;
opcode_t *addr;
INTVAL is_multi_dispatch = 0;
PMC *guilty_multi = PMCNULL;

/* In the straightforward case, we know our first candidate right off the
* bat; if not, use list. We also nullify first candidate so we hit the
* candidate list next time we're used. */
GETATTR_P6Invocation_first_candidate(interp, SELF, first_candidate);
if (PMC_IS_NULL(first_candidate))
first_candidate = get_next_candidate(interp, SELF, P6I_MODE_DISPATCH, &is_multi_dispatch);
first_candidate = get_next_candidate(interp, SELF, P6I_MODE_DISPATCH,
&is_multi_dispatch, &guilty_multi);
else
SETATTR_P6Invocation_first_candidate(interp, SELF, PMCNULL);

Expand All @@ -256,18 +263,37 @@ pmclass P6Invocation dynpmc group perl6_group auto_attrs {
/* Oh noes, no candidate. If we aren't in soft-fail mode, then
* throw an exception with an informative error message. */
if (!PObj_flag_TEST(P6I_FAILURE_MODE, SELF)) {
STRING *method_name, *type_name;
STRING *method_name, *type_name, *signatures;

/* Get object type name. */
PMC *WHAT, *perl_meth;
PMC *call_sig = Parrot_pcc_get_signature(interp, CURRENT_CONTEXT(interp));
PMC *obj = VTABLE_get_pmc_keyed_int(interp, call_sig, 0);
PMC *WHAT_meth = VTABLE_find_method(interp, obj, CONST_STRING(interp, "WHAT"));
PMC *first_cand = PMCNULL;
PMC *call_sig = Parrot_pcc_get_signature(interp, CURRENT_CONTEXT(interp));
PMC *obj = VTABLE_get_pmc_keyed_int(interp, call_sig, 0);
PMC *WHAT_meth = VTABLE_find_method(interp, obj, CONST_STRING(interp, "WHAT"));
Parrot_ext_call(interp, WHAT_meth, "Pi->P", obj, &WHAT);
perl_meth = VTABLE_find_method(interp, WHAT, CONST_STRING(interp, "perl"));
perl_meth = VTABLE_find_method(interp, WHAT, CONST_STRING(interp, "perl"));
Parrot_ext_call(interp, perl_meth, "Pi->S", WHAT, &type_name);

/* Get method name. */
GETATTR_P6Invocation_name(interp, SELF, method_name);
Parrot_ex_throw_from_c_args(interp, next, 1,
"No candidates found to invoke for method '%Ss' on object of type '%Ss'",
method_name, type_name);

/* If we know the multi most immediately blameable for not having any
* candidates, get a dump of it's signatures too. */
if (!PMC_IS_NULL(guilty_multi)) {
STRING *siggies;
PMC *dump_meth = VTABLE_find_method(interp, guilty_multi,
CONST_STRING(interp, "dump_sorted_candidate_signatures"));
Parrot_ext_call(interp, dump_meth, "Pi->S", guilty_multi, &siggies);
Parrot_ex_throw_from_c_args(interp, next, 1,
"No candidates found to invoke for method '%Ss' on object of type '%Ss'; available candidates has signatures:\n%Ss",
method_name, type_name, siggies);
}
else {
Parrot_ex_throw_from_c_args(interp, next, 1,
"No candidates found to invoke for method '%Ss' on object of type '%Ss'",
method_name, type_name);
}
}

/* Otherwise, we look up something that when invoked will just give a
Expand All @@ -294,7 +320,7 @@ pmclass P6Invocation dynpmc group perl6_group auto_attrs {
GETATTR_P6Invocation_first_candidate(interp, SELF, first_candidate);
if (PMC_IS_NULL(first_candidate)) {
Parrot_pcc_set_signature(interp, CURRENT_CONTEXT(interp), NULL);
get_next_candidate(interp, SELF, P6I_MODE_DISPATCH, NULL);
get_next_candidate(interp, SELF, P6I_MODE_DISPATCH, NULL, NULL);
}
else {
SETATTR_P6Invocation_first_candidate(interp, SELF, PMCNULL);
Expand Down
42 changes: 42 additions & 0 deletions src/pmc/perl6multisub.pmc
Expand Up @@ -1362,6 +1362,48 @@ skipping any candidates with subids already present in the invocant.
}
RETURN (PMC *SELF);
}

/*

=item METHOD STRING *dump_sorted_candidate_signatures()

Dumps a list of candidate signautres, sorted in dispatch order.

=cut

*/

METHOD STRING *dump_sorted_candidate_signatures() {
candidate_info **candidates = NULL;
candidate_info **cur_candidate = NULL;
STRING *signatures = Parrot_str_new(interp, "", 0);
PMC *unsorted;

/* Make sure that we have a candidate list built. */
GETATTR_Perl6MultiSub_candidates_sorted(interp, SELF, candidates);
GETATTR_Perl6MultiSub_candidates(interp, SELF, unsorted);
if (!candidates) {
PMC *proto;
candidates = sort_candidates(interp, unsorted, &proto);
SETATTR_Perl6MultiSub_candidates_sorted(interp, SELF, candidates);
SETATTR_Perl6MultiSub_proto(interp, SELF, proto);
}
if (!candidates)
Parrot_ex_throw_from_c_args(interp, NULL, 1,
"Failed to build candidate list");

/* Now go over the candidates and build a result string. */
cur_candidate = candidates;
while (1) {
if (!cur_candidate[0] && !cur_candidate[1])
break;
if (cur_candidate[0])
signatures = dump_signature(interp, signatures, (*cur_candidate)->sub);
cur_candidate++;
}

RETURN (STRING *signatures);
}
}

/*
Expand Down

0 comments on commit b19a44e

Please sign in to comment.