get_completion_signatures_type - #2194
Draft
RobertLeahy wants to merge 1 commit into
Draft
Conversation
get_completion_signatures attempts to leverage compile-time exceptions
to report errors. It does this by:
- Throwing an exception when it cannot synthesize completion signatures
(definitionally this happens at compile time because it's consteval),
and
- Reporting that it returns completion_signatures<> in situations
wherein it would throw an exception
The latter is reflective of a tension: get_completion_signatures "knows"
it will throw an exception when the return type is computed, but that
exception is not surfaced until and unless the function is invoked. This
creates the opportunity for very interesting bugs, for example decltype(
get_completion_signatures<...>()) is, in a vacuum, likely to be a bug
because this doesn't actually evaluate the expression and therefore any
exception is not thrown.
The solution is complete surrender to the invocation of functions at
compile time (rather than exposing information via channels which can be
interrogated via an unevaluated context): Reflection.
Added get_completion_signatures_type which returns std::meta::info and
provides a reflection-first way to access completion signatures
computation. In addition to ameliorating the above-described tension in
the design of get_completion_signatures this new function allows for
better ergonomics in reflection-first uses.
When attempting to use reflection to aid in the computation of a certain
asynchronous operation's completion signatures one needs to, in general:
- Obtain the completion signatures of one or more child operations
- Examine and manipulate those completion signatures
- Yield the final completion signatures
With get_completion_signatures this becomes awkward. The way to "lift"
the completion signatures into the reflection domain is via ^^decltype(
get_completion_signatures<...>()), which is pathological as discussed
above. In order to expose the needed interface (i.e. a static, consteval
member function get_completion_signatures which returns an instance of
an instantiation of the completion_signatures class template) one needs
to splice a reflection, but one can't splice a reflection unless that
reflection is constexpr, but initialization of constexpr variables needs
to be done by a constant expression which can't propagate exceptions
thereby ruining the entire error reporting mechanism of
get_completion_signatures.
To make the above concrete consider:
template<typename Self, typename Env>
static consteval auto get_completion_signatures() {
auto sigs = get_completion_signatures<...>();
auto meta = ^^decltype(sigs);
auto final_sigs = /* depends on meta */;
return typename [:final_sigs:]{};
}
This doesn't compile because final_sigs isn't a constant expression. If
we try to adapt:
template<typename Self, typename Env>
static consteval auto get_completion_signatures() {
auto sigs = get_completion_signatures<...>();
constexpr auto meta = ^^decltype(sigs);
constexpr auto final_sigs = /* depends on meta */;
return typename [:final_sigs:]{};
}
Then any operation in the synthesis of final_sigs which throws an
exception fails compilation rather than being propagated. Intuitively we
can understand why this is the case: The return type of the function
depends thereupon, a problem get_completion_signatures works around by
coalescing to the dummy type completion_signatures<>.
Also note again that in both of the above examples:
auto sigs = get_completion_signatures<...>();
constexpr auto meta = ^^decltype(sigs);
Is fragile. It seems as though it could be rewritten as:
constexpr auto meta = ^^decltype(get_completion_signatures<...>());
But that would introduce a subtle bug: get_completion_signatures would
not actually be evaluated.
get_completion_signatures_type ameliorates the above. The problematic
member function can be written as:
template<typename Self, typename Env>
static consteval std::meta::info get_completion_signatures_type() {
auto meta = get_completion_signatures_type<...>();
auto final_sigs = /* depends on meta */;
return final_sigs;
}
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
get_completion_signatures attempts to leverage compile-time exceptions to report errors. It does this by:
The latter is reflective of a tension: get_completion_signatures "knows" it will throw an exception when the return type is computed, but that exception is not surfaced until and unless the function is invoked. This creates the opportunity for very interesting bugs, for example decltype( get_completion_signatures<...>()) is, in a vacuum, likely to be a bug because this doesn't actually evaluate the expression and therefore any exception is not thrown.
The solution is complete surrender to the invocation of functions at compile time (rather than exposing information via channels which can be interrogated via an unevaluated context): Reflection.
Added get_completion_signatures_type which returns std::meta::info and provides a reflection-first way to access completion signatures computation. In addition to ameliorating the above-described tension in the design of get_completion_signatures this new function allows for better ergonomics in reflection-first uses.
When attempting to use reflection to aid in the computation of a certain asynchronous operation's completion signatures one needs to, in general:
With get_completion_signatures this becomes awkward. The way to "lift" the completion signatures into the reflection domain is via ^^decltype( get_completion_signatures<...>()), which is pathological as discussed above. In order to expose the needed interface (i.e. a static, consteval member function get_completion_signatures which returns an instance of an instantiation of the completion_signatures class template) one needs to splice a reflection, but one can't splice a reflection unless that reflection is constexpr, but initialization of constexpr variables needs to be done by a constant expression which can't propagate exceptions thereby ruining the entire error reporting mechanism of get_completion_signatures.
To make the above concrete consider:
This doesn't compile because final_sigs isn't a constant expression. If we try to adapt:
Then any operation in the synthesis of final_sigs which throws an exception fails compilation rather than being propagated. Intuitively we can understand why this is the case: The return type of the function depends thereupon, a problem get_completion_signatures works around by coalescing to the dummy type completion_signatures<>.
Also note again that in both of the above examples:
Is fragile. It seems as though it could be rewritten as:
But that would introduce a subtle bug: get_completion_signatures would not actually be evaluated.
get_completion_signatures_type ameliorates the above. The problematic member function can be written as: