From 64620549cb0ed08b178e2acb4cb05866eddd3a15 Mon Sep 17 00:00:00 2001
From: Mingxin Wang <mingxwa@microsoft.com>
Date: Sun, 15 Dec 2024 10:48:23 +0800
Subject: [PATCH 1/2] Fix ADL

---
 docs/PRO_DEF_FREE_AS_MEM_DISPATCH.md          |   4 +-
 docs/PRO_DEF_FREE_DISPATCH.md                 |   6 +-
 docs/PRO_DEF_MEM_DISPATCH.md                  |   4 +-
 docs/explicit_conversion_dispatch/accessor.md |   4 +-
 docs/implicit_conversion_dispatch/accessor.md |   4 +-
 docs/operator_dispatch/accessor.md            |  18 +-
 docs/proxy.md                                 |   2 +-
 docs/proxy_indirect_accessor.md               |  18 ++
 docs/specifications.md                        |   1 +
 proxy.h                                       | 233 ++++++++++--------
 tests/proxy_invocation_tests.cpp              |  27 +-
 11 files changed, 188 insertions(+), 133 deletions(-)
 create mode 100644 docs/proxy_indirect_accessor.md

diff --git a/docs/PRO_DEF_FREE_AS_MEM_DISPATCH.md b/docs/PRO_DEF_FREE_AS_MEM_DISPATCH.md
index cb2e1d27..55f41ebc 100644
--- a/docs/PRO_DEF_FREE_AS_MEM_DISPATCH.md
+++ b/docs/PRO_DEF_FREE_AS_MEM_DISPATCH.md
@@ -16,7 +16,7 @@ PRO_DEF_FREE_AS_MEM_DISPATCH(dispatch_name, func_name, accessibility_func_name);
 
 `(1)` Equivalent to `PRO_DEF_FREE_AS_MEM_DISPATCH(dispatch_name, func_name, func_name);`
 
-`(2)` Defines a class named `dispatch_name` of free function call expressions of `func_name` with accessibility via a member function. `dispatch_name` meets the [*ProAccessible*](ProAccessible.md) requirements of types `F`, `C`, and `Os...`, where `F` models concept [`facade`](facade.md), `C` is a tuple element type defined in `typename F::convention_types`, and each type `O` (possibly qualified with *cv ref noex*) in `Os...` is a tuple element type defined in `typename C::overload_types`. The member functions provided by `typename dispatch_name::template accessor<F, C, Os...>` are named `accessibility_func_name`. Let `SELF` be `std::forward<accessor cv ref>(*this)`, effectively equivalent to:
+`(2)` Defines a class named `dispatch_name` of free function call expressions of `func_name` with accessibility via a member function. `dispatch_name` meets the [*ProAccessible*](ProAccessible.md) requirements of types `F`, `C`, and `Os...`, where `F` models concept [`facade`](facade.md), `C` is a tuple element type defined in `typename F::convention_types`, and each type `O` (possibly qualified with *cv ref noex*) in `Os...` is a tuple element type defined in `typename C::overload_types`. The member functions provided by `typename dispatch_name::template accessor<F, C, Os...>` are named `accessibility_func_name`. Effectively equivalent to:
 
 ```cpp
 struct dispatch_name {
@@ -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, R(Args...) cv ref noex>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
+      return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(std::forward<accessor cv ref>(*this)), std::forward<Args>(args)...);
     }
   };
 }
diff --git a/docs/PRO_DEF_FREE_DISPATCH.md b/docs/PRO_DEF_FREE_DISPATCH.md
index 07557cef..67e5e71a 100644
--- a/docs/PRO_DEF_FREE_DISPATCH.md
+++ b/docs/PRO_DEF_FREE_DISPATCH.md
@@ -16,7 +16,7 @@ PRO_DEF_FREE_DISPATCH(dispatch_name, func_name, accessibility_func_name);
 
 `(1)` Equivalent to `PRO_DEF_FREE_DISPATCH(dispatch_name, func_name, func_name);`
 
-`(2)` Defines a class named `dispatch_name` of free function call expressions of `func_name` with accessibility. `dispatch_name` meets the [*ProAccessible*](ProAccessible.md) requirements of types `F`, `C`, and `Os...`, where `F` models concept [`facade`](facade.md), `C` is a tuple element type defined in `typename F::convention_types`, and each type `O` (possibly qualified with *cv ref noex*) in `Os...` is a tuple element type defined in `typename C::overload_types`. The functions provided by `typename dispatch_name::template accessor<F, C, Os...>` are named `accessibility_func_name` and can be found by [argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl) when `accessor` is an associated class of the arguments. Let `SELF` be `std::forward<accessor cv ref>(self)`, effectively equivalent to:
+`(2)` Defines a class named `dispatch_name` of free function call expressions of `func_name` with accessibility. `dispatch_name` meets the [*ProAccessible*](ProAccessible.md) requirements of types `F`, `C`, and `Os...`, where `F` models concept [`facade`](facade.md), `C` is a tuple element type defined in `typename F::convention_types`, and each type `O` (possibly qualified with *cv ref noex*) in `Os...` is a tuple element type defined in `typename C::overload_types`. Let `accessor_arg` be `std::conditional_t<C::is_direct, proxy<F>, proxy_indirect_accessor<F>>`. The functions provided by `typename dispatch_name::template accessor<F, C, Os...>` are named `accessibility_func_name` and can be found by [argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl) when `accessor_arg` is an associated class of the arguments. Effectively equivalent to:
 
 ```cpp
 struct dispatch_name {
@@ -35,8 +35,8 @@ struct dispatch_name {
   struct accessor<F, C, Os...> : accessor<F, C, Os>... {};
   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, R(Args...) cv ref noex>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
+    friend R accessibility_func_name(accessor_arg cv ref self, Args... args) noex {
+      return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(std::forward<accessor_arg cv ref>(self)), std::forward<Args>(args)...);
     }
   };
 }
diff --git a/docs/PRO_DEF_MEM_DISPATCH.md b/docs/PRO_DEF_MEM_DISPATCH.md
index 8932d7be..f012aa57 100644
--- a/docs/PRO_DEF_MEM_DISPATCH.md
+++ b/docs/PRO_DEF_MEM_DISPATCH.md
@@ -16,7 +16,7 @@ PRO_DEF_MEM_DISPATCH(dispatch_name, func_name, accessibility_func_name);
 
 `(1)` Equivalent to `PRO_DEF_MEM_DISPATCH(dispatch_name, func_name, func_name);`
 
