Ejemplo n.º 1
0
/*

=item C<static void add_to_cache(PARROT_INTERP, NQP_md_cache *cache, PMC *capture, INTVAL num_args)>

Adds an entry to the multi-dispatch cache.

=cut

*/
void add_to_cache(PARROT_INTERP, NQP_md_cache *cache, PMC *capture, INTVAL num_args, PMC *result) {
    INTVAL arg_tup[MD_CACHE_MAX_ARITY];
    INTVAL i, entries, ins_type;
    struct Pcc_cell * pc_positionals;
    
    /* Make sure 6model type ID is set. */
    if (!smo_id)
        smo_id = Parrot_pmc_get_type_str(interp, Parrot_str_new(interp, "SixModelObject", 0));
    
    /* If it's zero arity, just stick it in that slot. */
    if (num_args == 0) {
        cache->zero_arity = result;
        return;
    }
    
    /* If the cache is saturated, don't do anything (we could instead do a random
     * replacement). */
    entries = cache->arity_caches[num_args - 1].num_entries;
    if (entries == MD_CACHE_MAX_ENTRIES)
        return;
    
    /* Create arg tuple. */
    if (capture->vtable->base_type == enum_class_CallContext)
        GETATTR_CallContext_positionals(interp, capture, pc_positionals);
    else
        return;
    for (i = 0; i < num_args; i++) {
        if (pc_positionals[i].type == BIND_VAL_OBJ) {
            PMC *arg = pc_positionals[i].u.p;
            if (arg->vtable->base_type != smo_id)
                return;
            arg_tup[i] = STABLE(arg)->type_cache_id | (IS_CONCRETE(arg) ? 1 : 0);
        }
        else {
            arg_tup[i] = (pc_positionals[i].type << 1) | 1;
        }
    }

    /* If there's no entries yet, need to do some allocation. */
    if (entries == 0) {
        cache->arity_caches[num_args - 1].type_ids = mem_sys_allocate(num_args * sizeof(INTVAL) * MD_CACHE_MAX_ENTRIES);
        cache->arity_caches[num_args - 1].results  = mem_sys_allocate(sizeof(PMC *) * MD_CACHE_MAX_ENTRIES);
    }

    /* Add entry. */
    ins_type = entries * num_args;
    for (i = 0; i < num_args; i++)
        cache->arity_caches[num_args - 1].type_ids[ins_type + i] = arg_tup[i];
    cache->arity_caches[num_args - 1].results[entries] = result;
    cache->arity_caches[num_args - 1].num_entries = entries + 1;
}
Ejemplo n.º 2
0
/*

=item C<static PMC * find_in_cache(PARROT_INTERP, NQP_md_cache *cache, PMC *capture, INTVAL num_args)>

Looks for an entry in the multi-dispatch cache.

=cut

*/
static PMC *
find_in_cache(PARROT_INTERP, NQP_md_cache *cache, PMC *capture, INTVAL num_args) {
    INTVAL arg_tup[MD_CACHE_MAX_ARITY];
    INTVAL i, j, entries, t_pos;
    struct Pcc_cell * pc_positionals;
    
    /* If it's zero-arity, return result right off. */
    if (num_args == 0)
        return cache->zero_arity;

    /* Create arg tuple. */
    if (capture->vtable->base_type == enum_class_CallContext)
        GETATTR_CallContext_positionals(interp, capture, pc_positionals);
    else
        return NULL;
    for (i = 0; i < num_args; i++) {
        if (pc_positionals[i].type == BIND_VAL_OBJ) {
            PMC *arg = pc_positionals[i].u.p;
            if (arg->vtable->base_type != smo_id)
                return NULL;
            arg_tup[i] = STABLE(arg)->type_cache_id | (IS_CONCRETE(arg) ? 1 : 0);
        }
        else {
            arg_tup[i] = (pc_positionals[i].type << 1) | 1;
        }
    }

    /* Look through entries. */
    entries = cache->arity_caches[num_args - 1].num_entries;
    t_pos = 0;
    for (i = 0; i < entries; i++) {
        INTVAL match = 1;
        for (j = 0; j < num_args; j++) {
            if (cache->arity_caches[num_args - 1].type_ids[t_pos + j] != arg_tup[j]) {
                match = 0;
                break;
            }
        }
        if (match)
            return cache->arity_caches[num_args - 1].results[i];
        t_pos += num_args;
    }

    return NULL;
}
Ejemplo n.º 3
0
/* Takes a signature along with positional and named arguments and binds them
 * into the provided lexpad (actually, anything that has a Hash interface will
 * do). Returns BIND_RESULT_OK if binding works out, BIND_RESULT_FAIL if there
 * is a failure and BIND_RESULT_JUNCTION if the failure was because of a
 * Junction being passed (meaning we need to auto-thread). */
