/*****
* application.cc
* 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.
*****/
using namespace types;
using absyntax::varinit;
using absyntax::arrayinit;
using absyntax::arglist;
namespace trans {
// Lower scores are better. Packed is added onto the other qualifiers so
// we may score both exact and casted packed arguments.
const score FAIL=0, EXACT=1, CAST=2;
const score PACKED=2;
void restArg::transMaker(coenv &e, Int size, bool rest) {
// Push the number of cells and call the array maker.
e.c.encode(inst::intpush, size);
e.c.encode(inst::builtin, rest ? run::newAppendedArray :
run::newInitializedArray);
}
void restArg::trans(coenv &e, temp_vector &temps)
{
// Push the values on the stack.
for (mem::list<arg *>::iterator p = inits.begin(); p != inits.end(); ++p)
(*p)->trans(e, temps);
if (rest)
rest->trans(e, temps);
transMaker(e, (Int)inits.size(), (bool)rest);
}
class maximizer {
app_list l;
// Tests if x is as good (or better) an application as y.
bool asgood(application *x, application *y) {
// Matches to open signatures are always worse than matches to normal
// signatures.
if (x->sig->isOpen)
return y->sig->isOpen;
else if (y->sig->isOpen)
return true;
assert (x->scores.size() == y->scores.size());
// Test if each score in x is no higher than the corresponding score in
// y.
return std::equal(x->scores.begin(), x->scores.end(), y->scores.begin(),
std::less_equal<score>());
}
// Add an application that has already been determined to be maximal.
// Remove any other applications that are now not maximal because of its
// addition.
void addMaximal(application *x) {
app_list::iterator y=l.begin();
while (y!=l.end())
if (better(x,*y))
y=l.erase(y);
else
++y;
l.push_front(x);
}
// Tests if x is maximal.
bool maximal(application *x) {
for (app_list::iterator y=l.begin(); y!=l.end(); ++y)
if (better(*y,x))
return false;
return true;
}
public:
maximizer() {}
void add(application *x) {
if (maximal(x))
addMaximal(x);
}
app_list result() {
return l;
}
};
ty *restCellType(signature *sig) {
formal& f=sig->getRest();
if (f.t) {
array *a=dynamic_cast<array *>(f.t);
if (a)
return a->celltype;
}
return 0;
}
void application::initRest() {
formal& f=sig->getRest();
if (f.t) {
ty *ct = restCellType(sig);
if (!ct)
vm::error("formal rest argument must be an array");
if (index==args.size())
// Try to pack into the rest array.
return matchArgumentToRest(e, source, a, evalIndex);
else
// Match here, or failing that use a default and try to match at the next
// spot.
return matchAtSpot(index, e, source, a, evalIndex) ||
(matchDefault() && matchArgument(e, source, a, evalIndex));
}
Int spot=find(source.name);
return spot!=NOMATCH && matchAtSpot(spot, e, source, a, evalIndex);
}
bool application::complete() {
if (index==args.size())
return true;
else if (matchDefault())
return complete();
else
return false;
}
bool application::matchRest(env &e, formal &source, varinit *a,
size_t evalIndex) {
// First make sure all non-rest arguments are matched (matching to defaults
// if necessary).
if (complete())
// Match rest to rest.
if (rest) {
formal &target=sig->getRest();
score s=castScore(e, target, source);
if (s!=FAIL) {
rest->addRest(seq.addArg(a, target.t, evalIndex));
scores.push_back(s);
return true;
}
}
return false;
}
// When the argument should be evaluated, possibly adjusting for a rest
// argument which occurs before named arguments.
size_t adjustIndex(size_t i, size_t ri)
{
return i < ri ? i : i+1;
}
size_t ri = al.rest.val ? al.restPosition : f.size();
// First, match all of the named (non-rest) arguments.
for (size_t i=0; i<f.size(); ++i)
if (f[i].name)
if (!matchNamedArgument(e, f[i], al[i].val, adjustIndex(i,ri)))
return false;
// Then, the unnamed.
for (size_t i=0; i<f.size(); ++i)
if (!f[i].name)
if (!matchArgument(e, f[i], al[i].val, adjustIndex(i,ri)))
return false;
// Then, the rest argument.
if (source->hasRest())
if (!matchRest(e, source->getRest(), al.rest.val, ri))
return false;
// Fill in any remaining arguments with their defaults.
return complete();
}
// Pack all given parameters into the rest argument.
formal_vector &f=source->formals;
for (size_t i = 0; i < f.size(); ++i)
if (al[i].name)
// Named arguments are not handled by open signatures.
return false;
else
rest->add(seq.addArg(al[i].val, f[i].t, i));
if (source->hasRest())
rest->addRest(new varinitArg(al.rest.val, source->getRest().t));
for(arg_vector::iterator a=args.begin(); a != args.end(); ++a)
(*a)->trans(e,temps);
if (rest)
rest->trans(e,temps);
}
bool application::exact() {
if (sig->isOpen)
return false;
for (score_vector::iterator p = scores.begin(); p != scores.end(); ++p)
if (*p != EXACT)
return false;
return true;
}
bool application::halfExact() {
if (sig->isOpen)
return false;
if (scores.size() != 2)
return false;
if (scores[0] == EXACT && scores[1] == CAST)
return true;
if (scores[0] == CAST && scores[1] == EXACT)
return true;
return false;
}
// True if any of the formals have names.
bool namedFormals(signature *sig)
{
formal_vector& formals = sig->formals;
size_t n = formals.size();
for (size_t i = 0; i < n; ++i) {
if (formals[i].name)
return true;
}
return false;
}
// Tests if arguments in the source signature can be matched to the formals
// in the target signature with no casting or packing.
// This allows overloaded args, but not named args.
bool exactMightMatch(signature *target, signature *source)
{
// Open signatures never exactly match.
if (target->isOpen)
return false;
// Sizes of the two lists.
size_t fn = formals.size(), an = args.size();
// Indices for the two lists.
size_t fi = 0, ai = 0;
while (fi < fn && ai < an) {
if (equivalent(formals[fi].t, args[ai].t)) {
// Arguments match, move to the next.
++fi; ++ai;
} else if (formals[fi].defval) {
// Match formal to default value.
++fi;
} else {
// Failed to match formal.
return false;
}
}
assert(fi == fn || ai == an);
// Packing array arguments into the rest formal is inexact. Do not allow it
// here.
if (ai < an)
return false;
assert(ai == an);
// Match any remaining formal to defaults.
while (fi < fn)
if (formals[fi].defval) {
// Match formal to default value.
++fi;
} else {
// Failed to match formal.
return false;
}
// Non-rest arguments have matched.
assert(fi == fn && ai == an);
// Try to match the rest argument if given.
if (source->hasRest()) {
if (!target->hasRest())
return false;
if (!equivalent(source->getRest().t, target->getRest().t))
return false;
}
// All arguments have matched.
return true;
}
// Tries to match applications without casting. If an application matches
// here, we need not attempt to match others with the slower, more general
// techniques.
app_list exactMultimatch(env &e,
types::overloaded *o,
types::signature *source,
arglist &al)
{
assert(source);
app_list l;
// This can't handle named arguments.
if (namedFormals(source))
return l; /* empty */
for (ty_vector::iterator t=o->sub.begin(); t!=o->sub.end(); ++t)
{
if ((*t)->kind != ty_function)
continue;
function *ft = (function *)*t;
// First we run a test to see if all arguments could be exactly matched.
// If this returns false, no such match is possible.
// If it returns true, an exact match may or may not be possible.
if (!exactMightMatch(ft->getSignature(), source))
continue;
// Consider calling
// void f(A a=new A, int y)
// with
// f(3)
// This matches exactly if there is no implicit cast from int to A.
// Otherwise, it does not match.
// Thus, there is no way to know if the
// match truly is exact without looking at the environment.
// In such a case, exactMightMatch() must return true, but there is no
// exact match. Such false positives are eliminated here.
//
// Consider calling
// void f(int x, real y=0.0, int z=0)
// with
// f(1,2)
// exactMightMatch() will return true, matching 1 to x and 2 to z, but the
// application::match will give an inexact match of 1 to x to 2 to y, due
// to the cast from int to real. Therefore, we must test for exactness
// even after matching.
if (a && a->exact())
l.push_back(a);
}
bool halfExactMightMatch(env &e,
signature *target, types::ty *t1, types::ty *t2)
{
formal_vector& formals = target->formals;
if (formals.size() < 2)
return false;
if (formals.size() > 2) {
// We should probably abort the whole matching in this case. For now,
// return true and let the usual matching handle it.
return true;
}
assert(formals[0].t);
assert(formals[1].t);
// These casting tests if successful will be repeated again by
// application::match. It would be nice to avoid this somehow, but the
// additional complexity is probably not worth the minor speed improvement.
if (equivalent(formals[0].t, t1))
return e.fastCastable(formals[1].t, t2);
else
return equivalent(formals[1].t, t2) && e.fastCastable(formals[0].t, t1);
}
// Most common after exact matches are cases such as
// 2 + 3.4 (int, real) --> (real, real)
// that is, binary operations where one of the operands matches exactly and the
// other does not. This function searches for these so-called "half-exact"
// matches. This should only be called after exactMultimatch has failed.
app_list halfExactMultimatch(env &e,
types::overloaded *o,
types::signature *source,
arglist &al)
{
assert(source);
app_list l;
// Half exact is only in the case of two arguments.
formal_vector& formals = source->formals;
if (formals.size() != 2 || source->hasRest())
return l; /* empty */
// This can't handle named arguments.
if (namedFormals(source))
return l; /* empty */
// Alias the two argument types.
types::ty *t1 = formals[0].t;
types::ty *t2 = formals[1].t;
assert(t1); assert(t2);
for (ty_vector::iterator t=o->sub.begin(); t!=o->sub.end(); ++t)
{
if ((*t)->kind != ty_function)
continue;
function *ft = (function *)*t;
#if 1
if (!halfExactMightMatch(e, ft->getSignature(), t1, t2))
continue;
#endif
#if 1
if (a && a->halfExact())
l.push_back(a);
#endif
}
return l;
}
// Simple check if there are too many arguments to match the candidate
// function.
// A "tooFewArgs" variant was also implemented at some point, but did
// not give any speed-up.
bool tooManyArgs(types::signature *target, types::signature *source) {
return source->getNumFormals() > target->getNumFormals() &&
!target->hasRest();
}
// The full overloading resolution system, which handles casting of arguments,
// packing into rest arguments, named arguments, etc.
app_list inexactMultimatch(env &e,
types::overloaded *o,
types::signature *source,
arglist &al)
{
assert(source);
if (l.size() > 1) {
// Return the most specific candidates.
maximizer m;
for (app_list::iterator x=l.begin(); x!=l.end(); ++x) {
assert(*x);
m.add(*x);
}
return m.result();
}
else
return l;
}
// Sanity check for multimatch optimizations.
void sameApplications(app_list a, app_list b, testExactType te) {
assert(a.size() == b.size());
if (te == TEST_EXACT) {
for (app_list::iterator i = a.begin(); i != a.end(); ++i) {
if (!(*i)->exact()) {
cout << *(*i)->getType() << endl;
}
assert((*i)->exact());
}
for (app_list::iterator i = b.begin(); i != b.end(); ++i)
assert((*i)->exact());
}
if (a.size() == 1)
assert(equivalent(a.front()->getType(), b.front()->getType()));
}
app_list multimatch(env &e,
types::overloaded *o,
types::signature *source,
arglist &al)
{
app_list a = exactMultimatch(e, o, source, al);
if (!a.empty()) {
#if DEBUG_CACHE
// Make sure that exactMultimatch and the fallback return the same
// application(s).
sameApplications(a, inexactMultimatch(e, o, source, al), TEST_EXACT);
#endif
return a;
}
a = halfExactMultimatch(e, o, source, al);
if (!a.empty()) {
#if DEBUG_CACHE
sameApplications(a, inexactMultimatch(e, o, source, al), DONT_TEST_EXACT);
#endif
return a;
}
// Slow but most general method.
return inexactMultimatch(e, o, source, al);
}