-`(2)` Defines a class named `dispatch_name` of member function call expressions of `func_name` with accessibility. `dispatch_name` meets the [*ProAccessible*](ProAccessible.md) requirements of types `F`, `C`, and `Os...`, where `F` models concept [`facade`](facade.md), `C` is a tuple element type defined in `typename F::convention_types`, and each type `O` (possibly qualified with *cv, ref, noex*) in `Os...` is a tuple element type defined in `typename C::overload_types`. The member functions provided by `typename dispatch_name::template accessor<F, C, Os...>` are named `accessibility_func_name`. Let `SELF` be `std::forward<accessor cv ref>(*this)`, effectively equivalent to:
+`(2)` Defines a class named `dispatch_name` of member function call expressions of `func_name` with accessibility. `dispatch_name` meets the [*ProAccessible*](ProAccessible.md) requirements of types `F`, `C`, and `Os...`, where `F` models concept [`facade`](facade.md), `C` is a tuple element type defined in `typename F::convention_types`, and each type `O` (possibly qualified with *cv, ref, noex*) in `Os...` is a tuple element type defined in `typename C::overload_types`. The member functions provided by `typename dispatch_name::template accessor<F, C, Os...>` are named `accessibility_func_name`. Effectively equivalent to:
 
 ```cpp
 struct dispatch_name {
@@ -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, R(Args...) cv ref noex>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
+      return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(std::forward<accessor cv ref>(*this)), std::forward<Args>(args)...);
     }
   };
 }
diff --git a/docs/explicit_conversion_dispatch/accessor.md b/docs/explicit_conversion_dispatch/accessor.md
index a81237dd..8d6716c4 100644
--- a/docs/explicit_conversion_dispatch/accessor.md
+++ b/docs/explicit_conversion_dispatch/accessor.md
@@ -21,10 +21,8 @@ struct accessor<F, C, T() cv ref noex> {
 };
 ```
 
-Let `SELF` be `std::forward<accessor cv ref>(*this)`.
-
 `(1)` The default implementation of `accessor` is not constructible.
 
 `(2)` When `sizeof...(Os)` is greater than `1`, and `accessor<F, C, Os>...` are default-constructible, inherits all `accessor<F, C, Os>...` types and `using` their `operator return-type-of<Os>`. `return-type-of<O>` denotes the *return type* of the overload type `O`.
 
-`(3)` When `sizeof...(Os)` is `1` and the only type `O` in `Os` is `T() cv ref noex`, provides an explicit  `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))`.
+`(3)` When `sizeof...(Os)` is `1` and the only type `O` in `Os` is `T() cv ref noex`, provides an explicit  `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>(std::forward<accessor cv ref>(*this)))`.
diff --git a/docs/implicit_conversion_dispatch/accessor.md b/docs/implicit_conversion_dispatch/accessor.md
index fa2ead8f..0b78300c 100644
--- a/docs/implicit_conversion_dispatch/accessor.md
+++ b/docs/implicit_conversion_dispatch/accessor.md
@@ -21,10 +21,8 @@ struct accessor<F, C, T() cv ref noex> {
 };
 ```
 
-Let `SELF` be `std::forward<accessor cv ref>(*this)`.
-
 `(1)` The default implementation of `accessor` is not constructible.
 
 `(2)` When `sizeof...(Os)` is greater than `1`, and `accessor<F, C, Os>...` are default-constructible, inherits all `accessor<F, C, Os>...` types and `using` their `operator return-type-of<Os>`. `return-type-of<O>` denotes the *return type* of the overload type `O`.
 
-`(3)` When `sizeof...(Os)` is `1` and the only type `O` in `Os` is `T() cv ref noex`, provides an implicit  `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))`.
+`(3)` When `sizeof...(Os)` is `1` and the only type `O` in `Os` is `T() cv ref noex`, provides an implicit  `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>(std::forward<accessor cv ref>(*this)))`.
diff --git a/docs/operator_dispatch/accessor.md b/docs/operator_dispatch/accessor.md
index 513f568c..69f1e548 100644
--- a/docs/operator_dispatch/accessor.md
+++ b/docs/operator_dispatch/accessor.md
@@ -25,7 +25,7 @@ struct accessor<F, C, Os...> : accessor<F, C, Os>... {
 
 `(2)` When `sizeof...(Os)` is greater than `1`, and `accessor<F, C, Os>...` are default-constructible types, inherits all `accessor<F, C, Os>...` types and `using` their `operator sop`.
 
-When `Rhs` is `false`, the other specializations are defined as follows, where `sizeof...(Os)` is `1` and the only type `O` qualified with `cv ref noex` (let `SELF` be `std::forward<accessor cv ref>(*this)`):
+When `Rhs` is `false`, the other specializations are defined as follows, where `sizeof...(Os)` is `1` and the only type `O` qualified with `cv ref noex` (let `ACCESS_PROXY_EXPR` be `access_proxy<F>(std::forward<accessor cv ref>(*this))`):
 
 ### Regular SOPs
 
@@ -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, R(Args...) cv ref noex>(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_EXPR, 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, R() cv ref noex>(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_EXPR)`.
 
 ### 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, 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`.
+`(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_EXPR, std::forward<Arg>(arg))` and returns `ACCESS_PROXY_EXPR` when `C::is_direct` is `true`, or otherwise, returns `*ACCESS_PROXY_EXPR` when `C::is_direct` is `false`.
 
 ## Right-Hand-Side Operand Specializations
 
@@ -80,7 +80,7 @@ struct accessor<F, C, Os...> : accessor<F, C, Os>... {};
 
 `(6)` When `sizeof...(Os)` is greater than `1`, and `accessor<F, C, Os>...` are default-constructible types, inherits all `accessor<F, C, Os>...` types.
 