INTVAL
Rakudo_binding_bind(PARROT_INTERP, PMC *lexpad, PMC *sig_pmc, PMC *capture,
                    INTVAL no_nom_type_check, STRING **error) {
    INTVAL            i, num_pos_args;
    INTVAL            bind_fail   = 0;
    INTVAL            cur_pos_arg = 0;
    Rakudo_Signature *sig         = (Rakudo_Signature *)PMC_data(sig_pmc);
    PMC              *params      = sig->params;
    INTVAL            num_params  = VTABLE_elements(interp, params);
    Rakudo_BindVal    cur_bv;

    /* If we do have some named args, we want to make a clone of the hash
     * to work on. We'll delete stuff from it as we bind, and what we have
     * left over can become the slurpy hash or - if we aren't meant to be
     * taking one - tell us we have a problem. */
    PMC *named_args_copy = PMCNULL;

    /* If we have a |$foo that's followed by slurpies, then we can suppress
     * any future arity checks. */
    INTVAL suppress_arity_fail = 0;
    
    /* If it's a Parrot capture, it may contain natively typed arguments.
     * NOTE: This is a really an encapsulation breakage; if Parrot folks
     * change stuff and this breaks, it's not Parrot's fault. */
    struct Pcc_cell * pc_positionals = NULL;

    /* Set up statics. */
    if (!smo_id)
        setup_binder_statics(interp);

    /* If we've got a CallContext, just has an attribute with list of named
     * parameter names. Otherwise, it's probably a Perl 6 Capture and we need
     * to extract its parts. */
    if (capture->vtable->base_type == enum_class_CallContext) {
        PMC *named_names = VTABLE_get_attr_str(interp, capture, NAMED_str);
        if (!PMC_IS_NULL(named_names)) {
            PMC *iter = VTABLE_get_iter(interp, named_names);
            named_args_copy = Parrot_pmc_new(interp, enum_class_Hash);
            while (VTABLE_get_bool(interp, iter)) {
                STRING *name = VTABLE_shift_string(interp, iter);
                VTABLE_set_pmc_keyed_str(interp, named_args_copy, name,
                        VTABLE_get_pmc_keyed_str(interp, capture, name));
            }
        }
        GETATTR_CallContext_positionals(interp, capture, pc_positionals);
    }
    else if (capture->vtable->base_type == smo_id &&
            STABLE(capture)->type_check(interp, capture, Rakudo_types_capture_get())) {
        PMC *captype   = Rakudo_types_capture_get();
        PMC *list_part = VTABLE_get_attr_keyed(interp, capture, captype, LIST_str);
        PMC *hash_part = VTABLE_get_attr_keyed(interp, capture, captype, HASH_str);
        capture = Rakudo_isnqplist(list_part) 
                    ?  list_part 
                    : Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
        if (hash_part->vtable->base_type == enum_class_Hash) {
            PMC *iter = VTABLE_get_iter(interp, hash_part);
            named_args_copy = Parrot_pmc_new(interp, enum_class_Hash);
            while (VTABLE_get_bool(interp, iter)) {
                STRING *arg_copy_name = VTABLE_shift_string(interp, iter);
                VTABLE_set_pmc_keyed_str(interp, named_args_copy, arg_copy_name,
                    VTABLE_get_pmc_keyed_str(interp, hash_part, arg_copy_name));
            }
        }
    }
    else {
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                "Internal Error: Rakudo_binding_bind passed invalid Capture");
    }

    /* Now we'll walk through the signature and go about binding things. */
    num_pos_args = VTABLE_elements(interp, capture);
    for (i = 0; i < num_params; i++) {
        Rakudo_Parameter *param = (Rakudo_Parameter *)PMC_data(
                VTABLE_get_pmc_keyed_int(interp, params, i));

        /* Is it looking for us to bind a capture here? */
        if (param->flags & SIG_ELEM_IS_CAPTURE) {
            /* Capture the arguments from this point forwards into a Capture.
             * Of course, if there's no variable name we can (cheaply) do pretty
             * much nothing. */
            if (STRING_IS_NULL(param->variable_name)) {
                bind_fail = BIND_RESULT_OK;
            }
            else {
                PMC *captype    = Rakudo_types_capture_get();
                PMC *capsnap    = REPR(captype)->allocate(interp, STABLE(captype));
                PMC *pos_args   = Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
                PMC *named_args = Parrot_pmc_new(interp, enum_class_Hash);
                INTVAL k;
                VTABLE_set_attr_keyed(interp, capsnap, captype, LIST_str, pos_args);
                VTABLE_set_attr_keyed(interp, capsnap, captype, HASH_str, named_args);
                for (k = cur_pos_arg; k < num_pos_args; k++) {
                    cur_bv = get_positional_bind_val(interp, pc_positionals, capture, k);
                    VTABLE_push_pmc(interp, pos_args, cur_bv.type == BIND_VAL_OBJ ?
                        cur_bv.val.o :
                        create_box(interp, cur_bv));
                }
                if (!PMC_IS_NULL(named_args_copy)) {
                    PMC *iter = VTABLE_get_iter(interp, named_args_copy);
                    while (VTABLE_get_bool(interp, iter)) {
                        STRING *name = VTABLE_shift_string(interp, iter);
                        VTABLE_set_pmc_keyed_str(interp, named_args, name,
                            VTABLE_get_pmc_keyed_str(interp, named_args_copy, name));
                    }
                }
                cur_bv.type = BIND_VAL_OBJ;
                cur_bv.val.o = capsnap;
                bind_fail = Rakudo_binding_bind_one_param(interp, lexpad, sig, param, cur_bv,
                        no_nom_type_check, error);
            }
            if (bind_fail) {
                return bind_fail;
            }
            else if (i + 1 == num_params) {
                /* Since a capture acts as "the ultimate slurpy" in a sense, if
                 * this is the last parameter in the signature we can return
                 * success right off the bat. */
                return BIND_RESULT_OK;
            }
            else {
                Rakudo_Parameter *next_param = (Rakudo_Parameter *)PMC_data(
                    VTABLE_get_pmc_keyed_int(interp, params, i + 1));
                if (next_param->flags & (SIG_ELEM_SLURPY_POS | SIG_ELEM_SLURPY_NAMED))
                    suppress_arity_fail = 1;
            }
        }

        /* Could it be a named slurpy? */
        else if (param->flags & SIG_ELEM_SLURPY_NAMED) {
            /* Can cheat a bit if it's the default method %_.
             * We give the hash to the lexpad. */
            if (param->flags & SIG_ELEM_METHOD_SLURPY_NAMED && lexpad->vtable->base_type == p6l_id) {
                SETATTR_Perl6LexPad_default_named_slurpy(interp, lexpad, named_args_copy);
                PARROT_GC_WRITE_BARRIER(interp, lexpad);
            }
            else {
                /* We'll either take the current named arguments copy hash which
                 * will by definition contain all unbound named parameters and use
                 * that, or just create an empty one. */
                PMC *slurpy = PMC_IS_NULL(named_args_copy) ?
                        Parrot_pmc_new(interp, enum_class_Hash) :
                        named_args_copy;
                cur_bv.type = BIND_VAL_OBJ;
                cur_bv.val.o = Rakudo_binding_create_hash(interp, slurpy);
                bind_fail = Rakudo_binding_bind_one_param(interp, lexpad, sig, param,
                        cur_bv, no_nom_type_check, error);
                if (bind_fail)
                    return bind_fail;
            }
            
            /* Nullify named arguments hash now we've consumed it, to mark all
             * is well. */
            named_args_copy = PMCNULL;
        }

        /* Otherwise, maybe it's a positional. */
        else if (PMC_IS_NULL(param->named_names)) {
            /* Slurpy or LoL-slurpy? */
            if (param->flags & (SIG_ELEM_SLURPY_POS | SIG_ELEM_SLURPY_LOL)) {
                /* Create Perl 6 array, create RPA of all remaining things, then
                 * store it. */
                PMC *temp = Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
                while (cur_pos_arg < num_pos_args) {
                    cur_bv = get_positional_bind_val(interp, pc_positionals, capture, cur_pos_arg);
                    VTABLE_push_pmc(interp, temp, cur_bv.type == BIND_VAL_OBJ ?
                        cur_bv.val.o :
                        create_box(interp, cur_bv));
                    cur_pos_arg++;
                }
                cur_bv.type = BIND_VAL_OBJ;
                cur_bv.val.o = param->flags & SIG_ELEM_SLURPY_POS ?
                    Rakudo_binding_create_positional(interp, temp) :
                    Rakudo_binding_create_lol(interp, temp);
                bind_fail = Rakudo_binding_bind_one_param(interp, lexpad, sig, param,
                        cur_bv, no_nom_type_check, error);
                if (bind_fail)
                    return bind_fail;
            }

            /* Otherwise, a positional. */
            else {
                /* Do we have a value?. */
                if (cur_pos_arg < num_pos_args) {
                    /* Easy - just bind that. */
                    cur_bv = get_positional_bind_val(interp, pc_positionals, capture, cur_pos_arg);
                    bind_fail = Rakudo_binding_bind_one_param(interp, lexpad, sig, param,
                            cur_bv, no_nom_type_check, error);
                    if (bind_fail)
                        return bind_fail;
                    cur_pos_arg++;
                }
                else {
                    /* No value. If it's optional, fetch a default and bind that;
                     * if not, we're screwed. Note that we never nominal type check
                     * an optional with no value passed. */
                    if (param->flags & SIG_ELEM_IS_OPTIONAL) {
                        cur_bv.type = BIND_VAL_OBJ;
                        cur_bv.val.o = Rakudo_binding_handle_optional(interp, param, lexpad);
                        bind_fail = Rakudo_binding_bind_one_param(interp, lexpad, sig, param,
                                cur_bv, 0, error);
                        if (bind_fail)
                            return bind_fail;
                    }
                    else {
                        if (error)
                            *error = Rakudo_binding_arity_fail(interp, params, num_params, num_pos_args, 0);
                        return BIND_RESULT_FAIL;
                    }
                }
            }
        }

        /* Else, it's a non-slurpy named. */
        else {
            /* Try and get hold of value. */
            PMC *value = PMCNULL;
            INTVAL num_names = VTABLE_elements(interp, param->named_names);
            INTVAL j;
            if (!PMC_IS_NULL(named_args_copy)) {
                for (j = 0; j < num_names; j++) {
                    STRING *name = VTABLE_get_string_keyed_int(interp, param->named_names, j);
                    value = VTABLE_get_pmc_keyed_str(interp, named_args_copy, name);
                    if (!PMC_IS_NULL(value)) {
                        /* Found a value. Delete entry from to-bind args and stop looking. */
                        VTABLE_delete_keyed_str(interp, named_args_copy, name);
                        break;
                    }
                }
            }

            /* Did we get one? */
            if (PMC_IS_NULL(value)) {
                /* Nope. We'd better hope this param was optional... */
                if (param->flags & SIG_ELEM_IS_OPTIONAL) {
                    cur_bv.type = BIND_VAL_OBJ;
                    cur_bv.val.o = Rakudo_binding_handle_optional(interp, param, lexpad);
                    bind_fail = Rakudo_binding_bind_one_param(interp, lexpad, sig, param,
                            cur_bv, 0, error);
                }
                else if (!suppress_arity_fail) {
                    if (error)
                        *error = Parrot_sprintf_c(interp, "Required named parameter '%S' not passed",
                                VTABLE_get_string_keyed_int(interp, param->named_names, 0));
                    return BIND_RESULT_FAIL;
                }
            }
            else {
                cur_bv.type = BIND_VAL_OBJ;
                cur_bv.val.o = value;
                bind_fail = Rakudo_binding_bind_one_param(interp, lexpad, sig, param,
                        cur_bv, 0, error);
            }

            /* If we got a binding failure, return it. */
            if (bind_fail)
                return bind_fail;
        }
    }

    /* Do we have any left-over args? */
    if (cur_pos_arg < num_pos_args && !suppress_arity_fail) {
        /* Oh noes, too many positionals passed. */
        if (error)
            *error = Rakudo_binding_arity_fail(interp, params, num_params, num_pos_args, 1);
        return BIND_RESULT_FAIL;
    }
    if (!PMC_IS_NULL(named_args_copy) && VTABLE_elements(interp, named_args_copy)) {
        /* Oh noes, unexpected named args. */
        if (error) {
            INTVAL num_extra = VTABLE_elements(interp, named_args_copy);
            PMC *iter        = VTABLE_get_iter(interp, named_args_copy);
            if (num_extra == 1) {
                *error = Parrot_sprintf_c(interp, "Unexpected named parameter '%S' passed",
                        VTABLE_shift_string(interp, iter));
            }
            else {
                INTVAL first  = 1;
                STRING *comma = Parrot_str_new(interp, ", ", 0);
                *error = Parrot_sprintf_c(interp, "%d unexpected named parameters passed (", num_extra);
                while (VTABLE_get_bool(interp, iter)) {
                    STRING *name = VTABLE_shift_string(interp, iter);
                    if (!first)
                        *error = Parrot_str_concat(interp, *error, comma);
                    else
                        first = 0;
                    *error = Parrot_str_concat(interp, *error, name);
                }
                *error = Parrot_str_concat(interp, *error, Parrot_str_new(interp, ")", 0));
            }
        }
        return BIND_RESULT_FAIL;
    }

    /* If we get here, we're done. */
    return BIND_RESULT_OK;
}
Ejemplo n.º 4
0
/* Compile time trial binding; tries to determine at compile time whether
 * certain binds will/won't work. */
