bool
ASTNode::check_arglist (const char *funcname, ASTNode::ref arg,
                        const char *formals, bool coerce)
{
    // std::cerr << "ca " << funcname << " formals='" << formals << "\n";
    for ( ;  arg;  arg = arg->next()) {
        if (! *formals)   // More formal args, but no more actual args
            return false;
        if (*formals == '*')  // Will match anything left
            return true;
        if (*formals == '.') {  // Special case for token/value pairs
            // FIXME -- require that the tokens be string literals
            if (arg->typespec().is_string() && arg->next() != NULL) {
                arg = arg->next();
                continue;
            }
            return false;
        }
        if (*formals == '?') {
            if (formals[1] == '[' && formals[2] == ']') {
                // Any array
                formals += 3;
                if (arg->typespec().is_array())
                    continue;  // match
                else return false;  // wanted an array, didn't get one
            }
            if (arg->typespec().is_array())
                return false;   // wanted any scalar, got an array
            formals += 1;
            continue;  // match anything
        }

        TypeSpec argtype = arg->typespec();
        int advance;
        TypeSpec formaltype = m_compiler->type_from_code (formals, &advance);
        formals += advance;
        // std::cerr << "\targ is " << argtype.string() 
        //           << ", formal is " << formaltype.string() << "\n";
        if (argtype == formaltype)
            continue;   // ok, move on to next arg
        if (coerce && assignable (formaltype, argtype))
            continue;
        // Allow a fixed-length array match to a formal array with
        // unspecified length, if the element types are the same.
        if (formaltype.arraylength() < 0 && argtype.arraylength() &&
              formaltype.elementtype() == argtype.elementtype())
            continue;

        // anything that gets this far we don't consider a match
        return false;
    }
    if (*formals && *formals != '*' && *formals != '.') 
        return false;  // Non-*, non-... formals expected, no more actuals

    return true;  // Is this safe?
}
TypeSpec
ASTfunction_call::typecheck (TypeSpec expected)
{
    typecheck_children ();

    bool match = false;

    // Look for an exact match, including expected return type
    m_typespec = typecheck_all_poly (expected, false);
    if (m_typespec != TypeSpec())
        match = true;

    // Now look for an exact match on args, but any assignable return type
    if (! match && expected != TypeSpec()) {
        m_typespec = typecheck_all_poly (TypeSpec(), false);
        if (m_typespec != TypeSpec())
            match = true;
    }

    // Now look for a coercible match of args, exact march on return type
    if (! match) {
        m_typespec = typecheck_all_poly (expected, true);
        if (m_typespec != TypeSpec())
            match = true;
    }

    // All that failed, try for a coercible match on everything
    if (! match && expected != TypeSpec()) {
        m_typespec = typecheck_all_poly (TypeSpec(), true);
        if (m_typespec != TypeSpec())
            match = true;
    }

    if (match) {
        if (! is_user_function ())
            typecheck_builtin_specialcase ();
        return m_typespec;
    }

    // Couldn't find any way to match any polymorphic version of the
    // function that we know about.  OK, at least try for helpful error
    // message.
    std::string choices ("");
    for (FunctionSymbol *poly = func();  poly;  poly = poly->nextpoly()) {
        const char *code = poly->argcodes().c_str();
        int advance;
        TypeSpec returntype = m_compiler->type_from_code (code, &advance);
        code += advance;
        if (choices.length())
            choices += "\n";
        choices += Strutil::format ("\t%s %s (%s)",
                              type_c_str(returntype), m_name.c_str(),
                              m_compiler->typelist_from_code(code).c_str());
    }

    std::string actualargs;
    for (ASTNode::ref arg = args();  arg;  arg = arg->next()) {
        if (actualargs.length())
            actualargs += ", ";
        actualargs += arg->typespec().string();
    }

    error ("No matching function call to '%s (%s)'\n    Candidates are:\n%s", 
           m_name.c_str(), actualargs.c_str(), choices.c_str());
    return TypeSpec();
}