From c94e8b806bc1b5efadd6fd98604ba4cf924f7a2d Mon Sep 17 00:00:00 2001
From: Mingxin Wang <mingxwa@microsoft.com>
Date: Sat, 28 Sep 2024 17:35:43 +0800
Subject: [PATCH] Reduce semantics of proxy_invoke

---
 docs/PRO_DEF_FREE_DISPATCH.md                 |  2 +-
 docs/PRO_DEF_MEM_DISPATCH.md                  |  2 +-
 docs/access_proxy.md                          |  2 +-
 docs/conversion_dispatch/accessor.md          |  2 +-
 docs/operator_dispatch/accessor.md            | 10 ++--
 docs/proxy_invoke.md                          | 14 ++---
 proxy.h                                       | 55 +++++++++----------
 samples/access_proxy.cpp                      |  2 +-
 ..._msft_lib_proxy.cpp => msft_lib_proxy.cpp} |  2 +-
 samples/proxy_invoke.cpp                      |  2 +-
 10 files changed, 44 insertions(+), 49 deletions(-)
 rename samples/{__msft_lib_proxy.cpp => msft_lib_proxy.cpp} (83%)

diff --git a/docs/PRO_DEF_FREE_DISPATCH.md b/docs/PRO_DEF_FREE_DISPATCH.md
index a57614bf..60d96170 100644
--- a/docs/PRO_DEF_FREE_DISPATCH.md
+++ b/docs/PRO_DEF_FREE_DISPATCH.md
@@ -36,7 +36,7 @@ struct dispatch_name {
   template <class F, class C, class R, class... Args>
   struct accessor<F, C, R(Args...) cv ref noex> {
     friend R accessibility_func_name(accessor cv ref self, Args... args) noex {
-      return pro::proxy_invoke<C>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
+      return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
     }
   };
 };
diff --git a/docs/PRO_DEF_MEM_DISPATCH.md b/docs/PRO_DEF_MEM_DISPATCH.md
index c4ef01f9..1857974b 100644
--- a/docs/PRO_DEF_MEM_DISPATCH.md
+++ b/docs/PRO_DEF_MEM_DISPATCH.md
@@ -39,7 +39,7 @@ struct dispatch_name {
   template <class F, class C, class R, class... Args>
   struct accessor<F, C, R(Args...) cv ref noex> {
     R accessibility_func_name(Args... args) cv ref noex {
-      return pro::proxy_invoke<C>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
+      return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
     }
   };
 };
diff --git a/docs/access_proxy.md b/docs/access_proxy.md
index 33425939..d9f2ded4 100644
--- a/docs/access_proxy.md
+++ b/docs/access_proxy.md
@@ -55,7 +55,7 @@ int main() {
   pro::proxy<Stringable>& p2 = pro::access_proxy<Stringable>(a);
   std::cout << std::boolalpha << (&p == &p2) << "\n";  // Prints: "true" because access_proxy converts
                                                        // an accessor back to the original proxy
-  auto result = pro::proxy_invoke<Convention>(p2);
+  auto result = pro::proxy_invoke<Convention, std::string()>(p2);
   std::cout << result << "\n";  // Prints: "123"
 }
 ```
diff --git a/docs/conversion_dispatch/accessor.md b/docs/conversion_dispatch/accessor.md
index 94246f91..de894b6b 100644
--- a/docs/conversion_dispatch/accessor.md
+++ b/docs/conversion_dispatch/accessor.md
@@ -27,4 +27,4 @@ Let `SELF` be `std::forward<accessor cv ref>(*this)`.
 
 `(2)` When `sizeof...(Os)` is greater than `1`, and `accessor<F, C, Os>...` are trivial types, inherits all `accessor<F, C, Os>...` types and `using` their `operator T`.
 
-`(3)` When `sizeof...(Os)` is `1` and the only type `O` in `Os` is `T() cv ref noex`, provides an explicit (when `Expl` is `true`) or implicit (when `Expl` is `false`) `operator T()` with the same *cv ref noex* specifiers. `accessor::operator T()` is equivalent to `return proxy_invoke<C>(access_proxy<F>(SELF))`.
+`(3)` When `sizeof...(Os)` is `1` and the only type `O` in `Os` is `T() cv ref noex`, provides an explicit (when `Expl` is `true`) or implicit (when `Expl` is `false`) `operator T()` with the same *cv ref noex* specifiers. `accessor::operator T()` is equivalent to `return proxy_invoke<C, T() cv ref noex>(access_proxy<F>(SELF))`.
diff --git a/docs/operator_dispatch/accessor.md b/docs/operator_dispatch/accessor.md
index 64eda384..6b9effc8 100644
--- a/docs/operator_dispatch/accessor.md
+++ b/docs/operator_dispatch/accessor.md
@@ -39,7 +39,7 @@ struct accessor<F, C, R(Args...) cv ref noex> {
 }
 ```
 