INTVAL Rakudo_binding_trial_bind(PARROT_INTERP, PMC *sig_pmc, PMC *capture) {
    INTVAL            i, num_pos_args, got_prim;
    INTVAL            cur_pos_arg = 0;
    Rakudo_Signature *sig         = (Rakudo_Signature *)PMC_data(sig_pmc);
    PMC              *params      = sig->params;
    INTVAL            num_params  = VTABLE_elements(interp, params);

    /* Grab arguments. */
    struct Pcc_cell * pc_positionals = NULL;
    if (capture->vtable->base_type == enum_class_CallContext)
        GETATTR_CallContext_positionals(interp, capture, pc_positionals);
    else
        return TRIAL_BIND_NOT_SURE;

    /* Set up statics. */
    if (!smo_id)
        setup_binder_statics(interp);
        
    /* Walk through the signature and consider the parameters. */
    num_pos_args = VTABLE_elements(interp, capture);
    for (i = 0; i < num_params; i++) {
        Rakudo_Parameter *param = (Rakudo_Parameter *)PMC_data(
                VTABLE_get_pmc_keyed_int(interp, params, i));
        
        /* If the parameter is anything other than a boring old
         * required positional parameter, we won't analyze it. */
        if (param->flags & ~(
                SIG_ELEM_MULTI_INVOCANT | SIG_ELEM_IS_PARCEL |
                SIG_ELEM_IS_COPY | SIG_ELEM_ARRAY_SIGIL |
                SIG_ELEM_HASH_SIGIL | SIG_ELEM_NATIVE_VALUE))
            return TRIAL_BIND_NOT_SURE;
        if (!PMC_IS_NULL(param->named_names))
            return TRIAL_BIND_NOT_SURE;
        if (!PMC_IS_NULL(param->post_constraints))
            return TRIAL_BIND_NOT_SURE;
        if (!PMC_IS_NULL(param->type_captures))
            return TRIAL_BIND_NOT_SURE;

        /* Did we pass too few required arguments? If so, fail. */
        if (cur_pos_arg >= num_pos_args)
            return TRIAL_BIND_NO_WAY;
            
        /* Otherwise, need to consider type. */
        got_prim = pc_positionals[cur_pos_arg].type;
        if (param->flags & SIG_ELEM_NATIVE_VALUE) {
            if (got_prim == BIND_VAL_OBJ) {
                /* We got an object; if we aren't sure we can unbox, we can't
                 * be sure about the dispatch. */
                PMC *arg = pc_positionals[cur_pos_arg].u.p;
                storage_spec spec = REPR(arg)->get_storage_spec(interp, STABLE(arg));
                switch (param->flags & SIG_ELEM_NATIVE_VALUE) {
                    case SIG_ELEM_NATIVE_INT_VALUE:
                        if (!(spec.can_box & STORAGE_SPEC_CAN_BOX_INT))
                            return TRIAL_BIND_NOT_SURE;
                        break;
                    case SIG_ELEM_NATIVE_NUM_VALUE:
                        if (!(spec.can_box & STORAGE_SPEC_CAN_BOX_NUM))
                            return TRIAL_BIND_NOT_SURE;
                        break;
                    case SIG_ELEM_NATIVE_STR_VALUE:
                        if (!(spec.can_box & STORAGE_SPEC_CAN_BOX_STR))
                            return TRIAL_BIND_NOT_SURE;
                        break;
                    default:
                        /* WTF... */
                        return TRIAL_BIND_NOT_SURE;
                }
            }
            else {
                /* If it's the wrong type of native, there's no way it
                 * can ever bind. */
                if ((param->flags & SIG_ELEM_NATIVE_INT_VALUE) && got_prim != BIND_VAL_INT ||
                    (param->flags & SIG_ELEM_NATIVE_NUM_VALUE) && got_prim != BIND_VAL_NUM ||
                    (param->flags & SIG_ELEM_NATIVE_STR_VALUE) && got_prim != BIND_VAL_STR)
                    return TRIAL_BIND_NO_WAY;
            }
        }
        else {
            /* Work out a parameter type to consider, and see if it matches. */
            PMC * const arg =
                got_prim == BIND_VAL_OBJ ? pc_positionals[cur_pos_arg].u.p :
                got_prim == BIND_VAL_INT ? Rakudo_types_int_get() :
                got_prim == BIND_VAL_NUM ? Rakudo_types_num_get() :
                                           Rakudo_types_str_get();
            if (param->nominal_type != Rakudo_types_mu_get() &&
                    !STABLE(arg)->type_check(interp, arg, param->nominal_type)) {
                /* If it failed because we got a junction, may auto-thread;
                 * hand back "not sure" for now. */
                if (STABLE(arg)->WHAT == Rakudo_types_junction_get())
                    return TRIAL_BIND_NOT_SURE;
                
                /* It failed to, but that doesn't mean it can't work at runtime;
                 * we perhaps want an Int, and the most we know is we have an Any,
                 * which would include Int. However, the Int ~~ Str case can be
                 * rejected now, as there's no way it'd ever match. Basically, we
                 * just flip the type check around. */
                return STABLE(param->nominal_type)->type_check(interp, param->nominal_type, arg) ?
                    TRIAL_BIND_NOT_SURE : TRIAL_BIND_NO_WAY;
            }
        }

        /* Continue to next argument. */
        cur_pos_arg++;
    }

    /* If we have any left over arguments, it's a binding fail. */
    if (cur_pos_arg < num_pos_args)
        return TRIAL_BIND_NO_WAY;

    /* Otherwise, if we get there, all is well. */
    return TRIAL_BIND_OK;
}
Ejemplo n.º 5
0
static PMC* find_best_candidate(PARROT_INTERP, Rakudo_md_candidate_info **candidates,
                                INTVAL num_candidates, PMC *capture, opcode_t *next,
                                PMC *dispatcher, INTVAL many) {
    Rakudo_md_candidate_info **cur_candidate    = candidates;
    Rakudo_md_candidate_info **possibles        = mem_allocate_n_typed(num_candidates + 1, Rakudo_md_candidate_info *);
    PMC                       *junctional_res   = PMCNULL;
    PMC                       *many_res         = many ? Parrot_pmc_new(interp, enum_class_ResizablePMCArray) : PMCNULL;
    const INTVAL               num_args         = VTABLE_elements(interp, capture);
    INTVAL                     possibles_count  = 0;
    INTVAL                     pure_type_result = 1;
    INTVAL                     type_check_count;
    INTVAL                     type_mismatch;

    /* We expect a Parrot capture in the multi-dispatcher, always. */
    struct Pcc_cell * pc_positionals = NULL;
    if (capture->vtable->base_type == enum_class_CallContext) {
        GETATTR_CallContext_positionals(interp, capture, pc_positionals);
    }
    else {
        mem_sys_free(possibles);
        Parrot_ex_throw_from_c_args(interp, next, 1,
            "INTERNAL ERROR: multi-dispatcher must be given a low level capture");
    }

    /* Iterate over the candidates and collect best ones; terminate
     * when we see two nulls (may break out earlier). */
    while (1) {
        INTVAL i;

        if (*cur_candidate == NULL) {
            /* We've hit the end of a tied group now. If any of them have a
             * bindability check requirement, we'll do any of those now. */
            if (possibles_count) {
                Rakudo_md_candidate_info **new_possibles = NULL;
                INTVAL new_possibles_count = 0;
                INTVAL i;

                for (i = 0; i < possibles_count; i++) {
                    Parrot_pcc_set_signature(interp, CURRENT_CONTEXT(interp), NULL);

                    /* First, if there's a required named parameter and it was
                     * not passed, we can very quickly eliminate this candidate
                     * without doing a full bindability check. */
                    if (possibles[i]->req_named) {
                        if (!VTABLE_exists_keyed_str(interp, capture, possibles[i]->req_named)) {
                            /* Required named arg not passed, so we eliminate
                             * it right here. Flag that we've built a list of
                             * new possibles, and that this was not a pure
                             * type-based result that we can cache. */
                            if (!new_possibles)
                                new_possibles = mem_allocate_n_typed(num_candidates, Rakudo_md_candidate_info *);
                            pure_type_result = 0;
                            continue;
                        }
                    }

                    /* Otherwise, may need full bind check. */
                    if (possibles[i]->bind_check) {
                        /* We'll invoke the sub (but not re-enter the runloop)
                         * and then attempt to bind the signature. */
                        PMC      *cthunk, *lexpad, *sig;
                        opcode_t *where;
                        INTVAL    bind_check_result;
                        Rakudo_Code *code_obj = (Rakudo_Code *)PMC_data(possibles[i]->sub);
                        cthunk = Parrot_pmc_getprop(interp, code_obj->_do,
                            Parrot_str_new(interp, "COMPILER_THUNK", 0));
                        if (!PMC_IS_NULL(cthunk)) {
                            /* We need to do the tie-break on something not yet compiled.
                             * Get it compiled. */
                            Parrot_ext_call(interp, cthunk, "->");
                        }

                        Parrot_pcc_reuse_continuation(interp, CURRENT_CONTEXT(interp), next);
                        where  = VTABLE_invoke(interp, possibles[i]->sub, next);
                        lexpad = Parrot_pcc_get_lex_pad(interp, CURRENT_CONTEXT(interp));
                        sig    = possibles[i]->signature;
                        bind_check_result = Rakudo_binding_bind(interp, lexpad,
                              sig, capture, 0, NULL);
                        where = VTABLE_invoke(interp, Parrot_pcc_get_continuation(interp, CURRENT_CONTEXT(interp)), where);

                        /* If we haven't got a possibles storage space, allocate it now. */
                        if (!new_possibles)
                            new_possibles = mem_allocate_n_typed(num_candidates, Rakudo_md_candidate_info *);

                        /* If we don't fail, need to put this one onto the list
                         * (note that needing a junction dispatch is OK). */
                        if (bind_check_result != BIND_RESULT_FAIL) {
                            new_possibles[new_possibles_count] = possibles[i];
                            new_possibles_count++;
                        }

                        /* Since we had to do a bindability check, this is not
                         * a result we can cache on nominal type. */
                        pure_type_result = 0;
                    }
                    
                    /* Otherwise, it's just nominal; accept it. */
                    else {
                        if (!new_possibles)
                            new_possibles = mem_allocate_n_typed(num_candidates, Rakudo_md_candidate_info *);
                        new_possibles[new_possibles_count] = possibles[i];
                        new_possibles_count++;
                    }
                }

                /* If we have an updated list of possibles, free old one and use this
                 * new one from here on in. */
                if (new_possibles) {
                    mem_sys_free(possibles);
                    possibles = new_possibles;
                    possibles_count = new_possibles_count;
                }
            }

            /* Now we have eliminated any that fail the bindability check.
             * See if we need to push it onto the many list and continue.
             * Otherwise, we have the result we were looking for. */
            if (many) {
                for (i = 0; i < possibles_count; i++)
                    VTABLE_push_pmc(interp, many_res, possibles[i]->sub);
                possibles_count = 0;
            }
            else if (possibles_count) {
                break;
            }
            
            /* Keep looping and looking, unless we really hit the end. */
            if (cur_candidate[1]) {
                cur_candidate++;
                continue;
            }
            else {
                break;
            }
        }