diff options
Diffstat (limited to 'Build/source/utils/asymptote/application.cc')
-rw-r--r-- | Build/source/utils/asymptote/application.cc | 404 |
1 files changed, 360 insertions, 44 deletions
diff --git a/Build/source/utils/asymptote/application.cc b/Build/source/utils/asymptote/application.cc index 5a287bf2559..533b624fe55 100644 --- a/Build/source/utils/asymptote/application.cc +++ b/Build/source/utils/asymptote/application.cc @@ -34,7 +34,11 @@ bool castable(env &e, formal& target, formal& source) { score castScore(env &e, formal& target, formal& source) { return equivalent(target.t,source.t) ? EXACT : (!target.Explicit && +#ifdef FASTCAST + e.fastCastable(target.t,source.t)) ? CAST : FAIL; +#else e.castable(target.t,source.t, symbol::castsym)) ? CAST : FAIL; +#endif } @@ -115,18 +119,26 @@ public: } }; +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() { - types::formal& f=sig->getRest(); + formal& f=sig->getRest(); if (f.t) { - types::array *a=dynamic_cast<types::array *>(f.t); - if(!a) + ty *ct = restCellType(sig); + if (!ct) vm::error("formal rest argument must be an array"); - static symbol *null=0; - rf=types::formal(a->celltype, null, false, f.Explicit); + rf=formal(ct, symbol::nullsym, false, f.Explicit); } - if (f.t || sig->isOpen) { rest=new restArg(); } @@ -135,7 +147,7 @@ void application::initRest() { //const Int REST=-1; const Int NOMATCH=-2; -Int application::find(symbol *name) { +Int application::find(symbol name) { formal_vector &f=sig->formals; for (size_t i=index; i<f.size(); ++i) if (f[i].name==name && args[i]==0) @@ -192,7 +204,7 @@ bool application::matchAtSpot(size_t spot, env &e, formal &source, bool application::matchArgument(env &e, formal &source, varinit *a, size_t evalIndex) { - assert(source.name==0); + assert(!source.name); if (index==args.size()) // Try to pack into the rest array. @@ -207,7 +219,7 @@ bool application::matchArgument(env &e, formal &source, bool application::matchNamedArgument(env &e, formal &source, varinit *a, size_t evalIndex) { - assert(source.name!=0); + assert(source.name); Int spot=find(source.name); return spot!=NOMATCH && matchAtSpot(spot, e, source, a, evalIndex); @@ -268,20 +280,6 @@ bool application::matchSignature(env &e, types::signature *source, return complete(); } -#if 0 -application *application::matchHelper(env &e, - application *app, - signature *source) -{ - return app->matchSignature(e, source) ? app : 0; -} - -application *application::match(env &e, signature *target, signature *source) { - application *app=new application(target); - return matchHelper(e, app, source); -} -#endif - bool application::matchOpen(env &e, signature *source, arglist &al) { assert(rest); @@ -305,10 +303,13 @@ application *application::match(env &e, function *t, signature *source, assert(t->kind==ty_function); application *app=new application(t); - if (t->getSignature()->isOpen) - return app->matchOpen(e, source, al) ? app : 0; - else - return app->matchSignature(e, source, al) ? app : 0; + bool success = t->getSignature()->isOpen ? + app->matchOpen(e, source, al) : + app->matchSignature(e, source, al); + + //cout << "MATCH " << success << endl; + + return success ? app : 0; } void application::transArgs(coenv &e) { @@ -321,34 +322,307 @@ void application::transArgs(coenv &e) { rest->trans(e,temps); } -app_list multimatch(env &e, - types::overloaded *o, - types::signature *source, - arglist &al) +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 exactlyMatchable(signature *target, signature *source) +{ + // Open signatures never exactly match. + if (target->isOpen) + return false; + +#if 0 + assert(!namedFormals(source)); +#endif + + formal_vector& formals = target->formals; + formal_vector& args = source->formals; + + // 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; + + if (!exactlyMatchable(ft->getSignature(), source)) + continue; + + application *a=application::match(e, ft, source, al); + assert(a); + + // Consider calling + // void f(int x, real y=0.0, int z=0) + // with + // f(1,2) + // exactlyMatchable 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->exact()) + l.push_back(a); + } + + //cout << "EXACTMATCH " << (!l.empty()) << endl; + return l; +} + +bool halfExactMightMatch(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); + + // Test if one of the arguments matches exactly. + return equivalent(formals[0].t, t1) || equivalent(formals[1].t, t2); +} + +// Most commonly after exact matches are cases such as 2+3.4, that is, binary +// operations were one of the operands matches exactly and the other does not. +// This searches for this 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(ft->getSignature(), t1, t2)) + continue; +#endif + + application *a=application::match(e, ft, source, al); + +#if 1 + if (a && a->halfExact()) + l.push_back(a); +#endif + } + + return l; +} + +// 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); + + app_list l; + + +#define DEBUG_GETAPP 0 #if DEBUG_GETAPP + //cout << "source: " << *source << endl; + //cout << "ARGS: " << source->getNumFormals() << endl; bool perfect=false; + bool exact=false; + bool halfExact=false; #endif for(ty_vector::iterator t=o->sub.begin(); t!=o->sub.end(); ++t) { if ((*t)->kind==ty_function) { #if DEBUG_GETAPP function *ft = dynamic_cast<function *>(*t); - if (equivalent(ft->getSignature(), source)) + signature *target = ft->getSignature(); + cout << "TOTAL" << endl; + cout << "ARGS: source: " << source->getNumFormals() + << " target: " << target->getNumFormals() << endl; + //cout << "target: " << *target << endl; + if (equivalent(target, source)) perfect = true; +#if 0 + cout << "TOTAL" << endl; + if (target->getNumFormals() < source->getNumFormals() && + !target->hasRest()) { + cout << "TOO MANY ARGS" << endl; + } else if (target->getNumFormals() == source->getNumFormals() && + !target->hasRest()) { + cout << "EXACT ARGS" << endl; + } else { + cout << "NOT TOO MANY ARGS" << endl; + } +#endif #endif application *a=application::match(e, (function *)(*t), source, al); if (a) l.push_back(a); +#if DEBUG_GETAPP + if (a && !namedFormals(source)) { + if (a->exact() != exactlyMatchable(ft->getSignature(), source)) { + cout << a->exact() << " != " + << exactlyMatchable(ft->getSignature(), source) << endl; + cout << "ft = " << *ft << endl; + cout << "args = " << *source << endl; + } + assert(a->exact() == exactlyMatchable(ft->getSignature(), source)); + if (a->halfExact() && !namedFormals(source)) { + if (!halfExactMightMatch(target, source->getFormal(0).t, + source->getFormal(1).t)) + { + cerr << "target: " << *target << " source: " << *source << endl; + cerr.flush(); + } + cerr << "test" << endl;; + cerr.flush(); + assert(halfExactMightMatch(target, source->getFormal(0).t, + source->getFormal(1).t)); + } + + } + if (a && a->exact()) exact = true; + if (a && a->halfExact()) halfExact = true; +#endif } } #if DEBUG_GETAPP - cout << (perfect ? "PERFECT" : "IMPERFECT") << endl; + cout << (perfect ? "PERFECT" : + exact ? "EXACT" : + halfExact ? "HALFEXACT" : + "IMPERFECT") + << endl; #endif if (l.size() > 1) { @@ -364,19 +638,61 @@ app_list multimatch(env &e, return l; } -#if 0 -app_list resolve(env &e, - types::ty *t, - types::signature *source) -{ - if (t->kind == ty_overloaded) - return multimatch(e, (overloaded *)t, source); - else { - overloaded o; - o.add(t); - return multimatch(e, &o, source); +enum testExactType { + TEST_EXACT, + DONT_TEST_EXACT, +}; + +// 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) +{ +#ifdef EXACT_MATCH + 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 + // Make sure that exactMultimatch and the fallback return the same + // application(s). + sameApplications(a, inexactMultimatch(e, o, source, al), DONT_TEST_EXACT); +#endif + + return a; + } #endif + // Slow but most general method. + return inexactMultimatch(e, o, source, al); +} + } // namespace trans |