/***** * application.h * Andy Hammerlindl 2005/05/20 * * An application is a matching of arguments in a call expression to formal * parameters of a function. Since the language allows default arguments, * keyword arguments, rest arguments, and anything else we think of, this * is not a simple mapping. *****/ #ifndef APPLICATION_H #define APPLICATION_H #include "common.h" #include "types.h" #include "coenv.h" #include "exp.h" // Defined in runtime.in: namespace run { void pushDefault(vm::stack *Stack); } using absyntax::arglist; using absyntax::varinit; using absyntax::arrayinit; using absyntax::tempExp; // This is mid-way between trans and absyntax. namespace trans { typedef Int score; typedef mem::vector score_vector; // This is used during the translation of arguments to store temporary // expressions for arguments that need to be translated for side-effects at a // certain point but used later on. The invariant maintained is that if the // vector has n elements, then the side-effects for the first n arguments have // been translated. Null is pushed onto the vector to indicate that the // expression was evaluated directly onto the stack, without the use of a // temporary. typedef mem::vector temp_vector; struct arg : public gc { types::ty *t; arg(types::ty *t) : t(t) {} virtual ~arg() {} virtual void trans(coenv &e, temp_vector &) = 0; }; struct varinitArg : public arg { varinit *v; varinitArg(varinit *v, types::ty *t) : arg(t), v(v) {} virtual void trans(coenv &e, temp_vector &) { // Open signatures can match overloaded variables, but there is no way to // translate the result, so report an error. if (t->kind == types::ty_overloaded) { em.error(v->getPos()); em << "overloaded argument in function call"; } else v->transToType(e, t); } }; // Pushes a default argument token on the stack as a placeholder for the // argument. struct defaultArg : public arg { defaultArg(types::ty *t) : arg(t) {} virtual void trans(coenv &e, temp_vector &) { //e.c.encode(inst::builtin, run::pushDefault); e.c.encode(inst::push_default); } }; // Handles translation of all the arguments matched to the rest formal. // NOTE: This code duplicates a lot of arrayinit. struct restArg : public gc { mem::list inits; arg *rest; public: restArg() : rest(0) {} virtual ~restArg() {} // Encodes the instructions to make an array from size elements on the stack. static void transMaker(coenv &e, Int size, bool rest); void trans(coenv &e, temp_vector &temps); void add(arg *init) { inits.push_back(init); } void addRest(arg *init) { rest=init; } }; // This class generates sequenced args, args whose side-effects occur in order // according to their index, regardless of the order they are called. This is // used to ensure left-to-right order of evaluation of keyword arguments, even // if they are given out of the order specified in the declaration. class sequencer { struct sequencedArg : public varinitArg { sequencer &parent; size_t i; sequencedArg(varinit *v, types::ty *t, sequencer &parent, size_t i) : varinitArg(v, t), parent(parent), i(i) {} void trans(coenv &e, temp_vector &temps) { parent.trans(e, i, temps); } }; typedef mem::vector sa_vector; sa_vector args; // Makes a temporary for the next argument in the sequence. void alias(coenv &e, temp_vector &temps) { size_t n=temps.size(); assert(n < args.size()); sequencedArg *sa=args[n]; assert(sa); temps.push_back(new tempExp(e, sa->v, sa->t)); } // Get in a state to translate the i-th argument, aliasing any arguments that // occur before it in the sequence. void advance(coenv &e, size_t i, temp_vector &temps) { while (temps.size() < i) alias(e,temps); } void trans(coenv &e, size_t i, temp_vector &temps) { if (i < temps.size()) { // Already translated, use the alias. assert(temps[i]); temps[i]->trans(e); } else { // Alias earlier args if necessary. advance(e, i, temps); // Translate using the base method. args[i]->varinitArg::trans(e,temps); // Push null to indicate the argument has been translated. temps.push_back(0); } } public: arg *addArg(varinit *v, types::ty *t, size_t i) { if (args.size() <= i) args.resize(i+1); return args[i]=new sequencedArg(v, t, *this, i); } }; class application : public gc { types::signature *sig; types::function *t; // Sequencer to ensure given arguments are evaluated in the proper order. // Use of this sequencer means that transArgs can only be called once. sequencer seq; typedef mem::vector arg_vector; arg_vector args; restArg *rest; // Target formal to match with arguments to be packed into the rest array. types::formal rf; // During the matching of arguments to an application, this stores the index // of the first unmatched formal. size_t index; // To resolve which is the best application in case of multiple matches of // overloaded functions, a score is kept for every source argument matched, // and an application with higher-scoring matches is chosen. score_vector scores; void initRest(); application(types::signature *sig) : sig(sig), t(0), args(sig->formals.size()), rest(0), rf(0), index(0) { assert(sig); initRest(); } application(types::function *t) : sig(t->getSignature()), t(t), args(sig->formals.size()), rest(0), rf(0), index(0) { assert(sig); initRest(); } types::formal &getTarget() { return sig->getFormal(index); } // Move the index forward one, then keep going until we're at an unmatched // argument. void advanceIndex() { do { ++index; } while (index < args.size() && args[index]!=0); } // Finds the first unmatched formal of the given name, returning the index. // The rest formal is not tested. This function returns FAIL if no formals // match. Int find(symbol name); // Match the formal at index to its default argument (if it has one). bool matchDefault(); // Match the argument to the formal indexed by spot. bool matchAtSpot(size_t spot, env &e, types::formal &source, varinit *a, size_t evalIndex); // Match the argument to be packed into the rest array, if possible. bool matchArgumentToRest(env &e, types::formal& source, varinit *a, size_t evalIndex); // Matches the argument to a formal in the target signature (possibly causing // other formals in the target to be matched to default values), and updates // the matchpoint accordingly. bool matchArgument(env &e, types::formal& source, varinit *a, size_t evalIndex); // Match an argument bound to a name, as in f(index=7). bool matchNamedArgument(env &e, types::formal& source, varinit *a, size_t evalIndex); // After all source formals have been matched, checks if the match is // complete (filling in final default values if necessary). bool complete(); // Match a rest argument in the calling expression. bool matchRest(env &e, types::formal& f, varinit *a, size_t evalIndex); // Match the argument represented in signature to the target signature. On // success, all of the arguments in args will be properly set up. bool matchSignature(env &e, types::signature *source, arglist &al); // Match a signature which is open, meaning that any sequence of arguments is // matched. bool matchOpen(env &e, signature *source, arglist &al); friend class maximizer; public: // Attempt to build an application given the target signature and the source // signature (representing the given arguments). Return 0 if they cannot be // matched. static application *match(env &e, types::function *t, types::signature *source, arglist &al); // Translate the arguments so they appear in order on the stack in // preparation for a call. void transArgs(coenv &e); types::function *getType() { return t; } // This returns true in the special case that the arguments matched without // casting or packing into the rest formal. bool exact(); // The next best thing (score-wise) to an exact match. This returns true if // there are two arguments, one of which is cast and one is matched exactly // and neither are packed into the rest argument. bool halfExact(); }; typedef mem::list app_list; // Given an overloaded list of types, determines which type to call. If none // are applicable, returns an empty vector, if there is ambiguity, several will // be returned. app_list multimatch(env &e, types::overloaded *o, types::signature *source, arglist &al); } // namespace trans #endif