-`(3)` Provides an `operator sop(Args...)` with the same *cv ref noex* specifiers as of the overload type. `accessor::operator sop(Args...)` is equivalent to `return proxy_invoke<C>(access_proxy<F>(SELF), std::forward<Args>(args)...)`.
+`(3)` Provides an `operator sop(Args...)` with the same *cv ref noex* specifiers as of the overload type. `accessor::operator sop(Args...)` is equivalent to `return proxy_invoke<C, R(Args...) cv ref noex>(access_proxy<F>(SELF), std::forward<Args>(args)...)`.
 
 ### `!` and `~`
 
@@ -53,7 +53,7 @@ struct accessor<F, C, R() cv ref noex> {
 }
 ```
 
-`(4)` Provides an `operator sop()` with the same *cv ref noex* specifiers as of the overload type. `accessor::operator sop()` is equivalent to `return proxy_invoke<C>(access_proxy<F>(SELF))`.
+`(4)` Provides an `operator sop()` with the same *cv ref noex* specifiers as of the overload type. `accessor::operator sop()` is equivalent to `return proxy_invoke<C, R() cv ref noex>(access_proxy<F>(SELF))`.
 
 ### Assignment SOPs
 
@@ -67,7 +67,7 @@ struct accessor<F, C, R(Arg) cv ref noex> {
 }
 ```
 
-`(4)` Provides an `operator sop(Arg)` with the same *cv ref noex* specifiers as of the overload type. `accessor::operator sop(Arg)` calls `proxy_invoke<C>(access_proxy<F>(SELF), std::forward<Arg>(arg))` and returns `access_proxy<F>(SELF)` when `C::is_direct` is `true`, or otherwise, returns `*access_proxy<F>(SELF)` when `C::is_direct` is `false`.
+`(4)` Provides an `operator sop(Arg)` with the same *cv ref noex* specifiers as of the overload type. `accessor::operator sop(Arg)` calls `proxy_invoke<C, R(Arg) cv ref noex>(access_proxy<F>(SELF), std::forward<Arg>(arg))` and returns `access_proxy<F>(SELF)` when `C::is_direct` is `true`, or otherwise, returns `*access_proxy<F>(SELF)` when `C::is_direct` is `false`.
 
 ## Right-Hand-Side Operand Specializations
 
@@ -94,7 +94,7 @@ struct accessor<F, C, R(Arg) cv ref noex> {
 }
 ```
 
-`(7)` Provides a `friend operator sop(Arg arg, accessor cv ref)` with the same *noex* specifiers as of the overload type. `accessor::operator sop(Arg arg, accessor cv ref)` is equivalent to `return proxy_invoke<C>(access_proxy<F>(SELF), std::forward<Arg>(arg))`.
+`(7)` Provides a `friend operator sop(Arg arg, accessor cv ref)` with the same *noex* specifiers as of the overload type. `accessor::operator sop(Arg arg, accessor cv ref)` is equivalent to `return proxy_invoke<C, R(Arg) cv ref noex>(access_proxy<F>(SELF), std::forward<Arg>(arg))`.
 
 ### Assignment SOPs
 
@@ -108,4 +108,4 @@ struct accessor<F, C, R(Arg) cv ref noex> {
 }
 ```
 