-When `Rhs` is `true`, the other specializations are defined as follows, where `sizeof...(Os)` is `1` and the only type `O` qualified with `cv ref noex` (let `SELF` be `std::forward<accessor cv ref>(self)`):
+When `Rhs` is `true`, the other specializations are defined as follows, where `sizeof...(Os)` is `1` and the only type `O` qualified with `cv ref noex` (let `accessor_arg` be `std::conditional_t<C::is_direct, proxy<F>, proxy_indirect_accessor<F>>`, `ACCESS_PROXY_EXPR` be `access_proxy<F>(std::forward<accessor_arg cv ref>(self))`):
 
 ### Regular SOPs
 
@@ -90,11 +90,11 @@ When `Sign` is one of `"+"`, `"-"`, `"*"`, `"/"`, `"%"`, `"=="`, `"!="`, `">"`,
 // (7)
 template <class F, class C, class R, class Arg>
 struct accessor<F, C, R(Arg) cv ref noex> {
-  friend R operator sop (Arg arg, accessor cv ref self) noex;
+  friend R operator sop (Arg arg, accessor_arg cv ref self) 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, R(Arg) cv ref noex>(access_proxy<F>(SELF), std::forward<Arg>(arg))`.
+`(7)` Provides a `friend operator sop(Arg arg, accessor_arg cv ref self)` with the same *noex* specifiers as of the overload type. `accessor::operator sop(Arg arg, accessor_arg cv ref self)` is equivalent to `return proxy_invoke<C, R(Arg) cv ref noex>(ACCESS_PROXY_EXPR, std::forward<Arg>(arg))`.
 
 ### Assignment SOPs
 
@@ -104,8 +104,8 @@ When `Sign` is one of `"+="`, `"-="`, `"*="`, `"/="`, `"&="`, `"|="`, `"^="`, `"
 // (8)
 template <class F, class C, class R, class Arg>
 struct accessor<F, C, R(Arg) cv ref noex> {
-  friend /* see below */ operator sop (Arg arg, accessor cv ref self) noex;
+  friend /* see below */ operator sop (Arg arg, accessor_arg cv ref self) 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, 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`.
+`(8)` Provides a `friend operator sop(Arg arg, accessor_arg cv ref self)` with the same *noex* specifiers as of the overload type. `accessor::operator sop(Arg arg, accessor_arg cv ref self)` calls `proxy_invoke<C, R(Arg) cv ref noex>(ACCESS_PROXY_EXPR, std::forward<Arg>(arg))` and returns `ACCESS_PROXY_EXPR` when `C::is_direct` is `true`, or otherwise, returns `*ACCESS_PROXY_EXPR` when `C::is_direct` is `false`.
diff --git a/docs/proxy.md b/docs/proxy.md
index 5fd85c73..04c35789 100644
--- a/docs/proxy.md
+++ b/docs/proxy.md
@@ -7,7 +7,7 @@ class proxy;
 
 Class template `proxy` is a general-purpose polymorphic wrapper for C++ objects. Unlike other polymorphic wrappers in the C++ standard (e.g., [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function), [`std::move_only_function`](https://en.cppreference.com/w/cpp/utility/functional/move_only_function), [`std::any`](https://en.cppreference.com/w/cpp/utility/any), etc.), `proxy` is based on pointer semantics. It supports flexible lifetime management without runtime [garbage collection (GC)](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)) at runtime, and offers best-in-class code generation quality, extendibility and accessibility.
 
-To instantiate `proxy<F>`, `F` shall model [concept `facade`](facade.md). As per `facade<F>`, `typename F::convention_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Cs`, and `typename F::reflection_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Rs`. For each type `T` in `Cs` or `Rs`, if `T` meets the [*ProAccessible* requirements](ProAccessible.md) of `F`, `typename T::template accessor<F>` is inherited by `proxy<F>` when `T::is_direct` is `true`. Otherwise, it is inherited by the return type of [`operator*`](proxy/indirection.md) when `T::is_direct` is `false`. Implementation of accessors can call [`access_proxy`](access_proxy.md) to access the `proxy` object. It is recommended to use [`facade_builder`](basic_facade_builder.md) to define a facade type.
+To instantiate `proxy<F>`, `F` shall model [concept `facade`](facade.md). As per `facade<F>`, `typename F::convention_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Cs`, and `typename F::reflection_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Rs`. For each type `T` in `Cs` or `Rs`, if `T` meets the [*ProAccessible* requirements](ProAccessible.md) of `F`, `typename T::template accessor<F>` is inherited by `proxy<F>` when `T::is_direct` is `true`. Otherwise, it is inherited by [`proxy_indirect_accessor`](proxy_indirect_accessor.md), the return type of [`operator*`](proxy/indirection.md), when `T::is_direct` is `false`. Implementation of accessors can call [`access_proxy`](access_proxy.md) to access the `proxy` object. It is recommended to use [`facade_builder`](basic_facade_builder.md) to define a facade type.
 
 Any instance of `proxy<F>` at any given point in time either *contains a value* or *does not contain a value*. If a `proxy<F>` *contains a value*, the type of the value shall be a pointer type `P`  where [`proxiable<P, F>`](proxiable.md) is `true`, and the value is guaranteed to be allocated as part of the `proxy` object footprint, i.e. no dynamic memory allocation occurs. However, `P` may allocate during its construction, depending on its implementation.
 
diff --git a/docs/proxy_indirect_accessor.md b/docs/proxy_indirect_accessor.md
new file mode 100644
index 00000000..60610023
--- /dev/null
+++ b/docs/proxy_indirect_accessor.md
@@ -0,0 +1,18 @@
+# Class template `proxy_indirect_accessor`
+
+```cpp
+template <class F>
+class proxy_indirect_accessor;
+```
+
+Class template `proxy_indirect_accessor` provides indirection accessibility for `proxy`. To instantiate `proxy_indirect_accessor<F>`, `F` shall model [concept `facade`](facade.md). As per `facade<F>`, `typename F::convention_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Cs`, and `typename F::reflection_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Rs`. For each type `T` in `Cs` or `Rs`, if `T` meets the [*ProAccessible* requirements](ProAccessible.md) of `F` and `T::is_direct` is `false`, `typename T::template accessor<F>` is inherited by `proxy_indirect_accessor<F>`.
+
+## Member functions
+
+| Name                    | Description                               |
+| ----------------------- | ----------------------------------------- |
+| (constructor) [deleted] | Has neither default nor copy constructors |
+
+## See also
+
+[function template `access_proxy`](access_proxy.md)
diff --git a/docs/specifications.md b/docs/specifications.md
index f997d88b..870f8b6f 100644
--- a/docs/specifications.md
+++ b/docs/specifications.md
@@ -21,6 +21,7 @@ This document provides the API specifications for the C++ library Proxy (version
 | [`operator_dispatch`](operator_dispatch.md)                  | Dispatch type for operator expressions with accessibility    |
 | [`explicit_conversion_dispatch`<br />`conversion_dispatch`](explicit_conversion_dispatch.md) | Dispatch type for explicit conversion expressions with accessibility |
 | [`implicit_conversion_dispatch`](implicit_conversion_dispatch.md) | Dispatch type for implicit conversion expressions with accessibility |
+| [`proxy_indirect_accessor`](proxy_indirect_accessor.md)      | Provides indirection accessibility for `proxy` types         |
 
 ## Functions
 
diff --git a/proxy.h b/proxy.h
index 9186c690..e3d37a40 100644
--- a/proxy.h
+++ b/proxy.h
@@ -49,6 +49,7 @@ struct proxiable_ptr_constraints {
   constraint_level destructibility;
 };
 
+template <class F> struct proxy_indirect_accessor;
 template <class F> class proxy;
 
 namespace details {
@@ -457,11 +458,10 @@ struct lifetime_meta_traits<MP, constraint_level::nontrivial>
 template <template <bool> class MP, constraint_level C>
 using lifetime_meta_t = typename lifetime_meta_traits<MP, C>::type;
 
-template <class F> struct proxy_indirect_accessor;
 template <class... As>
 class ___PRO_ENFORCE_EBO composite_accessor_impl : public As... {
   template <class> friend class pro::proxy;
-  template <class F> friend struct proxy_indirect_accessor;
+  template <class F> friend struct pro::proxy_indirect_accessor;
 
   composite_accessor_impl() noexcept = default;
   composite_accessor_impl(const composite_accessor_impl&) noexcept = default;
@@ -580,10 +580,6 @@ struct facade_traits<F>
       typename facade_traits::indirect_accessor, composite_accessor_impl<>>;
 };
 
-template <class F> struct proxy_indirect_accessor {};
-template <class F> requires(facade_traits<F>::has_indirection)
-struct proxy_indirect_accessor<F> : facade_traits<F>::indirect_accessor {};
-
 using ptr_prototype = void*[2];
 
 template <class M>
@@ -698,9 +694,8 @@ struct proxy_helper {
 #endif  // defined(__NVCOMPILER)
       return reinterpret_cast<add_qualifier_t<proxy<F>, Q>>(
           *(reinterpret_cast<add_qualifier_ptr_t<std::byte, Q>>(
-              static_cast<add_qualifier_ptr_t<
-                  typename facade_traits<F>::indirect_accessor, Q>>(
-                      std::addressof(a))) - offset));
+              static_cast<add_qualifier_ptr_t<proxy_indirect_accessor<F>, Q>>(
+                  std::addressof(a))) - offset));
     }
   }
 };
@@ -719,29 +714,33 @@ concept proxiable = facade<F> && sizeof(P) <= F::constraints.max_size &&
     details::facade_traits<F>::template conv_applicable_ptr<P> &&
     details::facade_traits<F>::template refl_applicable_ptr<P>;
 
+template <class F> struct proxy_indirect_accessor {};
+template <class F> requires(details::facade_traits<F>::has_indirection)
+struct proxy_indirect_accessor<F>
+    : details::facade_traits<F>::indirect_accessor {};
+
 template <class F>
 class proxy : public details::facade_traits<F>::direct_accessor {
   static_assert(facade<F>);
   friend struct details::proxy_helper<F>;
   using _Traits = details::facade_traits<F>;
-  using _IA = details::proxy_indirect_accessor<F>;
 
  public:
   proxy() noexcept {
     ___PRO_DEBUG(
       if constexpr (_Traits::has_indirection) {
-        std::ignore = static_cast<_IA* (proxy::*)() noexcept>(
-            &proxy::operator->);
-        std::ignore = static_cast<const _IA* (proxy::*)() const noexcept>(
-            &proxy::operator->);
-        std::ignore = static_cast<_IA& (proxy::*)() & noexcept>(
-            &proxy::operator*);
-        std::ignore = static_cast<const _IA& (proxy::*)() const& noexcept>(
-            &proxy::operator*);
-        std::ignore = static_cast<_IA&& (proxy::*)() && noexcept>(
-            &proxy::operator*);
-        std::ignore = static_cast<const _IA&& (proxy::*)() const&& noexcept>(
-            &proxy::operator*);
+        std::ignore = static_cast<proxy_indirect_accessor<F>*
+            (proxy::*)() noexcept>(&proxy::operator->);
+        std::ignore = static_cast<const proxy_indirect_accessor<F>*
+            (proxy::*)() const noexcept>(&proxy::operator->);
+        std::ignore = static_cast<proxy_indirect_accessor<F>&
+            (proxy::*)() & noexcept>(&proxy::operator*);
+        std::ignore = static_cast<const proxy_indirect_accessor<F>&
+            (proxy::*)() const& noexcept>(&proxy::operator*);
+        std::ignore = static_cast<proxy_indirect_accessor<F>&& (proxy::*)()
+            && noexcept>(&proxy::operator*);
+        std::ignore = static_cast<const proxy_indirect_accessor<F>&&
+            (proxy::*)() const&& noexcept>(&proxy::operator*);
       }
     )
   }
@@ -892,18 +891,20 @@ class proxy : public details::facade_traits<F>::direct_accessor {
       requires(std::is_constructible_v<P, std::initializer_list<U>&, Args...> &&
           F::constraints.destructibility >= constraint_level::nontrivial)
       { reset(); return initialize<P>(il, std::forward<Args>(args)...); }
-  _IA* operator->() noexcept requires(_Traits::has_indirection)
-      { return std::addressof(ia_); }
-  const _IA* operator->() const noexcept requires(_Traits::has_indirection)
-      { return std::addressof(ia_); }
-  _IA& operator*() & noexcept requires(_Traits::has_indirection)
-      { return ia_; }
-  const _IA& operator*() const& noexcept requires(_Traits::has_indirection)
-      { return ia_; }
-  _IA&& operator*() && noexcept requires(_Traits::has_indirection)
-      { return std::forward<_IA>(ia_); }
-  const _IA&& operator*() const&& noexcept requires(_Traits::has_indirection)
-      { return std::forward<const _IA>(ia_); }
+  proxy_indirect_accessor<F>* operator->() noexcept
+      requires(_Traits::has_indirection) { return std::addressof(ia_); }
+  const proxy_indirect_accessor<F>* operator->() const noexcept
+      requires(_Traits::has_indirection) { return std::addressof(ia_); }
+  proxy_indirect_accessor<F>& operator*() & noexcept
+      requires(_Traits::has_indirection) { return ia_; }
+  const proxy_indirect_accessor<F>& operator*() const& noexcept
+      requires(_Traits::has_indirection) { return ia_; }
+  proxy_indirect_accessor<F>&& operator*() && noexcept
+      requires(_Traits::has_indirection)
+      { return std::forward<proxy_indirect_accessor<F>>(ia_); }
+  const proxy_indirect_accessor<F>&& operator*() const&& noexcept
+      requires(_Traits::has_indirection)
+      { return std::forward<const proxy_indirect_accessor<F>>(ia_); }
 
   friend void swap(proxy& lhs, proxy& rhs) noexcept(noexcept(lhs.swap(rhs)))
       { lhs.swap(rhs); }
@@ -922,7 +923,7 @@ class proxy : public details::facade_traits<F>::direct_accessor {
   }
 
   [[___PRO_NO_UNIQUE_ADDRESS_ATTRIBUTE]]
-  _IA ia_;
+  proxy_indirect_accessor<F> ia_;
   details::meta_ptr<typename _Traits::meta> meta_;
   alignas(F::constraints.max_align) std::byte ptr_[F::constraints.max_size];
 };
@@ -1176,20 +1177,24 @@ proxy<F> make_proxy(T&& value) {
             (::std::is_constructible_v<accessor<__F, __C, __Os>> && ...)) \
     struct accessor<__F, __C, __Os...> : accessor<__F, __C, __Os>... \
         { using accessor<__F, __C, __Os>::__VA_ARGS__...; }; \
-    __MACRO(, *this, __VA_ARGS__); \
-    __MACRO(noexcept, *this, __VA_ARGS__); \
-    __MACRO(&, *this, __VA_ARGS__); \
-    __MACRO(& noexcept, *this, __VA_ARGS__); \
-    __MACRO(&&, ::std::forward<accessor>(*this), __VA_ARGS__); \
-    __MACRO(&& noexcept, ::std::forward<accessor>(*this), __VA_ARGS__); \
-    __MACRO(const, *this, __VA_ARGS__); \
-    __MACRO(const noexcept, *this, __VA_ARGS__); \
-    __MACRO(const&, *this, __VA_ARGS__); \
-    __MACRO(const& noexcept, *this, __VA_ARGS__); \
-    __MACRO(const&&, ::std::forward<const accessor>(*this), __VA_ARGS__); \
-    __MACRO(const&& noexcept, ::std::forward<const accessor>(*this), \
-        __VA_ARGS__);
-
+    __MACRO(, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \
+    __MACRO(noexcept, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \
+    __MACRO(&, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \
+    __MACRO(& noexcept, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \
+    __MACRO(&&, ::pro::access_proxy<__F>(::std::forward<accessor>(*this)), \
+        __VA_ARGS__); \
+    __MACRO(&& noexcept, ::pro::access_proxy<__F>( \
+        ::std::forward<accessor>(*this)), __VA_ARGS__); \
+    __MACRO(const, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \
+    __MACRO(const noexcept, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \
+    __MACRO(const&, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \
+    __MACRO(const& noexcept, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \
+    __MACRO(const&&, ::pro::access_proxy<__F>( \
+        ::std::forward<const accessor>(*this)), __VA_ARGS__); \
+    __MACRO(const&& noexcept, ::pro::access_proxy<__F>( \
+        ::std::forward<const accessor>(*this)), __VA_ARGS__);
+
+#define ___PRO_ADL_ARG ::pro::details::adl_accessor_arg_t<__F, __C>
 #define ___PRO_DEF_FREE_ACCESSOR_TEMPLATE(__MACRO, ...) \
     template <class __F, class __C, class... __Os> \
     struct ___PRO_ENFORCE_EBO accessor { accessor() = delete; }; \
@@ -1197,24 +1202,32 @@ proxy<F> make_proxy(T&& value) {
         requires(sizeof...(__Os) > 1u && \
             (::std::is_constructible_v<accessor<__F, __C, __Os>> && ...)) \
     struct accessor<__F, __C, __Os...> : accessor<__F, __C, __Os>... {}; \
-    __MACRO(,, accessor& __self, __self, __VA_ARGS__); \
-    __MACRO(noexcept, noexcept, accessor& __self, __self, __VA_ARGS__); \
-    __MACRO(&,, accessor& __self, __self, __VA_ARGS__); \
-    __MACRO(& noexcept, noexcept, accessor& __self, __self, __VA_ARGS__); \
-    __MACRO(&&,, accessor&& __self, \
-        ::std::forward<accessor>(__self), __VA_ARGS__); \
-    __MACRO(&& noexcept, noexcept, accessor&& __self, \
-        ::std::forward<accessor>(__self), __VA_ARGS__); \
-    __MACRO(const,, const accessor& __self, __self, __VA_ARGS__); \
-    __MACRO(const noexcept, noexcept, const accessor& __self, \
-        __self, __VA_ARGS__); \
-    __MACRO(const&,, const accessor& __self, __self, __VA_ARGS__); \
-    __MACRO(const& noexcept, noexcept, const accessor& __self, \
-        __self, __VA_ARGS__); \
-    __MACRO(const&&,, const accessor&& __self, \
-        ::std::forward<const accessor>(__self), __VA_ARGS__); \
-    __MACRO(const&& noexcept, noexcept, const accessor&& __self, \
-        ::std::forward<const accessor>(__self), __VA_ARGS__);
+    __MACRO(,, ___PRO_ADL_ARG& __self, ::pro::access_proxy<__F>(__self), \
+        __VA_ARGS__); \
+    __MACRO(noexcept, noexcept, ___PRO_ADL_ARG& __self, \
+        ::pro::access_proxy<__F>(__self), __VA_ARGS__); \
+    __MACRO(&,, ___PRO_ADL_ARG& __self, ::pro::access_proxy<__F>(__self), \
+        __VA_ARGS__); \
+    __MACRO(& noexcept, noexcept, ___PRO_ADL_ARG& __self, \
+        ::pro::access_proxy<__F>(__self), __VA_ARGS__); \
+    __MACRO(&&,, ___PRO_ADL_ARG&& __self, ::pro::access_proxy<__F>( \
+        ::std::forward<decltype(__self)>(__self)), __VA_ARGS__); \
+    __MACRO(&& noexcept, noexcept, ___PRO_ADL_ARG&& __self, \
+        ::pro::access_proxy<__F>(::std::forward<decltype(__self)>(__self)), \
+        __VA_ARGS__); \
+    __MACRO(const,, const ___PRO_ADL_ARG& __self, \
+        ::pro::access_proxy<__F>(__self), __VA_ARGS__); \
+    __MACRO(const noexcept, noexcept, const ___PRO_ADL_ARG& __self, \
+        ::pro::access_proxy<__F>(__self), __VA_ARGS__); \
+    __MACRO(const&,, const ___PRO_ADL_ARG& __self, \
+        ::pro::access_proxy<__F>(__self), __VA_ARGS__); \
+    __MACRO(const& noexcept, noexcept, const ___PRO_ADL_ARG& __self, \
+        ::pro::access_proxy<__F>(__self), __VA_ARGS__); \
+    __MACRO(const&&,, const ___PRO_ADL_ARG&& __self, ::pro::access_proxy<__F>( \
+        ::std::forward<decltype(__self)>(__self)), __VA_ARGS__); \
+    __MACRO(const&& noexcept, noexcept, const ___PRO_ADL_ARG&& __self, \
+        ::pro::access_proxy<__F>(::std::forward<decltype(__self)>(__self)), \
+        __VA_ARGS__);
 
 #define ___PRO_GEN_DEBUG_SYMBOL_FOR_MEM_ACCESSOR(...) \
     ___PRO_DEBUG( \
@@ -1222,17 +1235,21 @@ proxy<F> make_proxy(T&& value) {
 
 namespace details {
 
+template <class F, class C>
+using adl_accessor_arg_t =
+    std::conditional_t<C::is_direct, proxy<F>, proxy_indirect_accessor<F>>;
+
 template <class O>
 using overload_return_type = typename overload_traits<O>::return_type;
 #define ___PRO_DEF_CAST_ACCESSOR(Q, SELF, ...) \
-    template <class F, class C, class T> \
-    struct accessor<F, C, T() Q> { \
+    template <class __F, class __C, class T> \
+    struct accessor<__F, __C, T() Q> { \
       ___PRO_GEN_DEBUG_SYMBOL_FOR_MEM_ACCESSOR(operator T) \
       explicit(Expl) operator T() Q { \
         if constexpr (Nullable) { \
-          if (!access_proxy<F>(SELF).has_value()) { return nullptr; } \
+          if (!SELF.has_value()) { return nullptr; } \
         } \
-        return proxy_invoke<C, T() Q>(access_proxy<F>(SELF)); \
+        return proxy_invoke<__C, T() Q>(SELF); \
       } \
     }
 template <bool Expl, bool Nullable>
@@ -1475,19 +1492,18 @@ template <details::sign Sign, bool Rhs = false>
 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> { \
+    template <class __F, class __C, class R> \
+    struct accessor<__F, __C, R() Q> { \
       ___PRO_GEN_DEBUG_SYMBOL_FOR_MEM_ACCESSOR(__VA_ARGS__) \
-      R __VA_ARGS__() Q \
-          { return proxy_invoke<C, R() Q>(access_proxy<F>(SELF)); } \
+      R __VA_ARGS__() Q { return proxy_invoke<__C, R() Q>(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> { \
+    template <class __F, class __C, class R, class... Args> \
+    struct accessor<__F, __C, R(Args...) Q> { \
       ___PRO_GEN_DEBUG_SYMBOL_FOR_MEM_ACCESSOR(__VA_ARGS__) \
       R __VA_ARGS__(Args... args) Q { \
-        return proxy_invoke<C, R(Args...) Q>( \
-            access_proxy<F>(SELF), std::forward<Args>(args)...); \
+        return proxy_invoke<__C, R(Args...) Q>( \
+            SELF, std::forward<Args>(args)...); \
       } \
     }
 #define ___PRO_DEF_LHS_UNARY_OP_ACCESSOR ___PRO_DEF_LHS_ANY_OP_ACCESSOR
@@ -1520,19 +1536,20 @@ struct operator_dispatch;
           operator __VA_ARGS__) \
     };
 
-#define ___PRO_DEF_RHS_OP_ACCESSOR(Q, NE, SELF, FW_SELF, ...) \
-    template <class F, class C, class R, class Arg> \
-    struct accessor<F, C, R(Arg) Q> { \
-      friend R operator __VA_ARGS__(Arg arg, SELF) NE { \
-        return proxy_invoke<C, R(Arg) Q>( \
-            access_proxy<F>(FW_SELF), std::forward<Arg>(arg)); \
+#define ___PRO_DEF_RHS_OP_ACCESSOR(Q, NE, SELF_ARG, SELF, ...) \
+    template <class __F, class __C, class R, class Arg> \
+    struct accessor<__F, __C, R(Arg) Q> { \
+      friend R operator __VA_ARGS__(Arg arg, SELF_ARG) NE { \
+        return proxy_invoke<__C, R(Arg) Q>(SELF, std::forward<Arg>(arg)); \
       } \
 ___PRO_DEBUG( \
       accessor() noexcept { std::ignore = &accessor::_symbol_guard; } \
     \
      private: \
-      static inline R _symbol_guard(Arg arg, SELF) NE \
-          { return std::forward<Arg>(arg) __VA_ARGS__ FW_SELF; } \
+      static inline R _symbol_guard(Arg arg, SELF_ARG) NE { \
+        return std::forward<Arg>(arg) __VA_ARGS__ \
+            std::forward<decltype(__self)>(__self); \
+      } \
 ) \
     }
 #define ___PRO_RHS_OP_DISPATCH_IMPL(...) \
@@ -1555,32 +1572,31 @@ ___PRO_DEBUG( \
     ___PRO_RHS_OP_DISPATCH_IMPL(__VA_ARGS__)
 
 #define ___PRO_DEF_LHS_ASSIGNMENT_OP_ACCESSOR(Q, SELF, ...) \
-    template <class F, class C, class R, class Arg> \
-    struct accessor<F, C, R(Arg) Q> { \
+    template <class __F, class __C, class R, class Arg> \
+    struct accessor<__F, __C, R(Arg) Q> { \
       ___PRO_GEN_DEBUG_SYMBOL_FOR_MEM_ACCESSOR(__VA_ARGS__) \
       decltype(auto) __VA_ARGS__(Arg arg) Q { \
-        proxy_invoke<C, R(Arg) Q>( \
-            access_proxy<F>(SELF), std::forward<Arg>(arg)); \
-        if constexpr (C::is_direct) { \
-          return access_proxy<F>(SELF); \
+        proxy_invoke<__C, R(Arg) Q>(SELF, std::forward<Arg>(arg)); \
+        if constexpr (__C::is_direct) { \
+          return SELF; \
         } else { \
-          return *access_proxy<F>(SELF); \
+          return *SELF; \
         } \
       } \
     }
-#define ___PRO_DEF_RHS_ASSIGNMENT_OP_ACCESSOR(Q, NE, SELF, FW_SELF, ...) \
-    template <class F, class C, class R, class Arg> \
-    struct accessor<F, C, R(Arg&) Q> { \
-      friend Arg& operator __VA_ARGS__(Arg& arg, SELF) NE { \
-        proxy_invoke<C, R(Arg&) Q>(access_proxy<F>(FW_SELF), arg); \
+#define ___PRO_DEF_RHS_ASSIGNMENT_OP_ACCESSOR(Q, NE, SELF_ARG, SELF, ...) \
+    template <class __F, class __C, class R, class Arg> \
+    struct accessor<__F, __C, R(Arg&) Q> { \
+      friend Arg& operator __VA_ARGS__(Arg& arg, SELF_ARG) NE { \
+        proxy_invoke<__C, R(Arg&) Q>(SELF, arg); \
         return arg; \
       } \
 ___PRO_DEBUG( \
       accessor() noexcept { std::ignore = &accessor::_symbol_guard; } \
     \
      private: \
-      static inline Arg& _symbol_guard(Arg& arg, SELF) NE \
-          { return arg __VA_ARGS__ FW_SELF; } \
+      static inline Arg& _symbol_guard(Arg& arg, SELF_ARG) NE \
+          { return arg __VA_ARGS__ std::forward<decltype(__self)>(__self); } \
 ) \
     }
 #define ___PRO_ASSIGNMENT_OP_DISPATCH_IMPL(...) \
@@ -1706,8 +1722,7 @@ using conversion_dispatch = explicit_conversion_dispatch;
       ___PRO_GEN_DEBUG_SYMBOL_FOR_MEM_ACCESSOR(__VA_ARGS__) \
       __R __VA_ARGS__(__Args... __args) __Q { \
         return ::pro::proxy_invoke<__C, __R(__Args...) __Q>( \
-            ::pro::access_proxy<__F>(__SELF), \
-            ::std::forward<__Args>(__args)...); \
+            __SELF, ::std::forward<__Args>(__args)...); \
       } \
     }
 #define ___PRO_DEF_MEM_DISPATCH_IMPL(__NAME, __FUNC, __FNAME) \
@@ -1725,20 +1740,20 @@ using conversion_dispatch = explicit_conversion_dispatch;
 #define PRO_DEF_MEM_DISPATCH(__NAME, ...) \
     ___PRO_EXPAND_MACRO(___PRO_DEF_MEM_DISPATCH, __NAME, __VA_ARGS__)
 
-#define ___PRO_DEF_FREE_ACCESSOR(__Q, __NE, __SELF, __FW_SELF, ...) \
+#define ___PRO_DEF_FREE_ACCESSOR(__Q, __NE, __SELF_ARG, __SELF, ...) \
     template <class __F, class __C, class __R, class... __Args> \
     struct accessor<__F, __C, __R(__Args...) __Q> { \
-      friend __R __VA_ARGS__(__SELF, __Args... __args) __NE { \
+      friend __R __VA_ARGS__(__SELF_ARG, __Args... __args) __NE { \
         return ::pro::proxy_invoke<__C, __R(__Args...) __Q>( \
-            ::pro::access_proxy<__F>(__FW_SELF), \
-            ::std::forward<__Args>(__args)...); \
+            __SELF, ::std::forward<__Args>(__args)...); \
       } \
 ___PRO_DEBUG( \
       accessor() noexcept { ::std::ignore = &accessor::_symbol_guard; } \
     \
      private: \
-      static inline __R _symbol_guard(__SELF, __Args... __args) __NE { \
-        return __VA_ARGS__(__FW_SELF, ::std::forward<__Args>(__args)...); \
+      static inline __R _symbol_guard(__SELF_ARG, __Args... __args) __NE { \
+        return __VA_ARGS__(::std::forward<decltype(__self)>(__self), \
+            ::std::forward<__Args>(__args)...); \
       } \
 ) \
     }
diff --git a/tests/proxy_invocation_tests.cpp b/tests/proxy_invocation_tests.cpp
index 9b1b78cf..bd059a77 100644
--- a/tests/proxy_invocation_tests.cpp
+++ b/tests/proxy_invocation_tests.cpp
@@ -4,9 +4,11 @@
 #include <gtest/gtest.h>
 #include <algorithm>
 #include <functional>
+#include <iomanip>
 #include <list>
 #include <map>
 #include <ranges>
+#include <sstream>
 #include <string>
 #include <typeindex>
 #include <typeinfo>
@@ -128,6 +130,15 @@ template <class... Args>
 std::vector<std::type_index> GetTypeIndices()
     { return {std::type_index{typeid(Args)}...}; }
 
+template <class T>
+std::string Dump(T&& value) noexcept {
+  std::ostringstream out;
+  out << std::boolalpha << "is_const=" << std::is_const_v<std::remove_reference_t<T>> << ", is_ref=" << std::is_lvalue_reference_v<T> << ", value=" << value;
+  return std::move(out).str();
+}
+
+PRO_DEF_FREE_DISPATCH(FreeDump, Dump);
+
 }  // namespace proxy_invocation_tests_details
 
 namespace details = proxy_invocation_tests_details;
@@ -306,7 +317,7 @@ TEST(ProxyInvocationTests, TestObserverDispatch) {
   ASSERT_EQ(ToString(*p), "123");
 }
 
-TEST(ProxyInvocationTests, TestQualifiedConvention) {
+TEST(ProxyInvocationTests, TestQualifiedConvention_Member) {
   struct TestFacade : pro::facade_builder
       ::add_convention<pro::operator_dispatch<"()">, int()&, int() const&, int() && noexcept, int() const&&>
       ::build {};
@@ -326,3 +337,17 @@ TEST(ProxyInvocationTests, TestQualifiedConvention) {
   ASSERT_EQ((*std::move(p))(), 2);
   ASSERT_EQ((*std::move(std::as_const(p)))(), 3);
 }
+
+TEST(ProxyInvocationTests, TestQualifiedConvention_Free) {
+  struct TestFacade : pro::facade_builder
+      ::add_convention<details::FreeDump, std::string() &, std::string() const&, std::string() && noexcept, std::string() const&&>
+      ::build {};
+
+  pro::proxy<TestFacade> p = pro::make_proxy<TestFacade>(123);
+  static_assert(!noexcept(Dump(*p)));
+  static_assert(noexcept(Dump(*std::move(p))));
+  ASSERT_EQ(Dump(*p), "is_const=false, is_ref=true, value=123");
+  ASSERT_EQ(Dump(*std::as_const(p)), "is_const=true, is_ref=true, value=123");
+  ASSERT_EQ(Dump(*std::move(p)), "is_const=false, is_ref=false, value=123");
+  ASSERT_EQ(Dump(*std::move(std::as_const(p))), "is_const=true, is_ref=false, value=123");
+}

From ba0aa501ceedb194a6420426e3618d98b93770e1 Mon Sep 17 00:00:00 2001
From: Mingxin Wang <mingxwa@microsoft.com>
Date: Sun, 15 Dec 2024 10:55:45 +0800
Subject: [PATCH 2/2] Fix doc

---
 docs/proxy/indirection.md | 20 ++++++++++----------
 docs/specifications.md    |  2 +-
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/docs/proxy/indirection.md b/docs/proxy/indirection.md
index 89926a3c..1b487b5b 100644
--- a/docs/proxy/indirection.md
+++ b/docs/proxy/indirection.md
@@ -1,30 +1,29 @@
 # `proxy::operator->`, `proxy::operator*`
 
-The definitions of `proxy::operator->` and `proxy::operator*` make use of the following exposition-only constant and type alias:
+The definitions of `proxy::operator->` and `proxy::operator*` make use of the following exposition-only constant:
 
 ```cpp
 static constexpr bool has-indirection = see below;  // exposition only
-using indirect-accessor = see below;  // exposition only
 ```
 
 As per [`facade<F>`](../facade.md), `typename F::convention_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Cs`. Let `Cs2` be the types in `Cs` where each type `C` meets the [*ProAccessible* requirements](../ProAccessible.md) of `F` and `C::is_direct` is `false`. *has-indirection* is `true` if `Cs2` contains at least one type; otherwise, it is `false`. *indirect-accessor* is a non-copyable type that inherits from every type in `Cs2`.
 
 ```cpp
 // (1)
-indirect-accessor* operator->() noexcept requires(has-indirection);
-const indirect-accessor* operator->() const noexcept requires(has-indirection);
+proxy_indirect_accessor<F>* operator->() noexcept requires(has-indirection);
+const proxy_indirect_accessor<F>* operator->() const noexcept requires(has-indirection);
 
 // (2)
-indirect-accessor& operator*() & noexcept requires(has-indirection);
-const indirect-accessor& operator*() const& noexcept requires(has-indirection);
-indirect-accessor&& operator*() && noexcept requires(has-indirection);
-const indirect-accessor&& operator*() const&& noexcept requires(has-indirection);
+proxy_indirect_accessor<F>& operator*() & noexcept requires(has-indirection);
+const proxy_indirect_accessor<F>& operator*() const& noexcept requires(has-indirection);
+proxy_indirect_accessor<F>&& operator*() && noexcept requires(has-indirection);
+const proxy_indirect_accessor<F>&& operator*() const&& noexcept requires(has-indirection);
 ```
 
 These operators access the accessors of the indirect conventions, as if dereferencing the contained value.
 
-- `(1)` Returns a pointer to the *indirect-accessor*.
-- `(2)` Returns a reference to the *indirect-accessor*.
+- `(1)` Returns a pointer to the `proxy_indirect_accessor<F>`.
+- `(2)` Returns a reference to the `proxy_indirect_accessor<F>`.
 
 The behavior is undefined if `*this` does not contain a value.
 
@@ -68,3 +67,4 @@ int main() {
 
 - [function template `access_proxy`](../access_proxy.md)
 - [function template `proxy_invoke`](../proxy_invoke.md)
+- [class template `proxy_indirect_accessor`](../proxy_indirect_accessor.md)
diff --git a/docs/specifications.md b/docs/specifications.md
index 870f8b6f..46c7ab5c 100644
--- a/docs/specifications.md
+++ b/docs/specifications.md
@@ -21,7 +21,7 @@ This document provides the API specifications for the C++ library Proxy (version
 | [`operator_dispatch`](operator_dispatch.md)                  | Dispatch type for operator expressions with accessibility    |
 | [`explicit_conversion_dispatch`<br />`conversion_dispatch`](explicit_conversion_dispatch.md) | Dispatch type for explicit conversion expressions with accessibility |
 | [`implicit_conversion_dispatch`](implicit_conversion_dispatch.md) | Dispatch type for implicit conversion expressions with accessibility |
-| [`proxy_indirect_accessor`](proxy_indirect_accessor.md)      | Provides indirection accessibility for `proxy` types         |
+| [`proxy_indirect_accessor`](proxy_indirect_accessor.md)      | Provides indirection accessibility for `proxy`               |
 
 ## Functions
 
