aboutsummaryrefslogtreecommitdiff
path: root/build2/function.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-11-21 11:56:00 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-11-21 11:56:00 +0200
commit7a528eab1561b0d0d4ec29f98355fe67025ea632 (patch)
tree28a2061f17e3ee625e8674378227a81a8738a6ec /build2/function.cxx
parent3db0756adc641e0a63c4c9f194c4f73cceddd90c (diff)
Add support for derived-to-base function overload resolution
Diffstat (limited to 'build2/function.cxx')
-rw-r--r--build2/function.cxx258
1 files changed, 170 insertions, 88 deletions
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 <build2/function>
+#include <cstring> // 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<const value_type*> t (
+ i < f.arg_types.size () ? f.arg_types[i] : nullopt);
+
+ os << (t ? (*t != nullptr ? (*t)->name : "<untyped>") : "<anytype>");
+ }
+ }
+
+ 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<value> args, const location& loc)
+ call (const string& name, vector_view<value> 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<const function_overload*, 1> 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<const value_type*> 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 : "<untyped>") : "<anytype>");
- }
- }
+ 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.