-`(8)` Provides a `friend operator sop(Arg arg, accessor cv ref)` with the same *noex* specifiers as of the overload type. `accessor::operator sop(Arg arg, accessor cv ref)` calls `proxy_invoke<C>(access_proxy<F>(SELF), std::forward<Arg>(arg))` and returns `access_proxy<F>(SELF)` when `C::is_direct` is `true`, or otherwise, returns `*access_proxy<F>(SELF)` when `C::is_direct` is `false`.
+`(8)` Provides a `friend operator sop(Arg arg, accessor cv ref)` with the same *noex* specifiers as of the overload type. `accessor::operator sop(Arg arg, accessor cv ref)` calls `proxy_invoke<C, R(Arg) cv ref noex>(access_proxy<F>(SELF), std::forward<Arg>(arg))` and returns `access_proxy<F>(SELF)` when `C::is_direct` is `true`, or otherwise, returns `*access_proxy<F>(SELF)` when `C::is_direct` is `false`.
diff --git a/docs/proxy_invoke.md b/docs/proxy_invoke.md
index 378a8c92..3495a925 100644
--- a/docs/proxy_invoke.md
+++ b/docs/proxy_invoke.md
@@ -1,22 +1,22 @@
 # Function template `proxy_invoke`
 
 ```cpp
-template <class C, class F, class... Args>
+template <class C, class O, class F, class... Args>
 /* see below */ proxy_invoke(proxy<F>& p, Args&&... args);
 
-template <class C, class F, class... Args>
+template <class C, class O, class F, class... Args>
 /* see below */ proxy_invoke(const proxy<F>& p, Args&&... args);
 
-template <class C, class F, class... Args>
+template <class C, class O, class F, class... Args>
 /* see below */ proxy_invoke(proxy<F>&& p, Args&&... args);
 
-template <class C, class F, class... Args>
+template <class C, class O, class F, class... Args>
 /* see below */ proxy_invoke(const proxy<F>&& p, Args&&... args);
 ```
 
-Invokes a `proxy` with a specified convention type and arguments. `C` is required to be defined in `typename F::convention_types`. Overload resolution is performed among the overload types defined in `typename C::overload_types`.
+Invokes a `proxy` with a specified convention type, an overload type, and arguments. `C` is required to be defined in `typename F::convention_types`. `O` is required to be defined in `typename C::overload_types`.
 
-Let `ptr` be the contained value of `p` with the same cv ref-qualifiers, `O` be the matched overload type among the overload types defined in `typename C::overload_types`, `Args2...` be the argument types of `O`, `R` be the return type of `O`,
+Let `ptr` be the contained value of `p` with the same cv ref-qualifiers, `Args2...` be the argument types of `O`, `R` be the return type of `O`,
 
 - if `C::is_direct` is `true`, let `v` be `std::forward<decltype(ptr)>(ptr)`, or otherwise,
 - if `C::is_direct` is `false`, let `v` be `*std::forward<decltype(ptr)>(ptr)`,
