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?
}
void
OSLCompilerImpl::write_oso_symbol (const Symbol *sym)
{
    // symtype / datatype / name
    oso ("%s\t%s\t%s", sym->symtype_shortname(),
         type_c_str(sym->typespec()), sym->mangled().c_str());

    ASTvariable_declaration *v = NULL;
    if (sym->node() && sym->node()->nodetype() == ASTNode::variable_declaration_node)
        v = static_cast<ASTvariable_declaration *>(sym->node());

    // Print default values
    bool isparam = (sym->symtype() == SymTypeParam ||
                    sym->symtype() == SymTypeOutputParam);
    if (sym->symtype() == SymTypeConst) {
        oso ("\t");
        write_oso_const_value (static_cast<const ConstantSymbol *>(sym));
        oso ("\t");
    } else if (v && isparam) {
        std::string out;
        v->param_default_literals (sym, out);
        oso ("\t%s\t", out.c_str());
    }

    //
    // Now output all the hints, which is most of the work!
    //

    int hints = 0;

    // %meta{} encodes metadata (handled by write_oso_metadata)
    if (v) {
        ASSERT (v);
        for (ASTNode::ref m = v->meta();  m;  m = m->next()) {
            if (hints++ == 0)
                oso ("\t");
            write_oso_metadata (m.get());
        }
    }

    // %read and %write give the range of ops over which a symbol is used.
    if (hints++ == 0)
        oso ("\t");
    oso (" %%read{%d,%d} %%write{%d,%d}", sym->firstread(), sym->lastread(),
         sym->firstwrite(), sym->lastwrite());

    // %struct, %structfields, and %structfieldtypes document the
    // definition of a structure and which other symbols comprise the
    // individual fields.
    if (sym->typespec().is_structure()) {
        if (hints++ == 0)
            oso ("\t");
        const StructSpec *structspec (sym->typespec().structspec());
        std::string fieldlist, signature;
        for (int i = 0;  i < (int)structspec->numfields();  ++i) {
            if (i > 0)
                fieldlist += ",";
            fieldlist += structspec->field(i).name.string();
            signature += code_from_type (structspec->field(i).type);
        }
        oso (" %%struct{\"%s\"} %%structfields{%s} %%structfieldtypes{\"%s\"} %%structnfields{%d}",
             structspec->mangled().c_str(), fieldlist.c_str(),
             signature.c_str(), structspec->numfields());
    }
    // %mystruct and %mystructfield document the symbols holding structure
    // fields, linking them back to the structures they are part of.
    if (sym->fieldid() >= 0) {
        if (hints++ == 0)
            oso ("\t");
        ASTvariable_declaration *vd = (ASTvariable_declaration *) sym->node();
        oso (" %%mystruct{%s} %%mystructfield{%d}",
             vd->sym()->mangled().c_str(), sym->fieldid());
    }

    // %derivs hint marks symbols that need to carry derivatives
    if (sym->has_derivs()) {
        if (hints++ == 0)
            oso ("\t");
        oso (" %%derivs");
    }

#if 0 // this is recomputed by the runtime optimizer, no need to bloat the .oso with these

    // %depends marks, for potential OUTPUTs, which symbols they depend
    // upon.  This is so that derivativeness, etc., may be
    // back-propagated as shader networks are linked together.
    if (isparam || sym->symtype() == SymTypeGlobal) {
        // FIXME
        const SymPtrSet &deps (m_symdeps[sym]);
        std::vector<const Symbol *> inputdeps;
        BOOST_FOREACH (const Symbol *d, deps)
            if (d->symtype() == SymTypeParam ||
                  d->symtype() == SymTypeOutputParam ||
                  d->symtype() == SymTypeGlobal ||
                  d->symtype() == SymTypeLocal ||
                  d->symtype() == SymTypeTemp)
                inputdeps.push_back (d);
        if (inputdeps.size()) {
            if (hints++ == 0)
                oso ("\t");
            oso (" %%depends{");
            int deps = 0;
            for (size_t i = 0;  i < inputdeps.size();  ++i) {
                if (inputdeps[i]->symtype() == SymTypeTemp &&
                    inputdeps[i]->dealias() != inputdeps[i])
                    continue;   // Skip aliased temporaries
                if (deps++)
                    oso (",");
                oso ("%s",  inputdeps[i]->mangled().c_str());
            }
            oso ("}");
        }
    }
#endif
    oso ("\n");
}
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();
}
void
OSLCompilerImpl::write_oso_file (const std::string &outfilename,
                                 string_view options)
{
    ASSERT (m_osofile != NULL && m_osofile->good());
    oso ("OpenShadingLanguage %d.%02d\n",
         OSO_FILE_VERSION_MAJOR, OSO_FILE_VERSION_MINOR);
    oso ("# Compiled by oslc %s\n", OSL_LIBRARY_VERSION_STRING);
    oso ("# options: %s\n", options);

    ASTshader_declaration *shaderdecl = shader_decl();
    oso ("%s %s", shaderdecl->shadertypename(), 
         shaderdecl->shadername().c_str());

    // output global hints and metadata
    int hints = 0;
    for (ASTNode::ref m = shaderdecl->metadata();  m;  m = m->next()) {
        if (hints++ == 0)
            oso ("\t");
        write_oso_metadata (m.get());
    }

    oso ("\n");

    // Output params, so they are first
    BOOST_FOREACH (const Symbol *s, symtab()) {
        if (s->symtype() == SymTypeParam || s->symtype() == SymTypeOutputParam)
            write_oso_symbol (s);
    }
    // Output globals, locals, temps, const
    BOOST_FOREACH (const Symbol *s, symtab()) {
        if (s->symtype() == SymTypeLocal || s->symtype() == SymTypeTemp ||
            s->symtype() == SymTypeGlobal || s->symtype() == SymTypeConst) {
            // Don't bother writing symbols that are never used
            if (s->lastuse() >= 0) {
                write_oso_symbol (s);
            }
        }
    }

    // Output all opcodes
    int lastline = -1;
    ustring lastfile;
    ustring lastmethod ("___uninitialized___");
    for (OpcodeVec::iterator op = m_ircode.begin(); op != m_ircode.end();  ++op) {
        if (lastmethod != op->method()) {
            oso ("code %s\n", op->method().c_str());
            lastmethod = op->method();
            lastfile = ustring();
            lastline = -1;
        }

        if (/*m_debug &&*/ op->sourcefile()) {
            ustring file = op->sourcefile();
            int line = op->sourceline();
            if (file != lastfile || line != lastline)
                oso ("# %s:%d\n# %s\n", file.c_str(), line,
                     retrieve_source (file, line).c_str());
        }

        // Op name
        oso ("\t%s", op->opname().c_str());

        // Register arguments
        if (op->nargs())
            oso (op->opname().length() < 8 ? "\t\t" : "\t");
        for (int i = 0;  i < op->nargs();  ++i) {
            int arg = op->firstarg() + i;
            oso ("%s ", m_opargs[arg]->dealias()->mangled().c_str());
        }

        // Jump targets
        for (size_t i = 0;  i < Opcode::max_jumps;  ++i)
            if (op->jump(i) >= 0)
                oso ("%d ", op->jump(i));

        //
        // Opcode Hints
        //

        bool firsthint = true;

        // %filename and %line document the source code file and line that
        // contained code that generated this op.  To avoid clutter, we
        // only output these hints when they DIFFER from the previous op.
        if (op->sourcefile()) {
            if (op->sourcefile() != lastfile) {
                lastfile = op->sourcefile();
                oso ("%c%%filename{\"%s\"}", firsthint ? '\t' : ' ', lastfile.c_str());
                firsthint = false;
            }
            if (op->sourceline() != lastline) {
                lastline = op->sourceline();
                oso ("%c%%line{%d}", firsthint ? '\t' : ' ', lastline);
                firsthint = false;
            }
        }

        // %argrw documents which arguments are read, written, or both (rwW).
        if (op->nargs()) {
            oso ("%c%%argrw{\"", firsthint ? '\t' : ' ');
            for (int i = 0;  i < op->nargs();  ++i) {
                if (op->argwrite(i))
                    oso (op->argread(i) ? "W" : "w");
                else
                    oso (op->argread(i) ? "r" : "-");
            }
            oso ("\"}");
            firsthint = false;
        }

        // %argderivs documents which arguments have derivs taken of
        // them by the op.
        if (op->argtakesderivs_all()) {
            oso (" %%argderivs{");
            int any = 0;
            for (int i = 0;  i < op->nargs();  ++i)
                if (op->argtakesderivs(i)) {
                    if (any++)
                        oso (",");
                    oso ("%d", i);
                }
            oso ("}");
            firsthint = false;
        }

        oso ("\n");
    }

    if (lastmethod != main_method_name()) // If no code, still need a code marker
        oso ("code %s\n", main_method_name().c_str());

    oso ("\tend\n");
    m_osofile = NULL;
}