Extend operator support - #388
Conversation
bbe3b3c to
55494be
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #388 +/- ##
==========================================
+ Coverage 74.48% 80.97% +6.49%
==========================================
Files 15 15
Lines 1011 1277 +266
Branches 224 214 -10
==========================================
+ Hits 753 1034 +281
+ Misses 49 44 -5
+ Partials 209 199 -10
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
fa8edc2 to
4ae0dda
Compare
4ae0dda to
260218d
Compare
philipcraig
left a comment
There was a problem hiding this comment.
Forwarding unary operator& breaks protocol's own assignment. The self-assignment checks at protocol.hh:413 and :439 spell &other, which now resolves to the interface's operator& thunk:
struct I { int operator&() const; int f() const; };
struct T { int operator&() const { return 99; } int f() const { return 3; } };
protocol<I> p{T{}}, q{T{}};
q = p; // protocol.hh:413: comparison between pointer and integer
q = std::move(p); // protocol.hh:439: same
auto r = &p; // int, 99If the interface's operator& returned a pointer the assignment would compile and compare this against the thunk's result, so genuine self-assignment falls through to destroy-then-copy-from-destroyed. std::addressof(other) at both sites fixes the wrappers (protocol.hh already uses it at 527 and 622). Whether &p on a wrapper should ever dispatch to the interface is a separate decision; at minimum it wants a sentence in the docs. Fine as a follow-up if you prefer.
CONTRIBUTING.md:194-196 still says no operators other than operator() are supported, and DRAFT.md:565-570 still says equality and comparison operators are not generated. Both need updating, or DRAFT.md needs a note that the reference implementation has diverged.
Inline comments cover the comparison-operator regression, a latent explicit-object mangling bug, and some duplication. Any of these can go to follow-up PRs; none needs to hold this one.
Address PR #388 review: fix operator& self-assignment, exclude comparison operators, update docs and comments, trim duplicated death tests. Address PR #388 review: share one valueless-check-and-dispatch helper (call_through_vtable) between operator_thunks.hh and member_function_thunks.hh instead of hand-writing the assert in three places.
Address PR #388 review: fix operator& self-assignment, exclude comparison operators, update docs and comments, trim duplicated death tests. Address PR #388 review: share one valueless-check-and-dispatch helper (call_through_vtable) between operator_thunks.hh and member_function_thunks.hh instead of hand-writing the assert in three places. Fix operator symbol formatting in unsupported-operator diagnostic
6a9dffc to
e766916
Compare
Address PR #388 review: fix operator& self-assignment, exclude comparison operators, update docs and comments, trim duplicated death tests. Address PR #388 review: share one valueless-check-and-dispatch helper (call_through_vtable) between operator_thunks.hh and member_function_thunks.hh instead of hand-writing the assert in three places. Fix operator symbol formatting in unsupported-operator diagnostic Address PR #388 review: reject explicit-object member functions on protocol interfaces, deduplicate call-operator/slicing test concepts into test_helpers.h, cover the unary/binary operator split and const-view dispatch for compound-assignment and increment/decrement, and trim valueless-call death tests to the call operator.
e766916 to
d13cafa
Compare
Address PR #388 review: fix operator& self-assignment, exclude comparison operators, update docs and comments, trim duplicated death tests. Address PR #388 review: share one valueless-check-and-dispatch helper (call_through_vtable) between operator_thunks.hh and member_function_thunks.hh instead of hand-writing the assert in three places. Fix operator symbol formatting in unsupported-operator diagnostic Address PR #388 review: reject explicit-object member functions on protocol interfaces, deduplicate call-operator/slicing test concepts into test_helpers.h, cover the unary/binary operator split and const-view dispatch for compound-assignment and increment/decrement, and trim valueless-call death tests to the call operator. Delete the unused duplicate call_through_vtable in operator_thunks.hh
d13cafa to
71c8b2a
Compare
Address PR #388 review: fix operator& self-assignment, exclude comparison operators, update docs and comments, trim duplicated death tests. Address PR #388 review: share one valueless-check-and-dispatch helper (call_through_vtable) between operator_thunks.hh and member_function_thunks.hh instead of hand-writing the assert in three places. Fix operator symbol formatting in unsupported-operator diagnostic Address PR #388 review: reject explicit-object member functions on protocol interfaces, deduplicate call-operator/slicing test concepts into test_helpers.h, cover the unary/binary operator split and const-view dispatch for compound-assignment and increment/decrement, and trim valueless-call death tests to the call operator. Delete the unused duplicate call_through_vtable in operator_thunks.hh Fold fixed_operator_code into base_name_of's switch for -Wswitch exhaustiveness
71c8b2a to
6100855
Compare
Address PR #388 review: fix operator& self-assignment, exclude comparison operators, update docs and comments, trim duplicated death tests. Address PR #388 review: share one valueless-check-and-dispatch helper (call_through_vtable) between operator_thunks.hh and member_function_thunks.hh instead of hand-writing the assert in three places. Fix operator symbol formatting in unsupported-operator diagnostic Address PR #388 review: reject explicit-object member functions on protocol interfaces, deduplicate call-operator/slicing test concepts into test_helpers.h, cover the unary/binary operator split and const-view dispatch for compound-assignment and increment/decrement, and trim valueless-call death tests to the call operator. Delete the unused duplicate call_through_vtable in operator_thunks.hh Fold fixed_operator_code into base_name_of's switch for -Wswitch exhaustiveness Suppress google-runtime-operator for unary operator& test coverage
6100855 to
5afa72f
Compare
Address PR #388 review: fix operator& self-assignment, exclude comparison operators, update docs and comments, trim duplicated death tests. Address PR #388 review: share one valueless-check-and-dispatch helper (call_through_vtable) between operator_thunks.hh and member_function_thunks.hh instead of hand-writing the assert in three places. Fix operator symbol formatting in unsupported-operator diagnostic Address PR #388 review: reject explicit-object member functions on protocol interfaces, deduplicate call-operator/slicing test concepts into test_helpers.h, cover the unary/binary operator split and const-view dispatch for compound-assignment and increment/decrement, and trim valueless-call death tests to the call operator. Delete the unused duplicate call_through_vtable in operator_thunks.hh Fold fixed_operator_code into base_name_of's switch for -Wswitch exhaustiveness Suppress google-runtime-operator for unary operator& test coverage
5afa72f to
4a45d8d
Compare
|
Thanks for the detailed review. All should be good to go now. |
Address PR #388 review: fix operator& self-assignment, exclude comparison operators, update docs and comments, trim duplicated death tests. Address PR #388 review: share one valueless-check-and-dispatch helper (call_through_vtable) between operator_thunks.hh and member_function_thunks.hh instead of hand-writing the assert in three places. Fix operator symbol formatting in unsupported-operator diagnostic Address PR #388 review: reject explicit-object member functions on protocol interfaces, deduplicate call-operator/slicing test concepts into test_helpers.h, cover the unary/binary operator split and const-view dispatch for compound-assignment and increment/decrement, and trim valueless-call death tests to the call operator. Delete the unused duplicate call_through_vtable in operator_thunks.hh Fold fixed_operator_code into base_name_of's switch for -Wswitch exhaustiveness Suppress google-runtime-operator for unary operator& test coverage Remove dead operator_thunk/operator_overload_set specializations for the seven comparison operators (==, !=, <, <=, >, >=, <=>). conformance.hh's is_unsupported_operator rejects these operators on any protocol interface before generate_member_bases_wrapper can instantiate operator_overload_set for them, so the specializations were unreachable and untested. Build and full test suite verified green after removal.
4a45d8d to
065d5fa
Compare
Address PR #388 review: fix operator& self-assignment, exclude comparison operators, update docs and comments, trim duplicated death tests. Address PR #388 review: share one valueless-check-and-dispatch helper (call_through_vtable) between operator_thunks.hh and member_function_thunks.hh instead of hand-writing the assert in three places. Fix operator symbol formatting in unsupported-operator diagnostic Address PR #388 review: reject explicit-object member functions on protocol interfaces, deduplicate call-operator/slicing test concepts into test_helpers.h, cover the unary/binary operator split and const-view dispatch for compound-assignment and increment/decrement, and trim valueless-call death tests to the call operator. Delete the unused duplicate call_through_vtable in operator_thunks.hh Fold fixed_operator_code into base_name_of's switch for -Wswitch exhaustiveness Suppress google-runtime-operator for unary operator& test coverage Remove dead operator_thunk/operator_overload_set specializations for the seven comparison operators (==, !=, <, <=, >, >=, <=>). conformance.hh's is_unsupported_operator rejects these operators on any protocol interface before generate_member_bases_wrapper can instantiate operator_overload_set for them, so the specializations were unreachable and untested. Build and full test suite verified green after removal. Clarify why only the call operator gets a valueless-dispatch death test. The comment named call_through_vtable, an implementation-detail helper whose rename would silently invalidate the comment's claim. State the invariant (one shared valueless check, so one death test covers it) without pinning the comment to that identifier.
114cdac to
1781ce0
Compare
Fold in main's independent valueless_after_move hidden-friend rename (#395) via the shared call_through_vtable helper; drop protocol_test.cc's newly-duplicated per-operator valueless-call death tests since one shared helper only needs one death test to cover it. Relocate the protocol_view overload-ambiguity regression test (#391) alongside the other operator* tests in protocol_operator_tests.cc. wip: fix clang-tidy issues from PR #388
1781ce0 to
851fcaa
Compare
philipcraig
left a comment
There was a problem hiding this comment.
Two new things since last round, both filed as issues so they can be handled here or after:
- #400:
call_through_vtablein vtable.hh only usesprotocol_objectinside theassert, so the installed header fails under-DNDEBUG -Wall -Wextra -Werrorwithunused parameter 'protocol_object'.mainis clean under the same flags. CI has no-Wextra, so it stays green.[[maybe_unused]]fixes it. - #401: rejecting
==/<=>turned a member comparison on the interface from silently ignored (whatmaindoes and DRAFT.md still says) into a hard consteval throw, so interfaces that compile onmainstop compiling, including the= defaultidiom. Options are in the issue.
Docs still saying the old thing: protocol.hh:32 ("Neither implementation currently supports operators other than operator()"), DRAFT.md:152-154 (protocol does not provide operator* or operator->) and DRAFT.md:565-567 (member comparisons "are not generated"). The protocol.hh line is a one-word fix; the DRAFT.md ones can wait for #396 if that is imminent.
#403: operator new/delete on an interface are dropped by the is_static_member skip with no diagnostic, while the new mangling tests assert codes for them. Probably intended, so a docs decision rather than a code one.
The rest are inline and each points at an issue (#402, #404, #405, #406, #407). Nothing here blocks merging.
|
|
||
| template <std::meta::info Member, typename Vtable, typename ProtocolObject, | ||
| typename VtablePtr, typename Object, typename... Args> | ||
| decltype(auto) call_through_vtable(ProtocolObject* protocol_object, |
There was a problem hiding this comment.
#400: protocol_object is only used inside the assert, so with NDEBUG this is -Werror=unused-parameter in an installed header. [[maybe_unused]] here; the issue also notes the -O0 cost of the helper not being inline.
| } | ||
|
|
||
| // Some operators are excluded from protocol interfaces. | ||
| consteval bool is_unsupported_operator(std::meta::operators op) { |
There was a problem hiding this comment.
#401: on main a member operator== on the interface was silently ignored; this makes it a hard error, so
struct I { int f() const; bool operator==(const I&) const = default; };no longer compiles against any candidate. There is also no rejection test for any of the seven comparisons; UnsupportedOperatorsAreRejected covers = and co_await only.
| is_operator<std::meta::operators::op_parentheses>(member) || | ||
| is_operator<std::meta::operators::op_square_brackets>(member); | ||
| return !is_special_member_function(member) && | ||
| (has_identifier(member) || is_operator_function(member)); |
There was a problem hiding this comment.
#402: operator T() has no identifier and is_operator_function is false for it, so conversion functions fall out here before conformance or rejection sees them. A candidate without the conversion conforms, and static_cast<bool>(p) is ill-formed even when both sides declare it. Pre-existing, but this is now the only operator category that neither forwards nor throws.
| declared_argument_count(member) != 0) { | ||
| std::vector<std::meta::info> params = parameters_of(member); | ||
| if (!params.empty() && is_explicit_object_parameter(params.front())) { | ||
| std::string name = has_identifier(member) |
There was a problem hiding this comment.
#406: this name expression is a copy of the one eight lines up, and operator_diagnostic_name below renders names a third way (no overload, no parameter list). Hoisting name once and nesting the two operator checks under a single if (is_operator_function(member)) removes the duplication and gives every rejection the same wording.
| checking is O(N\*M) in the number of interface and candidate member | ||
| functions. | ||
|
|
||
| - Guidance: Developers should refer to `protocol_test.cc`, |
There was a problem hiding this comment.
#407: The deleted Limitations bullet carried four statements that are still true and are now documented nowhere: member function templates are not matched (#393), the generated wrapper does not apply an interface member's ref-qualifier (#356), there is no conversion between wrappers of different interfaces, and conformance checking is O(N*M). They should come back, though they never really belonged in a contributing guide; the issue proposes a doc/limitations.md that this bullet links to, which can also list what #388 rejects. The Supported Features bullet above still names only operator().
|
|
||
| #endif | ||
|
|
||
| TEST(ReflectionProtocolTest, OperatorSquareBrackets) { |
There was a problem hiding this comment.
#404: from here down each test declares its own Interface/Conforming pair and instantiates all three wrapper kinds, about 132 define_aggregate generations for the TU. It is roughly 3 s of instantiation in every job that builds the tests; one shared interface per operator family halves it in a matched experiment. Test-structure optimisation only, no design question.
| Vtable> { | ||
| // Build the function-pointer type R(*)(Args...) noexcept(...) from the | ||
| // method's return type, parameter types and noexcept-ness. | ||
| static consteval std::meta::info fn_ptr_type() { |
There was a problem hiding this comment.
#405: third copy of this builder, after member_function_thunk_for::fn_ptr_type() and the inline one in generate_vtable_specs. vtable.hh's copy already differs (dealias on parameter types, relevant to #397); all three must agree for the thunk specialisation to match the vtable entry. One function_pointer_type_of(info) in vtable.hh would serve all three.
Extend operator forwarding support to more overloadable operators.
operator=,operator co_await, the comparison operators, explicit-object members and unaryoperator&are rejected on an interface.Fixes a self-assignment bug:
protocol's copy/move assignment usedthis == &other, which a forwardedoperator&would hijack; now usesstd::addressof.We opted not to use macros to reduce code verbosity. The limits of C++26 reflection mean that some boilerplate structure for operator thunks is somewhat repetitive.