@@ -55,7 +55,7 @@ int main() {
   std::cout << ToString(*p) << "\n";  // Invokes with accessor, prints: "123"
 
   using C = std::tuple_element_t<0u, Stringable::convention_types>;
-  std::cout << pro::proxy_invoke<C>(p) << "\n";  // Invokes with proxy_invoke, also prints: "123"
+  std::cout << pro::proxy_invoke<C, std::string() const>(p) << "\n";  // Invokes with proxy_invoke, also prints: "123"
 }
 ```
 
diff --git a/proxy.h b/proxy.h
index e92e2fb9..1c4e488c 100644
--- a/proxy.h
+++ b/proxy.h
@@ -259,9 +259,6 @@ struct overload_traits_impl : applicable_traits {
       }
     }
   };
-  struct resolver {
-    overload_traits_impl operator()(add_qualifier_t<std::byte, Q>, Args...);
-  };
 
   template <bool IS_DIRECT, class D, class P>
   static constexpr bool applicable_ptr =
@@ -350,13 +347,8 @@ struct conv_traits_impl : inapplicable_traits {};
 template <class C, class... Os>
     requires(sizeof...(Os) > 0u && (overload_traits<Os>::applicable && ...))
 struct conv_traits_impl<C, Os...> : applicable_traits {
-  struct overload_resolver : overload_traits<Os>::resolver...
-      { using overload_traits<Os>::resolver::operator()...; };
   using meta = composite_meta_impl<dispatcher_meta<typename overload_traits<Os>
       ::template meta_provider<C::is_direct, typename C::dispatch_type>>...>;
-  template <qualifier_type Q, class... Args>
-  using matched_overload_traits = std::invoke_result_t<
-      overload_resolver, add_qualifier_t<std::byte, Q>, Args...>;
 
   template <class P>
   static constexpr bool applicable_ptr =
@@ -575,15 +567,14 @@ template <class F>
 struct proxy_helper {
   static inline const auto& get_meta(const proxy<F>& p) noexcept
       { return *p.meta_.operator->(); }
-  template <class C, qualifier_type Q, class... Args>
+  template <class C, class O, qualifier_type Q, class... Args>
   static decltype(auto) invoke(add_qualifier_t<proxy<F>, Q> p, Args&&... args) {
-    using OverloadTraits = typename conv_traits<C>
-        ::template matched_overload_traits<Q, Args...>;
-    auto dispatcher = p.meta_->template dispatcher_meta<typename OverloadTraits
+    auto dispatcher = p.meta_
+        ->template dispatcher_meta<typename overload_traits<O>
         ::template meta_provider<C::is_direct, typename C::dispatch_type>>
         ::dispatcher;
     if constexpr (C::is_direct &&
-        OverloadTraits::qualifier == qualifier_type::rv) {
+        overload_traits<O>::qualifier == qualifier_type::rv) {
       meta_ptr_reset_guard guard{p.meta_};
       return dispatcher(std::forward<add_qualifier_t<std::byte, Q>>(*p.ptr_),
           std::forward<Args>(args)...);
@@ -808,26 +799,26 @@ class proxy : public details::facade_traits<F>::direct_accessor {
   alignas(F::constraints.max_align) std::byte ptr_[F::constraints.max_size];
 };
 
-template <class C, class F, class... Args>
+template <class C, class O, class F, class... Args>
 decltype(auto) proxy_invoke(proxy<F>& p, Args&&... args) {
   return details::proxy_helper<F>::template invoke<
-      C, details::qualifier_type::lv>(p, std::forward<Args>(args)...);
+      C, O, details::qualifier_type::lv>(p, std::forward<Args>(args)...);
 }
-template <class C, class F, class... Args>
+template <class C, class O, class F, class... Args>
 decltype(auto) proxy_invoke(const proxy<F>& p, Args&&... args) {
   return details::proxy_helper<F>::template invoke<
-      C, details::qualifier_type::const_lv>(p, std::forward<Args>(args)...);
+      C, O, details::qualifier_type::const_lv>(p, std::forward<Args>(args)...);
 }
-template <class C, class F, class... Args>
+template <class C, class O, class F, class... Args>
 decltype(auto) proxy_invoke(proxy<F>&& p, Args&&... args) {
   return details::proxy_helper<F>::template invoke<
-      C, details::qualifier_type::rv>(
+      C, O, details::qualifier_type::rv>(
       std::forward<proxy<F>>(p), std::forward<Args>(args)...);
 }
-template <class C, class F, class... Args>
+template <class C, class O, class F, class... Args>
 decltype(auto) proxy_invoke(const proxy<F>&& p, Args&&... args) {
   return details::proxy_helper<F>::template invoke<
-      C, details::qualifier_type::const_rv>(
+      C, O, details::qualifier_type::const_rv>(
       std::forward<const proxy<F>>(p), std::forward<Args>(args)...);
 }
 
@@ -1167,7 +1158,7 @@ struct facade_impl {
     struct accessor<F2, C, proxy<F>() Q> { \
       __VA_ARGS__ () Q { \
         if (access_proxy<F2>(SELF).has_value()) { \
-          return proxy_invoke<C>(access_proxy<F2>(SELF)); \
+          return proxy_invoke<C, proxy<F>() Q>(access_proxy<F2>(SELF)); \
         } \
         return nullptr; \
       } \
@@ -1323,13 +1314,14 @@ struct operator_dispatch;
 #define ___PRO_DEF_LHS_LEFT_OP_ACCESSOR(Q, SELF, ...) \
     template <class F, class C, class R> \
     struct accessor<F, C, R() Q> { \
-      R __VA_ARGS__ () Q { return proxy_invoke<C>(access_proxy<F>(SELF)); } \
+      R __VA_ARGS__ () Q \
+          { return proxy_invoke<C, R() Q>(access_proxy<F>(SELF)); } \
     }
 #define ___PRO_DEF_LHS_ANY_OP_ACCESSOR(Q, SELF, ...) \
     template <class F, class C, class R, class... Args> \
     struct accessor<F, C, R(Args...) Q> { \
       R __VA_ARGS__ (Args... args) Q { \
-        return proxy_invoke<C>( \
+        return proxy_invoke<C, R(Args...) Q>( \
             access_proxy<F>(SELF), std::forward<Args>(args)...); \
       } \
     }
@@ -1367,7 +1359,7 @@ struct operator_dispatch;
     template <class F, class C, class R, class Arg> \
     struct accessor<F, C, R(Arg) Q> { \
       friend R __VA_ARGS__ (Arg arg, SELF) NE { \
-        return proxy_invoke<C>( \
+        return proxy_invoke<C, R(Arg) Q>( \
             access_proxy<F>(FW_SELF), std::forward<Arg>(arg)); \
       } \
     }
@@ -1394,7 +1386,8 @@ struct operator_dispatch;
     template <class F, class C, class R, class Arg> \
     struct accessor<F, C, R(Arg) Q> { \
       decltype(auto) __VA_ARGS__ (Arg arg) Q { \
-        proxy_invoke<C>(access_proxy<F>(SELF), std::forward<Arg>(arg)); \
+        proxy_invoke<C, R(Arg) Q>( \
+            access_proxy<F>(SELF), std::forward<Arg>(arg)); \
         if constexpr (C::is_direct) { \
           return access_proxy<F>(SELF); \
         } else { \
@@ -1406,7 +1399,7 @@ struct operator_dispatch;
     template <class F, class C, class R, class Arg> \
     struct accessor<F, C, R(Arg&) Q> { \
       friend Arg& __VA_ARGS__ (Arg& arg, SELF) NE { \
-        proxy_invoke<C>(access_proxy<F>(FW_SELF), arg); \
+        proxy_invoke<C, R(Arg&) Q>(access_proxy<F>(FW_SELF), arg); \
         return arg; \
       } \
     }
@@ -1510,7 +1503,7 @@ struct operator_dispatch<"[]", false> {
     template <class F, class C> \
     struct accessor<F, C, T() Q> { \
       explicit(Expl) __VA_ARGS__ () Q \
-          { return proxy_invoke<C>(access_proxy<F>(SELF)); } \
+          { return proxy_invoke<C, T() Q>(access_proxy<F>(SELF)); } \
     }
 template <class T, bool Expl = true>
 struct conversion_dispatch {
@@ -1537,7 +1530,8 @@ struct conversion_dispatch {
     template <class __F, class __C, class __R, class... __Args> \
     struct accessor<__F, __C, __R(__Args...) __Q> { \
       __R __VA_ARGS__ (__Args... __args) __Q { \
-        return ::pro::proxy_invoke<__C>(::pro::access_proxy<__F>(__SELF), \
+        return ::pro::proxy_invoke<__C, __R(__Args...) __Q>( \
+            ::pro::access_proxy<__F>(__SELF), \
             ::std::forward<__Args>(__args)...); \
       } \
     }
@@ -1560,7 +1554,8 @@ struct conversion_dispatch {
     template <class __F, class __C, class __R, class... __Args> \
     struct accessor<__F, __C, __R(__Args...) __Q> { \
       friend __R __VA_ARGS__ (__SELF, __Args... __args) __NE { \
-        return ::pro::proxy_invoke<__C>(::pro::access_proxy<__F>(__FW_SELF), \
+        return ::pro::proxy_invoke<__C, __R(__Args...) __Q>( \
+            ::pro::access_proxy<__F>(__FW_SELF), \
             ::std::forward<__Args>(__args)...); \
       } \
     }
diff --git a/samples/access_proxy.cpp b/samples/access_proxy.cpp
index 5342bc82..7beaea3c 100644
--- a/samples/access_proxy.cpp
+++ b/samples/access_proxy.cpp
@@ -27,6 +27,6 @@ int main() {
   pro::proxy<Stringable>& p2 = pro::access_proxy<Stringable>(a);
   std::cout << std::boolalpha << (&p == &p2) << "\n";  // Prints: "true" because access_proxy converts
                                                        // an accessor back to the original proxy
-  auto result = pro::proxy_invoke<Convention>(p2);
+  auto result = pro::proxy_invoke<Convention, std::string()>(p2);
   std::cout << result << "\n";  // Prints: "123"
 }
diff --git a/samples/__msft_lib_proxy.cpp b/samples/msft_lib_proxy.cpp
similarity index 83%
rename from samples/__msft_lib_proxy.cpp
rename to samples/msft_lib_proxy.cpp
index 2856db11..4bbb0d91 100644
--- a/samples/__msft_lib_proxy.cpp
+++ b/samples/msft_lib_proxy.cpp
@@ -1,6 +1,6 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
-// This file contains example code from __msft_lib_proxy.md.
+// This file contains example code from msft_lib_proxy.md.
 
 #include <cstdio>
 
diff --git a/samples/proxy_invoke.cpp b/samples/proxy_invoke.cpp
index 13851673..44197c27 100644
--- a/samples/proxy_invoke.cpp
+++ b/samples/proxy_invoke.cpp
@@ -19,5 +19,5 @@ int main() {
   std::cout << ToString(*p) << "\n";  // Invokes with accessor, prints: "123"
 
   using C = std::tuple_element_t<0u, Stringable::convention_types>;
-  std::cout << pro::proxy_invoke<C>(p) << "\n";  // Invokes with proxy_invoke, also prints: "123"
+  std::cout << pro::proxy_invoke<C, std::string() const>(p) << "\n";  // Invokes with proxy_invoke, also prints: "123"
 }
