From 7a528eab1561b0d0d4ec29f98355fe67025ea632 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 21 Nov 2016 11:56:00 +0200 Subject: Add support for derived-to-base function overload resolution --- build2/function.cxx | 258 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 170 insertions(+), 88 deletions(-) (limited to 'build2/function.cxx') diff --git a/build2/function.cxx b/build2/function.cxx index 91c5b3e..ec70529 100644 --- a/build2/function.cxx +++ b/build2/function.cxx @@ -4,10 +4,60 @@ #include +#include // strchr() + using namespace std; namespace build2 { + ostream& + operator<< (ostream& os, const function_overload& f) + { + os << f.name << '('; + + bool v (f.arg_max == function_overload::arg_variadic); + size_t n (v ? max (f.arg_min, f.arg_types.size ()): f.arg_max); + + // Handle variadic tail as the last pseudo-argument. + // + for (size_t i (0); i != n + (v ? 1 : 0); ++i) + { + if (i == f.arg_min) + os << (i != 0 ? " [" : "["); + + os << (i != 0 ? ", " : ""); + + if (i == n) // Variadic tail (last). + os << "..."; + else + { + // If count is greater than f.arg_typed, then we assume the rest are + // valid but untyped. + // + const optional t ( + i < f.arg_types.size () ? f.arg_types[i] : nullopt); + + os << (t ? (*t != nullptr ? (*t)->name : "") : ""); + } + } + + if (n + (v ? 1 : 0) > f.arg_min) + os << ']'; + + os << ')'; + + if (f.alt_name != nullptr) + { + auto k (strchr (f.alt_name, '.') == nullptr + ? "unqualified" + : "qualified"); + + os << ", " << k << " name " << f.alt_name; + } + + return os; + } + auto function_map:: insert (string name, function_overload f) -> iterator { @@ -24,7 +74,7 @@ namespace build2 } value function_map:: - call (const string& name, vector_view args, const location& loc) + call (const string& name, vector_view args, const location& loc) const { auto print_call = [&name, &args] (ostream& os) { @@ -39,113 +89,144 @@ namespace build2 os << ')'; }; - auto print_fovl = [&name] (ostream& os, const function_overload& f) - { - os << name << '('; + // Overload resolution. + // + // Ours is pretty simple: if all the arguments match exactly, then we have + // a perfect match. Otherwise, if any argument matches via the derived-to- + // base conversion, then we have an imperfect match. More than one perfect + // or imperfect match is ambiguous (i.e., we don't try to rank imperfect + // matches). + // + size_t count (args.size ()); + auto ip (map_.equal_range (name)); - bool v (f.arg_max == function_overload::arg_variadic); - size_t n (v ? max (f.arg_min, f.arg_types.size ()): f.arg_max); + // First look for a perfect match, then for imperfect. We do it this way + // to make sure we always stay small in the successful case. + // + small_vector r; - // Handle variadic tail as the last pseudo-argument. - // - for (size_t i (0); i != n + (v ? 1 : 0); ++i) + for (bool perf (true);; perf = false) + { + for (auto it (ip.first); it != ip.second; ++it) { - if (i == f.arg_min) - os << (i != 0 ? " [" : "["); + const function_overload& f (it->second); - os << (i != 0 ? ", " : ""); + // Argument count match. + // + if (count < f.arg_min || count > f.arg_max) + continue; - if (i == n) // Variadic tail (last). - os << "..."; - else + // Argument types match. + // { - // If count is greater than f.arg_typed, then we assume the rest are - // valid but untyped. - // - const optional t ( - i < f.arg_types.size () ? f.arg_types[i] : nullopt); + size_t i (0), n (min (count, f.arg_types.size ())); + for (; i != n; ++i) + { + if (!f.arg_types[i]) // Anytyped. + continue; - os << (t ? (*t != nullptr ? (*t)->name : "") : ""); - } - } + const value_type* at (args[i].type); + const value_type* ft (*f.arg_types[i]); - if (n + (v ? 1 : 0) > f.arg_min) - os << ']'; + if (at == ft) // Types match perfectly. + continue; - os << ')'; + if (!perf && at != nullptr && ft != nullptr) + { + while ((at = at->base) != nullptr && at != ft) ; - if (f.qual_name) - os << ", qualified name " << f.qual_name; - }; + if (at != nullptr) // Types match via derived-to-base. + continue; + } - // Overload resolution. - // - const function_overload* r (nullptr); + break; + } - size_t count (args.size ()); - auto ip (map_.equal_range (name)); + if (i != n) + continue; + } - for (auto it (ip.first); it != ip.second; ++it) - { - const function_overload& f (it->second); + r.push_back (&f); // Continue looking to detect ambiguities. + } - // Argument count match. - // - if (count < f.arg_min || count > f.arg_max) - continue; + if (!r.empty () || !perf) + break; + } - // Argument types match. - // + switch (r.size ()) + { + case 1: { - size_t i (0), n (min (count, f.arg_types.size ())); - for (; i != n; ++i) - if (f.arg_types[i] && *f.arg_types[i] != args[i].type) - break; - - if (i != n) - continue; + // Print the call location if the function fails. + // + auto g ( + make_exception_guard ( + [&loc, &print_call] () + { + if (verb != 0) + { + diag_record dr (info (loc)); + dr << "while calling "; print_call (dr.os); + } + })); + + auto f (r.back ()); + return f->impl (move (args), *f); } - - if (r != nullptr) + case 0: { - diag_record dr (fail (loc)); + // No match. + // + { + diag_record dr (error (loc)); - dr << "ambiguous call to "; print_call (dr.os); - dr << info << " candidate: "; print_fovl (dr.os, *r); - dr << info << " candidate: "; print_fovl (dr.os, f); - } + dr << "unmatched call to "; print_call (dr.os); - r = &f; // Continue looking to detect ambiguities. - } - - if (r == nullptr) - { - diag_record dr (fail (loc)); + for (auto i (ip.first); i != ip.second; ++i) + dr << info << "candidate: " << i->second; - dr << "unmatched call to "; print_call (dr.os); + // If this is an unqualified name, then also print qualified + // functions that end with this name. But skip functions that we + // have already printed in the previous loop. + // + if (name.find ('.') == string::npos) + { + size_t n (name.size ()); + + for (auto i (functions.begin ()); i != functions.end (); ++i) + { + const string& q (i->first); + const function_overload& f (i->second); + + if ((f.alt_name == nullptr || f.alt_name != name) && + q.size () > n) + { + size_t p (q.size () - n); + if (q[p - 1] == '.' && q.compare (p, n, name) == 0) + dr << info << "candidate: " << i->second; + } + } + } + } - for (auto it (ip.first); it != ip.second; ++it) + throw failed (); + } + default: { - const function_overload& f (it->second); + // Ambigous match. + // + { + diag_record dr (error (loc)); - dr << info << " candidate: "; print_fovl (dr.os, f); - } - } + dr << "ambiguous call to "; print_call (dr.os); - // Print the call location if the function fails. - // - auto g ( - make_exception_guard ( - [&loc, &print_call] () - { - if (verb != 0) - { - diag_record dr (info (loc)); - dr << "while calling "; print_call (dr.os); - } - })); + for (auto f: r) + dr << info << "candidate: " << *f; + } - return r->impl (move (args), *r); + throw failed (); + } + } } value function_family:: @@ -201,15 +282,16 @@ namespace build2 n.insert (0, qual); } - // First insert the qualified name and use its key for f.qual_name. + auto i (qn.empty () ? functions.end () : functions.insert (move (qn), f)); + auto j (functions.insert (move (n), move (f))); + + // If we have both, then set alternative names. // - if (!qn.empty ()) + if (i != functions.end ()) { - auto i (functions.insert (move (qn), f)); - f.qual_name = i->first.c_str (); + i->second.alt_name = j->first.c_str (); + j->second.alt_name = i->first.c_str (); } - - functions.insert (move (n), move (f)); } // Static-initialize the function map and populate with builtin functions. -- cgit v1.1