Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-17 08:52:05

0001 // Copyright (c) 2018-2025 Jean-Louis Leroy
0002 // Distributed under the Boost Software License, Version 1.0.
0003 // See accompanying file LICENSE_1_0.txt
0004 // or copy at http://www.boost.org/LICENSE_1_0.txt)
0005 
0006 #ifndef BOOST_OPENMETHOD_COMPILER_HPP
0007 #define BOOST_OPENMETHOD_COMPILER_HPP
0008 
0009 #include <boost/openmethod/core.hpp>
0010 #include <boost/openmethod/detail/ostdstream.hpp>
0011 
0012 #include <algorithm>
0013 #include <cstdint>
0014 #include <deque>
0015 #include <map>
0016 #include <memory>
0017 #include <numeric>
0018 #include <string>
0019 #include <unordered_map>
0020 #include <unordered_set>
0021 #include <utility>
0022 #include <vector>
0023 
0024 #include <boost/assert.hpp>
0025 #include <boost/dynamic_bitset.hpp>
0026 
0027 #ifdef _MSC_VER
0028 #pragma warning(push)
0029 #pragma warning(disable : 4456)
0030 #pragma warning(disable : 4458)
0031 #pragma warning(disable : 4702) // unreachable code
0032 #endif
0033 
0034 namespace boost::openmethod {
0035 
0036 namespace detail {
0037 
0038 template<class Reports, class Facets, typename = void>
0039 struct aggregate_reports;
0040 
0041 template<class... Reports, class Facet, class... MoreFacets>
0042 struct aggregate_reports<
0043     mp11::mp_list<Reports...>, mp11::mp_list<Facet, MoreFacets...>,
0044     std::void_t<typename Facet::report>> {
0045     using type = typename aggregate_reports<
0046         mp11::mp_list<Reports..., typename Facet::report>,
0047         mp11::mp_list<MoreFacets...>>::type;
0048 };
0049 
0050 template<class... Reports, class Facet, class... MoreFacets, typename Void>
0051 struct aggregate_reports<
0052     mp11::mp_list<Reports...>, mp11::mp_list<Facet, MoreFacets...>, Void> {
0053     using type = typename aggregate_reports<
0054         mp11::mp_list<Reports...>, mp11::mp_list<MoreFacets...>>::type;
0055 };
0056 
0057 template<class... Reports, typename Void>
0058 struct aggregate_reports<mp11::mp_list<Reports...>, mp11::mp_list<>, Void> {
0059     struct type : Reports... {};
0060 };
0061 
0062 inline void merge_into(boost::dynamic_bitset<>& a, boost::dynamic_bitset<>& b) {
0063     if (b.size() < a.size()) {
0064         b.resize(a.size());
0065     }
0066 
0067     for (std::size_t i = 0; i < a.size(); ++i) {
0068         if (a[i]) {
0069             b[i] = true;
0070         }
0071     }
0072 }
0073 
0074 inline void set_bit(boost::dynamic_bitset<>& mask, std::size_t bit) {
0075     if (bit >= mask.size()) {
0076         mask.resize(bit + 1);
0077     }
0078 
0079     mask[bit] = true;
0080 }
0081 
0082 struct generic_compiler {
0083 
0084     struct method;
0085 
0086     struct parameter {
0087         struct method* method;
0088         std::size_t param;
0089     };
0090 
0091     struct vtbl_entry {
0092         std::size_t method_index, vp_index, group_index;
0093     };
0094 
0095     struct class_ {
0096         bool is_abstract = false;
0097         std::vector<type_id> type_ids;
0098         std::vector<class_*> transitive_bases;
0099         std::vector<class_*> direct_bases;
0100         std::vector<class_*> direct_derived;
0101         std::unordered_set<class_*> transitive_derived;
0102         std::vector<parameter> used_by_vp;
0103         boost::dynamic_bitset<> used_slots;
0104         boost::dynamic_bitset<> reserved_slots;
0105         std::size_t first_slot = 0;
0106         std::size_t mark = 0; // temporary mark to detect cycles
0107         std::vector<vtbl_entry> vtbl;
0108         vptr_type* static_vptr;
0109 
0110         auto is_base_of(class_* other) const -> bool {
0111             return transitive_derived.find(other) != transitive_derived.end();
0112         }
0113 
0114         auto vptr() const -> const vptr_type& {
0115             return *static_vptr;
0116         }
0117 
0118         auto type_id_begin() const {
0119             return type_ids.begin();
0120         }
0121 
0122         auto type_id_end() const {
0123             return type_ids.end();
0124         }
0125     };
0126 
0127     struct overrider {
0128         detail::overrider_info* info = nullptr;
0129         overrider* next = nullptr;
0130         std::vector<class_*> vp;
0131         class_* covariant_return_type = nullptr;
0132         void (*pf)();
0133         std::size_t method_index, spec_index;
0134     };
0135 
0136     using bitvec = boost::dynamic_bitset<>;
0137 
0138     struct group {
0139         std::vector<class_*> classes;
0140         bool has_concrete_classes{false};
0141     };
0142 
0143     using group_map = std::map<bitvec, group>;
0144 
0145     struct method_report {
0146         std::size_t cells = 0;
0147         std::size_t not_implemented = 0;
0148         std::size_t ambiguous = 0;
0149     };
0150 
0151     struct report : method_report {};
0152 
0153     static void accumulate(const method_report& partial, report& total);
0154 
0155     struct method {
0156         detail::method_info* info;
0157         std::vector<class_*> vp;
0158         class_* covariant_return_type = nullptr;
0159         std::vector<overrider> overriders;
0160         std::vector<std::size_t> slots;
0161         std::vector<std::size_t> strides;
0162         std::vector<const overrider*> dispatch_table;
0163         // following two are dummies, when converting to a function pointer, we will
0164         // get the corresponding pointer from method_info
0165         overrider not_implemented;
0166         overrider ambiguous;
0167         vptr_type gv_dispatch_table = nullptr;
0168         auto arity() const {
0169             return vp.size();
0170         }
0171         method_report report;
0172     };
0173 
0174     const method* operator[](const detail::method_info& info) const {
0175         auto iter = std::find_if(
0176             methods.begin(), methods.end(),
0177             [&info](const method& m) { return m.info == &info; });
0178 
0179         if (iter != methods.end()) {
0180             return &*iter;
0181         }
0182 
0183         return nullptr;
0184     }
0185 
0186     std::deque<class_> classes;
0187 
0188     auto classes_begin() const {
0189         return classes.begin();
0190     }
0191 
0192     auto classes_end() const {
0193         return classes.end();
0194     }
0195 
0196     std::vector<method> methods;
0197     std::size_t class_mark = 0;
0198     bool compilation_done = false;
0199 };
0200 
0201 template<class Compiler>
0202 struct trace_stream {
0203     bool on = false;
0204     std::size_t indentation_level{0};
0205 
0206     auto operator++() -> trace_stream& {
0207         if constexpr (Compiler::has_trace) {
0208             if (on) {
0209                 for (std::size_t i = 0; i < indentation_level; ++i) {
0210                     Compiler::Registry::output::os << "  ";
0211                 }
0212             }
0213         }
0214 
0215         return *this;
0216     }
0217 
0218     struct indent {
0219         trace_stream& trace;
0220         int by;
0221 
0222         explicit indent(trace_stream& trace, int by = 2)
0223             : trace(trace), by(by) {
0224             trace.indentation_level += by;
0225         }
0226 
0227         ~indent() {
0228             trace.indentation_level -= by;
0229         }
0230     };
0231 };
0232 
0233 struct rflush {
0234     std::size_t width;
0235     std::size_t value;
0236     explicit rflush(std::size_t width, std::size_t value)
0237         : width(width), value(value) {
0238     }
0239 };
0240 
0241 struct type_name {
0242     type_name(type_id type) : type(type) {
0243     }
0244     type_id type;
0245 };
0246 
0247 template<class Compiler>
0248 auto operator<<(trace_stream<Compiler>& tr, const generic_compiler::class_& cls)
0249     -> trace_stream<Compiler>& {
0250     if constexpr (Compiler::has_trace) {
0251         tr << type_name(cls.type_ids[0]);
0252     }
0253 
0254     return tr;
0255 }
0256 
0257 template<class Compiler, template<typename...> class Container, typename... T>
0258 auto operator<<(
0259     trace_stream<Compiler>& tr,
0260     Container<generic_compiler::class_*, T...>& classes)
0261     -> trace_stream<Compiler>& {
0262     if constexpr (Compiler::has_trace) {
0263         tr << "(";
0264         const char* sep = "";
0265         for (auto cls : classes) {
0266             tr << sep << *cls;
0267             sep = ", ";
0268         }
0269 
0270         tr << ")";
0271     }
0272 
0273     return tr;
0274 }
0275 
0276 struct spec_name {
0277     spec_name(
0278         const detail::generic_compiler::method& method,
0279         const detail::generic_compiler::overrider* def)
0280         : method(method), def(def) {
0281     }
0282     const detail::generic_compiler::method& method;
0283     const detail::generic_compiler::overrider* def;
0284 };
0285 
0286 template<class Compiler>
0287 auto operator<<(trace_stream<Compiler>& tr, const spec_name& sn)
0288     -> trace_stream<Compiler>& {
0289     if (sn.def == &sn.method.not_implemented) {
0290         tr << "not implemented";
0291     } else if (sn.def == &sn.method.ambiguous) {
0292         tr << "ambiguous";
0293     } else {
0294         tr << type_name(sn.def->info->type);
0295     }
0296 
0297     return tr;
0298 }
0299 
0300 template<typename Iterator>
0301 struct range;
0302 
0303 template<class Compiler, typename T, typename F>
0304 auto write_range(trace_stream<Compiler>& tr, range<T> range, F fn) -> auto& {
0305     if constexpr (Compiler::has_trace) {
0306         if (tr.on) {
0307             tr << "(";
0308             const char* sep = "";
0309             for (auto value : range) {
0310                 tr << sep << fn(value);
0311                 sep = ", ";
0312             }
0313 
0314             tr << ")";
0315         }
0316     }
0317 
0318     return tr;
0319 }
0320 
0321 template<class Compiler, typename T>
0322 auto operator<<(trace_stream<Compiler>& tr, const T& value) -> auto& {
0323     if constexpr (Compiler::has_trace) {
0324         if (tr.on) {
0325             Compiler::Registry::output::os << value;
0326         }
0327     }
0328     return tr;
0329 }
0330 
0331 template<class Compiler>
0332 auto operator<<(trace_stream<Compiler>& tr, const rflush& rf) -> auto& {
0333     if constexpr (Compiler::has_trace) {
0334         if (tr.on) {
0335             std::size_t digits = 1;
0336             auto tmp = rf.value / 10;
0337 
0338             while (tmp) {
0339                 ++digits;
0340                 tmp /= 10;
0341             }
0342 
0343             while (digits < rf.width) {
0344                 tr << " ";
0345                 ++digits;
0346             }
0347 
0348             tr << rf.value;
0349         }
0350     }
0351 
0352     return tr;
0353 }
0354 
0355 template<class Compiler>
0356 auto operator<<(trace_stream<Compiler>& tr, const boost::dynamic_bitset<>& bits)
0357     -> auto& {
0358     if constexpr (Compiler::has_trace) {
0359         if (tr.on) {
0360             auto i = bits.size();
0361             while (i != 0) {
0362                 --i;
0363                 Compiler::Registry::output::os << bits[i];
0364             }
0365         }
0366     }
0367 
0368     return tr;
0369 }
0370 
0371 template<class Compiler>
0372 auto operator<<(trace_stream<Compiler>& tr, const range<type_id*>& tips)
0373     -> auto& {
0374     return write_range(tr, tips, [](auto tip) { return type_name(tip); });
0375 }
0376 
0377 template<class Compiler, typename T>
0378 auto operator<<(trace_stream<Compiler>& tr, const range<T>& range) -> auto& {
0379     return write_range(tr, range, [](auto value) { return value; });
0380 }
0381 
0382 template<class Compiler>
0383 auto operator<<(trace_stream<Compiler>& tr, const type_name& manip) -> auto& {
0384     if constexpr (Compiler::has_trace) {
0385         Compiler::Registry::rtti::type_name(manip.type, tr);
0386     }
0387 
0388     return tr;
0389 }
0390 } // namespace detail
0391 
0392 // Definition of the nested template struct outside the registry class
0393 template<class... Policies>
0394 template<class... Options>
0395 struct registry<Policies...>::compiler : detail::generic_compiler {
0396     using type_index_type = decltype(rtti::type_index(0));
0397 
0398     typename detail::aggregate_reports<mp11::mp_list<report>, policy_list>::type
0399         report;
0400 
0401     std::unordered_map<type_index_type, class_*> class_map;
0402 
0403     using Registry = registry;
0404 
0405     compiler(Options... opts);
0406 
0407     auto compile();
0408     void initialize();
0409     void install_global_tables();
0410 
0411     void augment_classes();
0412     void collect_transitive_bases(class_* cls, class_* base);
0413     void calculate_transitive_derived(class_& cls);
0414     void augment_methods();
0415     void assign_slots();
0416     void assign_tree_slots(class_& cls, std::size_t base_slot);
0417     void assign_lattice_slots(class_& cls);
0418     void build_dispatch_tables();
0419     void build_dispatch_table(
0420         method& m, std::size_t dim,
0421         std::vector<group_map>::const_iterator group, const bitvec& candidates,
0422         bool concrete);
0423     void write_global_data();
0424     void print(const method_report& report) const;
0425     static void select_dominant_overriders(
0426         std::vector<overrider*>& dominants, std::size_t& pick,
0427         std::size_t& remaining);
0428     static auto
0429     is_more_specific(const overrider* a, const overrider* b) -> bool;
0430     static auto is_base(const overrider* a, const overrider* b) -> bool;
0431 
0432     std::tuple<Options...> options;
0433 
0434     template<class Option>
0435     static constexpr bool has_option =
0436         mp11::mp_contains<mp11::mp_list<Options...>, Option>::value;
0437 
0438     static constexpr bool has_trace = has_option<trace>;
0439     static constexpr bool has_n2216 = has_option<n2216>;
0440 
0441     mutable detail::trace_stream<compiler> tr;
0442     using indent = typename detail::trace_stream<compiler>::indent;
0443 };
0444 
0445 template<class... Policies>
0446 template<class... Options>
0447 void registry<Policies...>::compiler<Options...>::install_global_tables() {
0448     if (!compilation_done) {
0449         abort();
0450     }
0451 
0452     write_global_data();
0453 
0454     print(report);
0455     ++tr << "Finished\n";
0456 }
0457 
0458 template<class... Policies>
0459 template<class... Options>
0460 auto registry<Policies...>::compiler<Options...>::compile() {
0461     augment_classes();
0462     augment_methods();
0463     assign_slots();
0464     build_dispatch_tables();
0465 
0466     compilation_done = true;
0467 
0468     return report;
0469 }
0470 
0471 template<class... Policies>
0472 template<class... Options>
0473 void registry<Policies...>::compiler<Options...>::initialize() {
0474     compile();
0475     install_global_tables();
0476     registry<Policies...>::initialized = true;
0477 }
0478 
0479 #ifdef _MSC_VER
0480 namespace detail {
0481 
0482 template<bool HasTrace, typename T>
0483 struct msvc_tuple_get;
0484 
0485 template<typename T>
0486 struct msvc_tuple_get<true, T> {
0487     template<class Tuple>
0488     static decltype(auto) fn(const Tuple& t) {
0489         return std::get<T>(t);
0490     }
0491 };
0492 
0493 template<typename T>
0494 struct msvc_tuple_get<false, T> {
0495     template<class Tuple>
0496     static decltype(auto) fn(const Tuple&) {
0497         return T();
0498     }
0499 };
0500 } // namespace detail
0501 #endif
0502 
0503 template<class... Policies>
0504 template<class... Options>
0505 registry<Policies...>::compiler<Options...>::compiler(Options... opts)
0506     : options(opts...) {
0507     if constexpr (has_trace) {
0508 #ifdef _MSC_VER
0509         tr.on = detail::msvc_tuple_get<has_trace, trace>::fn(options).on;
0510 #else
0511         // Even with the constexpr has_trace guard, msvc errors on this.
0512         tr.on = std::get<trace>(options).on;
0513 #endif
0514     }
0515 }
0516 
0517 template<class... Policies>
0518 template<class... Options>
0519 void registry<Policies...>::compiler<Options...>::collect_transitive_bases(
0520     class_* cls, class_* base) {
0521     if (base->mark == class_mark) {
0522         return;
0523     }
0524 
0525     cls->transitive_bases.push_back(base);
0526     base->mark = class_mark;
0527 
0528     for (auto base_base : base->transitive_bases) {
0529         collect_transitive_bases(cls, base_base);
0530     }
0531 }
0532 
0533 template<class... Policies>
0534 template<class... Options>
0535 void registry<Policies...>::compiler<Options...>::augment_classes() {
0536     using namespace detail;
0537 
0538     // scope
0539     {
0540         ++tr << "Static class info:\n";
0541 
0542         // The standard does not guarantee that there is exactly one
0543         // type_info object per class. However, it guarantees that the
0544         // type_index for a class has a unique value.
0545         for (auto& cr : registry::classes) {
0546             if constexpr (has_deferred_static_rtti) {
0547                 static_cast<deferred_class_info&>(cr).resolve_type_ids();
0548             }
0549 
0550             {
0551                 indent _(tr);
0552                 ++tr << type_name(cr.type) << ": "
0553                      << range{cr.first_base, cr.last_base} << "\n";
0554             }
0555 
0556             auto& rtc = class_map[rtti::type_index(cr.type)];
0557 
0558             if (rtc == nullptr) {
0559                 rtc = &classes.emplace_back();
0560                 rtc->is_abstract = cr.is_abstract;
0561                 rtc->static_vptr = cr.static_vptr;
0562             }
0563 
0564             if (std::find(
0565                     rtc->type_ids.begin(), rtc->type_ids.end(), cr.type) ==
0566                 rtc->type_ids.end()) {
0567                 rtc->type_ids.push_back(cr.type);
0568             }
0569         }
0570     }
0571 
0572     // All known classes now have exactly one associated class_* in the
0573     // map. Collect the bases.
0574 
0575     for (auto& cr : registry::classes) {
0576         auto rtc = class_map[rtti::type_index(cr.type)];
0577 
0578         for (auto& base : range{cr.first_base, cr.last_base}) {
0579             auto rtb = class_map[rtti::type_index(base)];
0580 
0581             if (!rtb) {
0582                 missing_class error;
0583                 error.type = base;
0584 
0585                 if constexpr (has_error_handler) {
0586                     error_handler::error(error);
0587                 }
0588 
0589                 abort();
0590             }
0591 
0592             if (rtc != rtb) {
0593                 // At compile time we collected the class as its own
0594                 // improper base, as per std::is_base_of. Eliminate that.
0595                 ++class_mark;
0596                 collect_transitive_bases(rtc, rtb);
0597             }
0598         }
0599     }
0600 
0601     // At this point bases may contain duplicates, and also indirect
0602     // bases. Clean that up.
0603 
0604     std::size_t mark = ++class_mark;
0605 
0606     for (auto& rtc : classes) {
0607         decltype(rtc.transitive_bases) bases;
0608         mark = ++class_mark;
0609 
0610         for (auto rtb : rtc.transitive_bases) {
0611             if (rtb->mark != mark) {
0612                 bases.push_back(rtb);
0613                 rtb->mark = mark;
0614             }
0615         }
0616 
0617         rtc.transitive_bases.swap(bases);
0618     }
0619 
0620     for (auto& rtc : classes) {
0621         // Sort base classes by number of transitive bases. This ensures that a
0622         // base class is never preceded by one if its own base classes.
0623         std::sort(
0624             rtc.transitive_bases.begin(), rtc.transitive_bases.end(),
0625             [](auto a, auto b) {
0626                 return a->transitive_bases.size() > b->transitive_bases.size();
0627             });
0628         mark = ++class_mark;
0629 
0630         // Collect the direct base classes. The first base is certainly a
0631         // direct one. Remove *its* bases from the candidates, by marking
0632         // them. Continue with the next base that is not marked. It is the
0633         // next direct base. And so on...
0634 
0635         for (auto rtb : rtc.transitive_bases) {
0636             if (rtb->mark == mark) {
0637                 continue;
0638             }
0639 
0640             rtc.direct_bases.push_back(rtb);
0641 
0642             for (auto rtbb : rtb->transitive_bases) {
0643                 rtbb->mark = mark;
0644             }
0645         }
0646     }
0647 
0648     for (auto& rtc : classes) {
0649         for (auto rtb : rtc.direct_bases) {
0650             rtb->direct_derived.push_back(&rtc);
0651         }
0652     }
0653 
0654     for (auto& rtc : classes) {
0655         calculate_transitive_derived(rtc);
0656     }
0657 
0658     if constexpr (has_trace) {
0659         ++tr << "Inheritance lattice:\n";
0660 
0661         for (auto& rtc : classes) {
0662             indent _2(tr);
0663             ++tr << rtc << "\n";
0664 
0665             {
0666                 indent _3(tr);
0667                 ++tr << "bases:      " << rtc.direct_bases << "\n";
0668                 ++tr << "derived:    " << rtc.direct_derived << "\n";
0669                 ++tr << "covariant:  " << rtc.transitive_derived << "\n";
0670             }
0671         }
0672     }
0673 }
0674 
0675 template<class... Policies>
0676 template<class... Options>
0677 void registry<Policies...>::compiler<Options...>::calculate_transitive_derived(
0678     class_& cls) {
0679     if (!cls.transitive_derived.empty()) {
0680         return;
0681     }
0682 
0683     cls.transitive_derived.insert(&cls);
0684 
0685     for (auto derived : cls.direct_derived) {
0686         if (derived->transitive_derived.empty()) {
0687             calculate_transitive_derived(*derived);
0688         }
0689 
0690         std::copy(
0691             derived->transitive_derived.begin(),
0692             derived->transitive_derived.end(),
0693             std::inserter(
0694                 cls.transitive_derived, cls.transitive_derived.end()));
0695     }
0696 }
0697 
0698 template<class... Policies>
0699 template<class... Options>
0700 void registry<Policies...>::compiler<Options...>::augment_methods() {
0701     using namespace policies;
0702     using namespace detail;
0703 
0704     methods.resize(registry::methods.size());
0705 
0706     ++tr << "Methods:\n";
0707     indent _(tr);
0708 
0709     auto meth_iter = methods.begin();
0710 
0711     for (auto& meth_info : registry::methods) {
0712         if constexpr (has_deferred_static_rtti) {
0713             static_cast<deferred_method_info&>(meth_info).resolve_type_ids();
0714         }
0715 
0716         ++tr << type_name(meth_info.method_type_id) << " "
0717              << range{meth_info.vp_begin, meth_info.vp_end} << "\n";
0718 
0719         indent _(tr);
0720 
0721         meth_iter->info = &meth_info;
0722         meth_iter->vp.reserve(meth_info.arity());
0723         meth_iter->slots.resize(meth_info.arity());
0724 
0725         {
0726             std::size_t param_index = 0;
0727 
0728             for (auto ti : range{meth_info.vp_begin, meth_info.vp_end}) {
0729                 auto class_ = class_map[rtti::type_index(ti)];
0730                 if (!class_) {
0731                     ++tr << "unknown class " << ti << "(" << type_name(ti)
0732                          << ") for parameter #" << (param_index + 1) << "\n";
0733                     missing_class error;
0734                     error.type = ti;
0735 
0736                     if constexpr (has_error_handler) {
0737                         error_handler::error(error);
0738                     }
0739 
0740                     abort();
0741                 }
0742 
0743                 meth_iter->vp.push_back(class_);
0744             }
0745         }
0746 
0747         if (rtti::type_index(meth_info.return_type_id) !=
0748             rtti::type_index(rtti::template static_type<void>())) {
0749             auto covariant_return_iter =
0750                 class_map.find(rtti::type_index(meth_info.return_type_id));
0751 
0752             if (covariant_return_iter != class_map.end()) {
0753                 meth_iter->covariant_return_type =
0754                     covariant_return_iter->second;
0755             }
0756         }
0757 
0758         // initialize the function pointer in the synthetic not_implemented
0759         // overrider
0760         const auto method_index = meth_iter - methods.begin();
0761         auto spec_size = meth_info.overriders.size();
0762         meth_iter->not_implemented.pf = meth_iter->info->not_implemented;
0763         meth_iter->not_implemented.method_index = method_index;
0764         meth_iter->not_implemented.spec_index = spec_size;
0765         meth_iter->ambiguous.pf = meth_iter->info->ambiguous;
0766         meth_iter->ambiguous.method_index = method_index;
0767         meth_iter->ambiguous.spec_index = spec_size + 1;
0768 
0769         meth_iter->overriders.resize(spec_size);
0770         auto spec_iter = meth_iter->overriders.begin();
0771 
0772         for (auto& overrider_info : meth_info.overriders) {
0773             if constexpr (has_deferred_static_rtti) {
0774                 static_cast<deferred_overrider_info&>(overrider_info)
0775                     .resolve_type_ids();
0776             }
0777 
0778             spec_iter->method_index = method_index;
0779             spec_iter->spec_index = spec_iter - meth_iter->overriders.begin();
0780 
0781             ++tr << type_name(overrider_info.type) << " (" << overrider_info.pf
0782                  << ")\n";
0783             spec_iter->info = &overrider_info;
0784             spec_iter->vp.reserve(meth_info.arity());
0785             std::size_t param_index = 0;
0786 
0787             for (auto type :
0788                  range{overrider_info.vp_begin, overrider_info.vp_end}) {
0789                 indent _(tr);
0790                 auto class_ = class_map[rtti::type_index(type)];
0791 
0792                 if (!class_) {
0793                     ++tr << "unknown class error for *virtual* parameter #"
0794                          << (param_index + 1) << "\n";
0795                     missing_class error;
0796                     error.type = type;
0797 
0798                     if constexpr (has_error_handler) {
0799                         error_handler::error(error);
0800                     }
0801 
0802                     abort();
0803                 }
0804 
0805                 spec_iter->pf = spec_iter->info->pf;
0806                 spec_iter->vp.push_back(class_);
0807             }
0808 
0809             if (meth_iter->covariant_return_type) {
0810                 auto covariant_return_iter = class_map.find(
0811                     rtti::type_index(overrider_info.return_type));
0812 
0813                 if (covariant_return_iter != class_map.end()) {
0814                     spec_iter->covariant_return_type =
0815                         covariant_return_iter->second;
0816                 } else {
0817                     missing_class error;
0818                     error.type = overrider_info.return_type;
0819 
0820                     if constexpr (has_error_handler) {
0821                         error_handler::error(error);
0822                     }
0823 
0824                     abort();
0825                 }
0826             }
0827 
0828             ++spec_iter;
0829         }
0830 
0831         ++meth_iter;
0832     }
0833 
0834     for (auto& method : methods) {
0835         std::size_t param_index = 0;
0836 
0837         for (auto vp : method.vp) {
0838             for (auto& overrider : method.overriders) {
0839                 if (overrider.vp[param_index] == vp) {
0840                     continue;
0841                 }
0842 
0843                 if (!vp->is_base_of(overrider.vp[param_index])) {
0844                     missing_base error;
0845                     error.base = overrider.vp[param_index]->type_ids[0];
0846                     error.derived = vp->type_ids[0];
0847 
0848                     if constexpr (has_error_handler) {
0849                         error_handler::error(error);
0850                     }
0851 
0852                     abort();
0853                 }
0854             }
0855 
0856             vp->used_by_vp.push_back({&method, param_index++});
0857         }
0858     }
0859 }
0860 
0861 template<class... Policies>
0862 template<class... Options>
0863 void registry<Policies...>::compiler<Options...>::assign_slots() {
0864     ++tr << "Allocating slots...\n";
0865 
0866     {
0867         indent _(tr);
0868 
0869         ++class_mark;
0870 
0871         for (auto& cls : classes) {
0872             if (cls.direct_bases.size() == 0) {
0873                 if (std::find_if(
0874                         cls.transitive_derived.begin(),
0875                         cls.transitive_derived.end(), [](auto cls) {
0876                             return cls->direct_bases.size() > 1;
0877                         }) == cls.transitive_derived.end()) {
0878                     indent _(tr);
0879                     assign_tree_slots(cls, 0);
0880                 } else {
0881                     assign_lattice_slots(cls);
0882                 }
0883             }
0884         }
0885     }
0886 
0887     ++tr << "Allocating MI v-tables...\n";
0888 
0889     {
0890         indent _(tr);
0891 
0892         for (auto& cls : classes) {
0893             if (cls.used_slots.empty()) {
0894                 // not involved in multiple inheritance
0895                 continue;
0896             }
0897 
0898             auto first_slot = cls.used_slots.find_first();
0899             cls.first_slot =
0900                 first_slot == boost::dynamic_bitset<>::npos ? 0u : first_slot;
0901             cls.vtbl.resize(cls.used_slots.size() - cls.first_slot);
0902             ++tr << cls << " vtbl: " << cls.first_slot << "-"
0903                  << cls.used_slots.size() << " slots " << cls.used_slots
0904                  << "\n";
0905         }
0906     }
0907 }
0908 
0909 template<class... Policies>
0910 template<class... Options>
0911 void registry<Policies...>::compiler<Options...>::assign_tree_slots(
0912     class_& cls, std::size_t base_slot) {
0913     auto next_slot = base_slot;
0914     using namespace detail;
0915 
0916     for (const auto& mp : cls.used_by_vp) {
0917         ++tr << " in " << cls << " for "
0918              << type_name(mp.method->info->method_type_id) << " parameter "
0919              << mp.param << ": " << next_slot << "\n";
0920         mp.method->slots[mp.param] = next_slot++;
0921     }
0922 
0923     cls.first_slot = 0;
0924     cls.vtbl.resize(next_slot);
0925 
0926     for (auto pd : cls.direct_derived) {
0927         assign_tree_slots(*pd, next_slot);
0928     }
0929 }
0930 
0931 template<class... Policies>
0932 template<class... Options>
0933 void registry<Policies...>::compiler<Options...>::assign_lattice_slots(
0934     class_& cls) {
0935     using namespace detail;
0936 
0937     if (cls.mark == class_mark) {
0938         return;
0939     }
0940 
0941     cls.mark = class_mark;
0942 
0943     if (!cls.used_by_vp.empty()) {
0944         for (const auto& mp : cls.used_by_vp) {
0945             ++tr << " in " << cls << " for "
0946                  << type_name(mp.method->info->method_type_id) << " parameter "
0947                  << mp.param << "\n";
0948 
0949             indent _(tr);
0950 
0951             ++tr << "reserved slots: " << cls.reserved_slots
0952                  << " used slots: " << cls.used_slots << "\n";
0953 
0954             auto unavailable_slots = cls.used_slots;
0955             detail::merge_into(cls.reserved_slots, unavailable_slots);
0956 
0957             ++tr << "unavailable slots: " << unavailable_slots << "\n";
0958 
0959             std::size_t slot = 0;
0960 
0961             for (; slot < unavailable_slots.size(); ++slot) {
0962                 if (!unavailable_slots[slot]) {
0963                     break;
0964                 }
0965             }
0966 
0967             ++tr << "first available slot: " << slot << "\n";
0968 
0969             mp.method->slots[mp.param] = slot;
0970             detail::set_bit(cls.used_slots, slot);
0971             detail::set_bit(cls.reserved_slots, slot);
0972 
0973             {
0974                 ++tr << "reserve slots " << cls.used_slots << " in:\n";
0975                 indent _(tr);
0976 
0977                 for (auto base : cls.transitive_bases) {
0978                     ++tr << *base << "\n";
0979                     detail::merge_into(cls.used_slots, base->reserved_slots);
0980                 }
0981             }
0982 
0983             {
0984                 ++tr << "assign slots " << cls.used_slots << " in:\n";
0985                 indent _(tr);
0986 
0987                 for (auto covariant : cls.transitive_derived) {
0988                     if (&cls != covariant) {
0989                         ++tr << *covariant << "\n";
0990                         detail::merge_into(
0991                             cls.used_slots, covariant->used_slots);
0992 
0993                         for (auto base : covariant->transitive_bases) {
0994                             ++tr << *base << "\n";
0995                             detail::merge_into(
0996                                 cls.used_slots, base->reserved_slots);
0997                         }
0998                     }
0999                 }
1000             }
1001         }
1002     }
1003 
1004     for (auto pd : cls.direct_derived) {
1005         assign_lattice_slots(*pd);
1006     }
1007 }
1008 
1009 template<class... Policies>
1010 template<class... Options>
1011 void registry<Policies...>::compiler<Options...>::build_dispatch_tables() {
1012     using namespace detail;
1013 
1014     for (auto& m : methods) {
1015         ++tr << "Building dispatch table for "
1016              << type_name(m.info->method_type_id) << "\n";
1017         indent _(tr);
1018 
1019         auto dims = m.arity();
1020 
1021         std::vector<group_map> groups;
1022         groups.resize(dims);
1023 
1024         {
1025             std::size_t dim = 0;
1026 
1027             for (auto vp : m.vp) {
1028                 auto& dim_group = groups[dim];
1029                 ++tr << "make groups for param #" << dim << ", class " << *vp
1030                      << "\n";
1031                 indent _(tr);
1032 
1033                 for (auto covariant_class : vp->transitive_derived) {
1034                     ++tr << "overriders applicable to " << *covariant_class
1035                          << "\n";
1036                     bitvec mask;
1037                     mask.resize(m.overriders.size());
1038 
1039                     std::size_t group_index = 0;
1040                     indent _2(tr);
1041 
1042                     for (auto& spec : m.overriders) {
1043                         if (spec.vp[dim]->transitive_derived.find(
1044                                 covariant_class) !=
1045                             spec.vp[dim]->transitive_derived.end()) {
1046                             ++tr << type_name(spec.info->type) << "\n";
1047                             mask[group_index] = 1;
1048                         }
1049                         ++group_index;
1050                     }
1051 
1052                     auto& group = dim_group[mask];
1053                     group.classes.push_back(covariant_class);
1054                     group.has_concrete_classes = group.has_concrete_classes ||
1055                         !covariant_class->is_abstract;
1056 
1057                     ++tr << "-> mask: " << mask << "\n";
1058                 }
1059 
1060                 ++dim;
1061             }
1062         }
1063 
1064         {
1065             std::size_t stride = 1;
1066             m.strides.reserve(dims - 1);
1067 
1068             for (std::size_t dim = 1; dim < m.arity(); ++dim) {
1069                 stride *= groups[dim - 1].size();
1070                 ++tr << "    stride for dim " << dim << " = " << stride << "\n";
1071                 m.strides.push_back(stride);
1072             }
1073         }
1074 
1075         for (std::size_t dim = 0; dim < m.arity(); ++dim) {
1076             indent _(tr);
1077             std::size_t group_num = 0;
1078 
1079             for (auto& [mask, group] : groups[dim]) {
1080                 ++tr << "groups for dim " << dim << ":\n";
1081                 indent _(tr);
1082                 ++tr << group_num << " mask " << mask << ":\n";
1083 
1084                 for (auto cls : group.classes) {
1085                     indent _(tr);
1086                     ++tr << type_name(cls->type_ids[0]) << "\n";
1087                     auto& entry = cls->vtbl[m.slots[dim] - cls->first_slot];
1088                     entry.method_index = &m - &methods[0];
1089                     entry.vp_index = dim;
1090                     entry.group_index = group_num;
1091                 }
1092 
1093                 ++group_num;
1094             }
1095         }
1096 
1097         {
1098             ++tr << "building dispatch table\n";
1099             bitvec all(m.overriders.size());
1100             all = ~all;
1101             build_dispatch_table(m, dims - 1, groups.end() - 1, all, true);
1102 
1103             if (m.arity() > 1) {
1104                 indent _(tr);
1105                 m.report.cells = 1;
1106                 ++tr << "dispatch table rank: ";
1107                 const char* prefix = "";
1108 
1109                 for (const auto& dim_groups : groups) {
1110                     m.report.cells *= dim_groups.size();
1111                     tr << prefix << dim_groups.size();
1112                     prefix = " x ";
1113                 }
1114 
1115                 prefix = ", concrete only: ";
1116 
1117                 for (const auto& dim_groups : groups) {
1118                     auto cells = std::count_if(
1119                         dim_groups.begin(), dim_groups.end(),
1120                         [](const auto& group) {
1121                             return group.second.has_concrete_classes;
1122                         });
1123                     tr << prefix << cells;
1124                     prefix = " x ";
1125                 }
1126 
1127                 tr << "\n";
1128             }
1129 
1130             print(m.report);
1131             accumulate(m.report, report);
1132         }
1133     }
1134 }
1135 
1136 template<class... Policies>
1137 template<class... Options>
1138 void registry<Policies...>::compiler<Options...>::build_dispatch_table(
1139     method& m, std::size_t dim,
1140     std::vector<group_map>::const_iterator group_iter, const bitvec& candidates,
1141     bool concrete) {
1142     using namespace detail;
1143 
1144     indent _(tr);
1145     std::size_t group_index = 0;
1146 
1147     for (const auto& [group_mask, group] : *group_iter) {
1148         auto mask = candidates & group_mask;
1149 
1150         if constexpr (has_trace) {
1151             ++tr << "group " << dim << "/" << group_index << " mask " << mask
1152                  << "\n";
1153             indent _(tr);
1154             for (auto cls : range{group.classes.begin(), group.classes.end()}) {
1155                 ++tr << type_name(cls->type_ids[0]) << "\n";
1156             }
1157         }
1158 
1159         if (dim == 0) {
1160             std::vector<overrider*> overriders;
1161             std::size_t i = 0;
1162 
1163             for (auto& spec : m.overriders) {
1164                 if (mask[i]) {
1165                     overriders.push_back(&spec);
1166                 }
1167                 ++i;
1168             }
1169 
1170             if constexpr (has_trace) {
1171                 ++tr << "select best of:\n";
1172                 indent _(tr);
1173 
1174                 for (auto& app : overriders) {
1175                     ++tr << "#" << app->spec_index << " "
1176                          << type_name(app->info->type) << "\n";
1177                 }
1178             }
1179 
1180             std::vector<overrider*> dominants = overriders;
1181             std::size_t pick, remaining;
1182 
1183             select_dominant_overriders(dominants, pick, remaining);
1184 
1185             if (remaining == 0) {
1186                 indent _(tr);
1187                 ++tr << "not implemented\n";
1188                 m.dispatch_table.push_back(&m.not_implemented);
1189                 ++m.report.not_implemented;
1190             } else {
1191                 if constexpr (!has_option<n2216>) {
1192                     if (remaining > 1) {
1193                         ++tr << "ambiguous\n";
1194                         m.dispatch_table.push_back(&m.ambiguous);
1195                         ++m.report.ambiguous;
1196                         continue;
1197                     }
1198                 }
1199 
1200                 auto overrider = dominants[pick];
1201                 m.dispatch_table.push_back(overrider);
1202                 ++tr;
1203 
1204                 tr << "-> #" << overrider->spec_index << " "
1205                    << type_name(overrider->info->type)
1206                    << " pf = " << overrider->info->pf;
1207 
1208                 if (remaining > 1) {
1209                     tr << " (ambiguous)";
1210                     ++m.report.ambiguous;
1211                 }
1212 
1213                 tr << "\n";
1214 
1215                 // -------------------------------------------------------------
1216                 // next
1217 
1218                 // First remove the dominant overriders from the overriders.
1219                 // Note that the dominants appear in the overriders in the same
1220                 // relative order.
1221                 auto candidate = overriders.begin();
1222                 remaining = 0;
1223 
1224                 for (auto dominant : dominants) {
1225                     if (*candidate == dominant) {
1226                         *candidate = nullptr;
1227                     } else {
1228                         ++remaining;
1229                     }
1230 
1231                     ++candidate;
1232                 }
1233 
1234                 if (remaining == 0) {
1235                     ++tr << "no 'next'\n";
1236                     overrider->next = &m.not_implemented;
1237                 } else {
1238                     if constexpr (has_trace) {
1239                         ++tr << "for 'next', select best of:\n";
1240                         indent _(tr);
1241 
1242                         for (auto& app : overriders) {
1243                             if (app) {
1244                                 ++tr << "#" << app->spec_index << " "
1245                                      << type_name(app->info->type) << "\n";
1246                             }
1247                         }
1248                     }
1249 
1250                     select_dominant_overriders(overriders, pick, remaining);
1251 
1252                     if constexpr (!has_option<n2216>) {
1253                         if (remaining > 1) {
1254                             ++tr << "ambiguous 'next'\n";
1255                             overrider->next = &m.ambiguous;
1256                             continue;
1257                         }
1258                     }
1259 
1260                     auto next_overrider = overriders[pick];
1261                     overrider->next = next_overrider;
1262 
1263                     ++tr << "-> #" << next_overrider->spec_index << " "
1264                          << type_name(next_overrider->info->type)
1265                          << " pf = " << next_overrider->info->pf;
1266 
1267                     if (remaining > 1) {
1268                         tr << " (ambiguous)";
1269                         // do not increment m.report.ambiguous, for same reason
1270                     }
1271 
1272                     tr << "\n";
1273                 }
1274             }
1275         } else {
1276             build_dispatch_table(
1277                 m, dim - 1, group_iter - 1, mask,
1278                 concrete && group.has_concrete_classes);
1279         }
1280 
1281         ++group_index;
1282     }
1283 }
1284 
1285 inline void detail::generic_compiler::accumulate(
1286     const method_report& partial, report& total) {
1287     total.cells += partial.cells;
1288     total.not_implemented += partial.not_implemented != 0;
1289     total.ambiguous += partial.ambiguous != 0;
1290 }
1291 
1292 template<class... Policies>
1293 template<class... Options>
1294 void registry<Policies...>::compiler<Options...>::write_global_data() {
1295     using namespace policies;
1296     using namespace detail;
1297 
1298     auto dispatch_data_size = std::accumulate(
1299         methods.begin(), methods.end(), std::size_t(0),
1300         [](std::size_t sum, const method& m) {
1301             // msvc doesn't like (auto sum, auto& m) (C2187), go figure...
1302             return sum + m.dispatch_table.size();
1303         });
1304     dispatch_data_size = std::accumulate(
1305         classes.begin(), classes.end(), dispatch_data_size,
1306         [](auto sum, const auto& cls) { return sum + cls.vtbl.size(); });
1307 
1308     std::vector<detail::word> new_dispatch_data(dispatch_data_size);
1309     auto gv_first = new_dispatch_data.data();
1310     [[maybe_unused]] auto gv_last = gv_first + dispatch_data_size;
1311     auto gv_iter = gv_first;
1312 
1313     ++tr << "Initializing multi-method dispatch tables at " << gv_iter << "\n";
1314 
1315     for (auto& m : methods) {
1316         if (m.info->arity() == 1) {
1317             // Uni-methods just need an index in the method table.
1318             m.info->slots_strides_ptr[0] = m.slots[0];
1319         } else {
1320             auto strides_iter = std::copy(
1321                 m.slots.begin(), m.slots.end(), m.info->slots_strides_ptr);
1322             std::copy(m.strides.begin(), m.strides.end(), strides_iter);
1323 
1324             if constexpr (has_trace) {
1325                 ++tr << rflush(4, dispatch_data_size) << " " << " method #"
1326                      << m.dispatch_table[0]->method_index << " "
1327                      << type_name(m.info->method_type_id) << "\n";
1328                 indent _(tr);
1329 
1330                 for (auto& entry : m.dispatch_table) {
1331                     ++tr << "spec #" << entry->spec_index << " "
1332                          << spec_name(m, entry) << "\n";
1333                 }
1334             }
1335 
1336             m.gv_dispatch_table = gv_iter;
1337             BOOST_ASSERT(gv_iter + m.dispatch_table.size() <= gv_last);
1338             gv_iter = std::transform(
1339                 m.dispatch_table.begin(), m.dispatch_table.end(), gv_iter,
1340                 [](auto spec) { return spec->pf; });
1341         }
1342     }
1343 
1344     ++tr << "Setting 'next' pointers\n";
1345 
1346     for (auto& m : methods) {
1347         indent _(tr);
1348         ++tr << "method #" << " " << type_name(m.info->method_type_id) << "\n";
1349 
1350         for (auto& overrider : m.overriders) {
1351             if (overrider.next) {
1352                 ++tr << "#" << overrider.spec_index << " "
1353                      << spec_name(m, &overrider) << " -> ";
1354 
1355                 tr << "#" << overrider.next->spec_index << " "
1356                    << spec_name(m, overrider.next);
1357                 *overrider.info->next =
1358                     reinterpret_cast<void (*)()>(overrider.next->pf);
1359             } else {
1360                 tr << "none";
1361             }
1362 
1363             tr << "\n";
1364         }
1365     }
1366 
1367     ++tr << "Initializing v-tables at " << gv_iter << "\n";
1368 
1369     for (auto& cls : classes) {
1370         *cls.static_vptr = gv_iter - cls.first_slot;
1371 
1372         ++tr << rflush(4, gv_iter - gv_first) << " " << gv_iter << " vtbl for "
1373              << cls << " slots " << cls.first_slot << "-"
1374              << (cls.first_slot + cls.vtbl.size() - 1) << "\n";
1375         indent _(tr);
1376 
1377         for (auto& entry : cls.vtbl) {
1378             ++tr << "method #" << entry.method_index << " ";
1379             auto& method = methods[entry.method_index];
1380 
1381             if (method.arity() == 1) {
1382                 auto spec = method.dispatch_table[entry.group_index];
1383                 tr << "spec #" << spec->spec_index << "\n";
1384                 indent _(tr);
1385                 ++tr << type_name(method.info->method_type_id) << "\n";
1386                 ++tr << spec_name(method, spec);
1387                 BOOST_ASSERT(gv_iter + 1 <= gv_last);
1388                 *gv_iter++ = spec->pf;
1389             } else {
1390                 tr << "vp #" << entry.vp_index << " group #"
1391                    << entry.group_index << "\n";
1392                 indent _2(tr);
1393                 ++tr << type_name(method.info->method_type_id);
1394                 BOOST_ASSERT(gv_iter + 1 <= gv_last);
1395 
1396                 if (entry.vp_index == 0) {
1397                     *gv_iter++ = std::uintptr_t(
1398                         method.gv_dispatch_table + entry.group_index);
1399                 } else {
1400                     *gv_iter++ = entry.group_index;
1401                 }
1402             }
1403 
1404             tr << "\n";
1405         }
1406     }
1407 
1408     ++tr << rflush(4, dispatch_data_size) << " " << gv_iter << " end\n";
1409 
1410     if constexpr (has_vptr) {
1411         vptr::initialize(*this, options);
1412     }
1413 
1414     new_dispatch_data.swap(dispatch_data);
1415 }
1416 
1417 template<class... Policies>
1418 template<class... Options>
1419 void registry<Policies...>::compiler<Options...>::select_dominant_overriders(
1420     std::vector<overrider*>& candidates, std::size_t& pick,
1421     std::size_t& remaining) {
1422 
1423     pick = 0;
1424     remaining = 0;
1425 
1426     for (size_t i = 0; i < candidates.size(); ++i) {
1427         if (candidates[i]) {
1428             for (size_t j = i + 1; j < candidates.size(); ++j) {
1429                 if (candidates[j]) {
1430                     if (is_more_specific(candidates[i], candidates[j])) {
1431                         candidates[j] = nullptr;
1432                     } else if (is_more_specific(candidates[j], candidates[i])) {
1433                         candidates[i] = nullptr;
1434                         break; // this one is dead
1435                     }
1436                 }
1437             }
1438         }
1439 
1440         if (candidates[i]) {
1441             pick = i;
1442             ++remaining;
1443         }
1444     }
1445 
1446     if (remaining <= 1) {
1447         return;
1448     }
1449 
1450     if constexpr (has_option<n2216>) {
1451         if (!candidates[pick]->covariant_return_type) {
1452             return;
1453         }
1454 
1455         remaining = 0;
1456 
1457         for (size_t i = 0; i < candidates.size(); ++i) {
1458             if (candidates[i]) {
1459                 for (size_t j = i + 1; j < candidates.size(); ++j) {
1460                     if (candidates[j]) {
1461                         BOOST_ASSERT(candidates[i] != candidates[j]);
1462 
1463                         if (candidates[i]->covariant_return_type->is_base_of(
1464                                 candidates[j]->covariant_return_type)) {
1465                             candidates[i] = nullptr;
1466                         } else if (candidates[j]
1467                                        ->covariant_return_type->is_base_of(
1468                                            candidates[i]
1469                                                ->covariant_return_type)) {
1470                             candidates[j] = nullptr;
1471                         }
1472                     }
1473                 }
1474             }
1475 
1476             if (candidates[i]) {
1477                 pick = i;
1478                 ++remaining;
1479             }
1480         }
1481     }
1482 }
1483 
1484 template<class... Policies>
1485 template<class... Options>
1486 auto registry<Policies...>::compiler<Options...>::is_more_specific(
1487     const overrider* a, const overrider* b) -> bool {
1488     bool result = false;
1489 
1490     auto a_iter = a->vp.begin(), a_last = a->vp.end(), b_iter = b->vp.begin();
1491 
1492     for (; a_iter != a_last; ++a_iter, ++b_iter) {
1493         if (*a_iter != *b_iter) {
1494             if ((*b_iter)->transitive_derived.find(*a_iter) !=
1495                 (*b_iter)->transitive_derived.end()) {
1496                 result = true;
1497             } else if (
1498                 (*a_iter)->transitive_derived.find(*b_iter) !=
1499                 (*a_iter)->transitive_derived.end()) {
1500                 return false;
1501             }
1502         }
1503     }
1504 
1505     return result;
1506 }
1507 
1508 template<class... Policies>
1509 template<class... Options>
1510 auto registry<Policies...>::compiler<Options...>::is_base(
1511     const overrider* a, const overrider* b) -> bool {
1512     bool result = false;
1513 
1514     auto a_iter = a->vp.begin(), a_last = a->vp.end(), b_iter = b->vp.begin();
1515 
1516     for (; a_iter != a_last; ++a_iter, ++b_iter) {
1517         if (*a_iter != *b_iter) {
1518             if ((*a_iter)->transitive_derived.find(*b_iter) ==
1519                 (*a_iter)->transitive_derived.end()) {
1520                 return false;
1521             } else {
1522                 result = true;
1523             }
1524         }
1525     }
1526 
1527     return result;
1528 }
1529 
1530 template<class... Policies>
1531 template<class... Options>
1532 void registry<Policies...>::compiler<Options...>::print(
1533     const method_report& r) const {
1534     ++tr;
1535 
1536     if (r.cells) {
1537         // only for multi-methods, uni-methods don't have dispatch tables
1538         ++tr << r.cells << " dispatch table cells, ";
1539     }
1540 
1541     tr << r.not_implemented << " not implemented, " << r.ambiguous
1542        << " ambiguous\n";
1543 }
1544 
1545 //! Initialize a registry.
1546 //!
1547 //! Initialize the @ref registry passed as an explicit function template
1548 //! argument, or @ref default_registry if the registry is not specified. The
1549 //! default can be changed by defining {{BOOST_OPENMETHOD_DEFAULT_REGISTRY}}.
1550 //! Option objects can be passed to change the behavior of the function.
1551 //! Currently two options exist:
1552 //! @li @ref trace Enable tracing of the initialization process.
1553 //! @li @ref n2216 Enable resolution of ambiguities according to the N2216
1554 //! paper.
1555 //!
1556 //! `initialize` must be called, typically at the beginning of `main`, before
1557 //! using any of the methods in a registry. It sets up the v-tables,
1558 //! multi-method dispatch tables, and any other data required by the policies.
1559 //!
1560 //! The function returns an object of an unspecified type that contains a
1561 //! `report` member, itself an object of an unspecified type, thatcontains the
1562 //! following members:
1563 //! @li `std::size_t cells`: The number of cells in all multi-method dispatch
1564 //! tables.
1565 //! @li `std::size_t not_implemented`: The number of multi-method dispatch tables that
1566 //! contain at least one not implemented entry.
1567 //! @li `std::size_t ambiguous`: The number of multi-method dispatch tables that contain at
1568 //! least one ambiguous entry.
1569 //!
1570 //! @note
1571 //! A translation unit that calls `initialize` must include the
1572 //! `<boost/openmethod/initialize.hpp>` header.
1573 //!
1574 //! @tparam Registry The registry to initialize.
1575 //! @tparam Options... Zero or more option types, deduced from the function
1576 //! arguments.
1577 //! @param options Zero or more option objects.
1578 //! @return An object of an unspecified type.
1579 //!
1580 //! @par Errors
1581 //!
1582 //! @li @ref missing_class: A class used in a virtual parameter was not
1583 //! registered.
1584 //! @li The registry's policies may report additional errors.
1585 //!
1586 //! @par Example
1587 //!
1588 //! Initialize the default registry with tracing enabled, and exit with an error
1589 //! message if there were any possibility of a @reg bad_call error. User may run
1590 //! the program again after setting environment variable
1591 //! `BOOST_OPENMETHOD_TRACE` to `1` to troubleshoot.
1592 //!
1593 //! @code
1594 //! #include <iostream>
1595 //!
1596 //! #include <boost/openmethod.hpp>
1597 //! #include <boost/openmethod/initialize.hpp>
1598 //!
1599 //! int main() {
1600 //!     namespace bom = boost::openmethod;
1601 //!     auto report = bom::initialize(bom::trace::from_env()).report;
1602 //!
1603 //!     if (report.not_implemented != 0 || report.ambiguous != 0) {
1604 //!         std::cerr << "missing overriders or ambiguous methods\n";
1605 //!         return 1;
1606 //!     }
1607 //!
1608 //!     // ...
1609 //! }
1610 //! @endcode
1611 template<class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY, class... Options>
1612 inline auto initialize(Options&&... options) {
1613     if (detail::odr_check<Registry>::count > 1) {
1614         // Multiple definitions of default_registry detected.
1615         // This indicates an ODR violation.
1616         // Signal a final_error using the error handler, then abort.
1617         if constexpr (Registry::has_error_handler) {
1618             Registry::error_handler::error(odr_violation());
1619         }
1620 
1621         std::abort();
1622     }
1623 
1624     typename Registry::template compiler<Options...> comp(
1625         std::forward<Options>(options)...);
1626     comp.initialize();
1627 
1628     return comp;
1629 }
1630 
1631 namespace detail {
1632 
1633 template<typename, class Policy, class... Options>
1634 struct has_finalize_aux : std::false_type {};
1635 
1636 template<class Policy, class... Options>
1637 struct has_finalize_aux<
1638     std::void_t<decltype(Policy::finalize(
1639         std::declval<std::tuple<Options...>>()))>,
1640     Policy, Options...> : std::true_type {};
1641 
1642 } // namespace detail
1643 
1644 template<class... Policies>
1645 template<class... Options>
1646 auto registry<Policies...>::finalize(Options... opts) -> void {
1647     std::tuple<Options...> options(opts...); // gcc-8 doesn't like CTAD here
1648     mp11::mp_for_each<policy_list>([&options](auto policy) {
1649         using fn = typename decltype(policy)::template fn<registry>;
1650         if constexpr (detail::has_finalize_aux<void, fn, Options...>::value) {
1651             fn::finalize(options);
1652         }
1653     });
1654 
1655     dispatch_data.clear();
1656     initialized = false;
1657 }
1658 
1659 //! Release resources held by registry.
1660 //!
1661 //! `finalize` may be called to release any resources allocated by
1662 //! @ref registry::initialize.
1663 //!
1664 //! @note
1665 //! A translation unit that contains a call to `finalize` must include the
1666 //! `<boost/openmethod/initialize.hpp>` header.
1667 //!
1668 //! @tparam Registry The registry to finalize.
1669 //! @tparam Options... Zero or more option types, deduced from the function
1670 //! arguments.
1671 //! @param options Zero or more option objects.
1672 template<class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY, class... Options>
1673 inline auto finalize(Options&&... opts) -> void {
1674     Registry::finalize(std::forward<Options>(opts)...);
1675 }
1676 
1677 } // namespace boost::openmethod
1678 
1679 #ifdef _MSC_VER
1680 #pragma warning(pop)
1681 #endif
1682 
1683 #endif