Repository navigation
Conversation
…cess check [] A class member designated by a splice-expression is accessible from any point ([class.access.base]), and, as clarified by CWG 3109, the additional protected member access check of [class.protected] does not apply to it either. The front end already skipped the ordinary access check for a splice (is_splicer in the symbol locator), but still did the [class.protected] check when forming a pointer to member with &[:r:] and when naming the member with obj.[:r:] or p->[:r:], also in template instantiations. An operand made by scan_expr_splicer for a nonstatic member now has the new designated_by_splice flag, which suppresses the check in conv_sym_for_member_operand_to_ptr_to_member, and a member selection through a splice no longer asks cast_pointer_for_field_selection for the check. The conversion of the object expression to the designating class is still checked. Also: new tests reflections/splice_protected_access.sft.cpp and reflections/splice_protected_access_neg.sft.cpp, and a src/Changes entry.
…std::meta::members_of [] std::meta::members_of collected the nonstatic data members of a class first and then the entities listed in the class or namespace scope kind by kind (functions, types, variables, templates, namespaces), so the members came out grouped by kind. P2996R13 ([meta.reflection.member.queries]) requires the members of a class to appear in the order in which they are declared, with the implicitly-declared special members after the user-declared ones. The collected reflections are now sorted by the positions of the declarations of the members (sort_reflections_in_declaration_order, using compare_member_declaration_order), with compiler-generated functions last. Members with the same position, such as those of a class defined by std::meta::define_aggregate, keep their relative order. The members of a namespace are sorted the same way. Also: a new test (reflections/members_of_order.sft.cpp) and a src/Changes entry.
As specified in P2996R13 ([meta.reflection.names]), source_location_of has no "Constant When" clause. For a reflection of a value, of a type other than a class or enumeration type (that is not a type alias), of the global namespace, or of a data member description, it now returns a value-initialized std::source_location instead of failing; so does any other reflection without a declaration position. A cv-qualified class or enumeration type uses the position of the class or enumeration. build_source_location_value accepts a null position to build a value-initialized std::source_location (a null __impl pointer). Also: a new test (reflections/source_location_of.sft.cpp) and a src/Changes entry.
parent_of_reflection, which implements std::meta::parent_of and std::meta::has_parent, did not handle a reflection of a namespace (an iek_scope entry) or of a namespace alias (an iek_namespace entry), so parent_of(^^ns) was not a constant expression and has_parent(^^ns) was false. P2996R13 ([meta.reflection.scope]) allows any namespace or namespace alias other than the global namespace; the result is the enclosing namespace. The parent is now found from the source correspondence of the namespace (or alias), as for other namespace members. The global namespace has no source correspondence and so still has no parent. Also: a new test (reflections/parent_of_namespace.sft.cpp) and a src/Changes entry.
…on type [] For a reflection of a nonstatic member function, type_of returned the routine type of the function, whose this_class is set. That type is not identical to the corresponding function type, so type_of(^^S::f) did not compare equal to ^^int(int) and the spliced type did not match a partial specialization such as X<R(A...)>. The type of a nonstatic member function is a plain function type ([dcl.fct]), including its cv-qualifiers and ref-qualifier. type_of now drops the this_class with routine_type_without_this_class (which keeps the qualifiers), building the new type in file scope memory so that the reflection outlives the evaluation. Also: a new test (reflections/type_of_member_function.sft.cpp) and a src/Changes entry.
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.
Five independent fixes that bring the reflection support closer to
P2996R13 and the current working draft. Each fix
is a separate commit with its own test in
tests/tests/reflections/and itsown
src/Changesentry.There is no open issue for these; I can open a Discussion first if that is
preferred.
splice_protected_access,splice_protected_access_negmembers_ofin declaration ordermembers_of_ordersource_location_ofis a constant for every reflectionsource_location_ofparent_of/has_parentfor namespacesparent_of_namespacetype_ofof a nonstatic member functiontype_of_member_functionMotivation
These fixes came up while building a reflection-based replacement for Qt's
moc: a CRTP base that builds a
QMetaObjectfrom annotated members of thederived class. With clang-p2996 and GCC the plain code works. EDG needed a
workaround for each item below. Each "Before" is the EDG workaround, and
each "After" is the code that now works.
Pointers to non-public members (splice access,
type_of)The library takes pointers to protected and private slots and properties of
the user's class from outside that class.
Before:
&[:M:]was rejected for protected members.std::meta::extractdoes not check access, but it needs the exact pointer-to-member type, and
type_of(M)could not be used to build it:After:
The function type of a method (
type_of)Qt's
SignalData<R(Args...)>andMethodData<R(Args...)>are specializedon a function type.
Before:
typename[:type_of(m):]did not matchR(Args...), so the type wasrebuilt:
After:
Declaration order (
members_of,source_location_of)moc lists signals, slots and properties in declaration order. Some signals
are data members and others are member functions.
Before:
members_ofput all data members first, so every list was re-sortedby source position:
Such a helper also has to accept any reflection, which needs
source_location_ofto be a constant for all of them.After: the
members_oforder is used as is.Qualified names (
parent_of)moc writes fully qualified class names. Serializers need them too, for XML
element names, a JSON type tag or type names in a schema. They need the plain
identifier of each enclosing namespace, often joined with a separator other
than
::.display_string_ofcannot provide that portably: its format isunspecified, and for
app::CounterEDG and GCC printapp::Counterwhileclang-p2996 prints
Counter.Before:
parent_ofdid not accept a namespace, so the EDG build useddisplay_string_ofand relied on its implementation-defined format:After:
Details
Splice-expressions and protected members
A member designated by a splice-expression is accessible from any point, and
the
[class.protected]check does not apply to it (CWG 3109). The ordinaryaccess check was already skipped for splices. The protected check was still
done for
&[:r:],obj.[:r:]andp->[:r:], including in templateinstantiations, so these were rejected outside the members and friends of the
class:
The conversion of the object expression to the designating class is still
checked, so
e.[:r:]through a private base remains an error.std::meta::members_ofin declaration orderMembers used to come out grouped by kind: nonstatic data members first, then
functions, types, variables and templates. They are now sorted by the position
of their declarations. Compiler-generated members come last, after the
user-declared ones. Members without a position, such as those created by
define_aggregate, keep their relative order. Namespace members are sortedthe same way, although the standard leaves their order unspecified.
std::meta::source_location_ofsource_location_ofhas no "Constant When" clause, so it must be a constantexpression for every reflection. Values, types that are neither class nor
enumeration types, the global namespace and data member descriptions now give
a value-initialized
std::source_location. Previously these calls were notconstant expressions.
std::meta::parent_offor namespacesparent_ofandhas_parentnow accept a namespace or namespace alias otherthan
::and return the enclosing namespace. Previouslyparent_of(^^ns)wasnot a constant expression and
has_parent(^^ns)returned false.std::meta::type_ofof a nonstatic member functionThe result is now a plain function type that keeps its cv-qualifiers,
ref-qualifier and
noexcept. Previously it still carried the class. As aresult,
type_of(^^S::f) == ^^int(int)holds, and the spliced type matchespartial specializations such as
X<R(A...)>andX<R(A...) const noexcept>.Implementation notes
designated_by_splicebit onan_operandis setby
scan_expr_splicerfor nonstatic members and suppresses the check inconv_sym_for_member_operand_to_ptr_to_member. Member selections pass!locator.is_splicerasdo_protected_member_check. Settingaccess_control_error_reportedinstead would have been shorter, but thatflag means "an error was already reported" and would mislead later code.
members_of. The scope keeps a separate list per entity kind, so thecollected reflections are sorted afterwards
(
sort_reflections_in_declaration_order). The sort comparesdecl_positionwithcompare_source_positions, puts compiler-generatedroutines last and breaks ties by the original index.
source_location_of. The entity kind decides whether there is aposition.
build_source_location_valueaccepts a null position and buildsa value-initialized
std::source_location.parent_of.parent_of_reflectiontakes the source correspondence ofa namespace (
iek_scope) or a namespace alias (iek_namespace). Theglobal namespace has none, so it still has no parent.
type_of.routine_type_without_this_classdrops the class and keepsthe qualifiers. The type is built in the file scope region so that the
reflection outlives the evaluation. Reflections of types compare with
identical_types, so a fresh type per call compares equal.Not addressed here
Found while testing, left for separate changes:
members_oflists a member template twice: once as the template and onceas its prototype routine, variable or type.
members_oflists only the implicit special members that are alreadydeclared. The example in [meta.reflection.member.queries] expects all six.
display_string_ofdrops the cv-qualifiers of a qualified function type:void () noexceptforvoid() const noexcept.Testing
edg-docker-test --non-interactivein the defaultdayposture (debugbuild, configurations
edg_x86_64andedg_x86_64_cp) ran 45,433 tests ineach configuration. There were no regressions, and the new tests match
their recorded output.
cwg/cwg1835andcwg/cwg2881differ from their recordings. Thedifferences are a symbol name in a linker message and the position of an
internal error. A run on unmodified
upstream/mainat 4b6c10b gives thesame differences, so they are not caused by this branch.