From ab491b329dd2c6d1e4dcdcab18604819dbb040bf Mon Sep 17 00:00:00 2001 From: Bakiev Damir Date: Tue, 10 Nov 2026 11:30:47 +0200 Subject: [PATCH] Various implementation improvements [GH #330] --- src/class_decl.c | 13 +- src/decl_inits.c | 2 +- src/decl_spec.c | 1 + src/decls.c | 3 +- src/expr.c | 23 +- src/exprutil.c | 17 +- src/exprutil.h | 9 + src/folding.c | 3 +- src/interpret.c | 280 +++++++++++++++--- src/layout.c | 4 +- src/lower_name.c | 6 + src/overload.c | 21 +- ...h-330-meta-implementation-improvements.txt | 52 ++++ src/symbol_tbl.c | 47 +-- src/symbol_tbl.h | 4 +- src/trans_corresp.c | 4 +- tests/expectations/edg_x86_32/reflections.log | 5 + .../edg_x86_32_cp/reflections.log | 6 + .../edg_x86_64_cp/reflections.log | 6 + .../.members_of_order.rto/default.1.1.txt | 1 + .../.members_of_order.rto/edg_x86_32.1.1.txt | 34 +++ .../.parent_of_namespace.rto/default.1.1.txt | 1 + .../edg_x86_32.1.1.txt | 34 +++ .../.source_location_of.rto/default.1.1.txt | 1 + .../edg_x86_32.1.1.txt | 34 +++ .../default.1.1.txt | 1 + .../default.1.2.txt | 5 + .../edg_x86_32.1.1.txt | 34 +++ .../default.1.1.txt | 18 ++ .../edg_x86_32.1.1.txt | 50 ++++ .../default.1.1.txt | 1 + .../edg_x86_32.1.1.txt | 34 +++ .../reflections/members_of_order.sft.cpp | 172 +++++++++++ .../reflections/parent_of_namespace.sft.cpp | 73 +++++ .../reflections/source_location_of.sft.cpp | 62 ++++ .../splice_protected_access.sft.cpp | 134 +++++++++ .../splice_protected_access_neg.sft.cpp | 43 +++ .../type_of_member_function.sft.cpp | 70 +++++ 38 files changed, 1208 insertions(+), 100 deletions(-) create mode 100644 src/pending_changes/gh-330-meta-implementation-improvements.txt create mode 100644 tests/tests/reflections/.members_of_order.rto/default.1.1.txt create mode 100644 tests/tests/reflections/.members_of_order.rto/edg_x86_32.1.1.txt create mode 100644 tests/tests/reflections/.parent_of_namespace.rto/default.1.1.txt create mode 100644 tests/tests/reflections/.parent_of_namespace.rto/edg_x86_32.1.1.txt create mode 100644 tests/tests/reflections/.source_location_of.rto/default.1.1.txt create mode 100644 tests/tests/reflections/.source_location_of.rto/edg_x86_32.1.1.txt create mode 100644 tests/tests/reflections/.splice_protected_access.rto/default.1.1.txt create mode 100644 tests/tests/reflections/.splice_protected_access.rto/default.1.2.txt create mode 100644 tests/tests/reflections/.splice_protected_access.rto/edg_x86_32.1.1.txt create mode 100644 tests/tests/reflections/.splice_protected_access_neg.rto/default.1.1.txt create mode 100644 tests/tests/reflections/.splice_protected_access_neg.rto/edg_x86_32.1.1.txt create mode 100644 tests/tests/reflections/.type_of_member_function.rto/default.1.1.txt create mode 100644 tests/tests/reflections/.type_of_member_function.rto/edg_x86_32.1.1.txt create mode 100644 tests/tests/reflections/members_of_order.sft.cpp create mode 100644 tests/tests/reflections/parent_of_namespace.sft.cpp create mode 100644 tests/tests/reflections/source_location_of.sft.cpp create mode 100644 tests/tests/reflections/splice_protected_access.sft.cpp create mode 100644 tests/tests/reflections/splice_protected_access_neg.sft.cpp create mode 100644 tests/tests/reflections/type_of_member_function.sft.cpp diff --git a/src/class_decl.c b/src/class_decl.c index dd6990113f..ca0564d110 100644 --- a/src/class_decl.c +++ b/src/class_decl.c @@ -21533,11 +21533,12 @@ be entered. /* Copy the type (which may have been changed by apply_bit_field_size) into the field entry. */ field->type = member_type; - /* For an unnamed bit field, do not create the field symbol. */ + /* For an unnamed bit field, create an unnamed field symbol that is not + entered in the symbol table. */ if (unnamed_field && decl_info->is_bit_field) { - /* All field entries for an unnamed bit field share the same symbol. It is - used for easy identification. */ - field->source_corresp.assoc_info = (char *)get_unnamed_field_symbol(); + member_sym = make_unnamed_field_symbol(&locator->source_position); + member_sym->variant.field.ptr = field; + field->source_corresp.assoc_info = (char *)member_sym; /* Update the source correspondence information manually -- there's no symbol. */ field->source_corresp.decl_position = locator->source_position; @@ -21633,7 +21634,9 @@ be entered. /* Fields of closure types may be declared "on the fly" (in the case of implicit captures), and are not subject to pragmas in any case. However, we do want to record the declaration of init-capture fields. */ - } else if (member_sym != NULL && !decl_info->is_anonymous_union) { + } else if (member_sym != NULL && !decl_info->is_anonymous_union && + !is_unnamed_field_symbol(member_sym)) { + /* (The declaration of an unnamed bit-field was recorded above.) */ record_symbol_declaration(SRK_DECLARATION | SRK_DEFINITION, member_sym, &locator->source_position, decl_state->source_sequence_entry); diff --git a/src/decl_inits.c b/src/decl_inits.c index c077697a93..c844e3d29d 100644 --- a/src/decl_inits.c +++ b/src/decl_inits.c @@ -9201,7 +9201,7 @@ non-NULL, set *end_of_list to the last initializer in the list. } /* if */ } else if (!field->compiler_generated) { a_symbol_ptr sym = symbol_for(field); - if (sym == NULL || sym->is_error || sym == get_unnamed_field_symbol()) { + if (sym == NULL || sym->is_error || is_unnamed_field_symbol(sym)) { /* Skip over any error and unnamed fields. */ continue; } /* if */ diff --git a/src/decl_spec.c b/src/decl_spec.c index 43b0e74d85..08854a2254 100644 --- a/src/decl_spec.c +++ b/src/decl_spec.c @@ -7207,6 +7207,7 @@ template. dsi_flags is the set of input flags passed to decl_specifiers. } else if (is_definition) { /* An unnamed enum type. mark_defined can't be called to record the declaration, so the subroutines that do so are called directly. */ + set_decl_sequence_number(tag_sym); #if MAINTAIN_CLASS_MEMBER_LIST record_class_member_declaration((char*)enum_type, iek_type); #endif /* MAINTAIN_CLASS_MEMBER_LIST */ diff --git a/src/decls.c b/src/decls.c index 3394db4688..245a1fdff8 100644 --- a/src/decls.c +++ b/src/decls.c @@ -2834,10 +2834,11 @@ is invalid. Also promote the fields of the union type to the current scope. /* Promote the fields of the anonymous union to the current scope, and do some error checking on the anonymous union's members. */ assoc_object_sym = make_anonymous_parent_object_symbol( - (a_symbol_kind)sk_variable, + sk_variable, &pos_curr_token, scope_stack[decl_scope_level].number); assoc_object_sym->variant.variable.ptr = vp; + vp->source_corresp.assoc_info = (char*)assoc_object_sym; if (at_file_or_namespace_scope) { set_namespace_membership(assoc_object_sym, &vp->source_corresp, (a_namespace_ptr)NULL); diff --git a/src/expr.c b/src/expr.c index 8986d35f0c..5afe6288dc 100644 --- a/src/expr.c +++ b/src/expr.c @@ -10841,6 +10841,7 @@ a left parenthesis in the source. { locator->source_position = eriep->saved_operand.position; } /* if */ + locator->is_splicer = eriep->saved_operand.designated_by_splice; /* For a qualified id case, make sure the id is a member of the class or one of its base classes. */ if (need_member_sym_check && !*err && is_qualified) { @@ -11553,7 +11554,8 @@ case). operand_1, is_arrow_operator, member_sym, projection_member_sym, (a_boolean)locator.access_control_error_reported, - /*do_protected_member_check=*/TRUE, &member_position); + /*do_protected_member_check=*/!locator.is_splicer, + &member_position); do_field_selection_operation(operand_1, orig_class_struct_union_type, is_arrow_operator, /*compiler_generated=*/FALSE, @@ -11629,7 +11631,7 @@ case). error_and_make_error_operand(ec_expr_not_constant, result); } else { if (!force_indefinite_function && - member_sym->kind == (a_symbol_kind)sk_member_function) { + symbol_is(member_sym, sk_member_function)) { /* For a simple non-overloaded function, adjust the selector to the proper class. We don't do this for the cases that go through overload resolution, since that @@ -11639,15 +11641,16 @@ case). is_arrow_operator, member_sym, projection_member_sym, - (a_boolean)locator. - access_control_error_reported, - /*do_protected_member_check=*/TRUE, + (a_boolean)locator + .access_control_error_reported, + /*do_protected_member_check=*/ + !locator.is_splicer, &member_position); } /* if */ /* Make an operand for the function with the selector bound to it. */ - if (member_sym->kind == (a_symbol_kind)sk_overloaded_function || - member_sym->kind == (a_symbol_kind)sk_function_template || + if (symbol_is(member_sym, sk_overloaded_function) || + symbol_is(member_sym, sk_function_template) || force_indefinite_function) { /* Overloaded function or member template. */ make_indefinite_function_operand(projection_member_sym, @@ -11672,7 +11675,7 @@ case). &end_position), rep, result); - set_operand_name_reference_from_locator(result, &locator); + set_operand_id_details_from_locator(result, &locator); } /* if */ copy_operand(operand_1, bound_function_selector) /*lint !e413*/; bind_member_function_operand_to_selector(bound_function_selector, @@ -11690,7 +11693,7 @@ case). &end_position), rep, result); - set_operand_name_reference_from_locator(result, &locator); + set_operand_id_details_from_locator(result, &locator); treat_as_static_member: result->is_name_followed_by_left_paren = member_name_followed_by_left_paren; @@ -41914,6 +41917,7 @@ FIXME: This is currently incomplete. (a_ref_entry*)NULL, result); result->is_id_expression = TRUE; + result->designated_by_splice = TRUE; } else if (iek == iek_variable) { vp = (a_variable*)rvp->entity.ptr; variable_case: @@ -41936,6 +41940,7 @@ FIXME: This is currently incomplete. (a_ref_entry*)NULL, result); result->is_id_expression = TRUE; + result->designated_by_splice = TRUE; } else { make_function_designator_operand(symbol_for(rp), /*is_qualified_name=*/FALSE, diff --git a/src/exprutil.c b/src/exprutil.c index af7ed5fb67..2eb97af5f9 100644 --- a/src/exprutil.c +++ b/src/exprutil.c @@ -3489,6 +3489,7 @@ values. operand->is_qualified_name = FALSE; operand->is_microsoft_deferred_name = FALSE; operand->access_control_error_reported = FALSE; + operand->designated_by_splice = FALSE; operand->is_operand_of_address_of = FALSE; operand->has_required_ptr_to_member_form = FALSE; operand->is_template_id = FALSE; @@ -6344,10 +6345,11 @@ This includes in particular name reference information. { set_operand_name_reference_from_locator(operand, locator); operand->is_qualified_name = locator->is_qualified_name; - if (operand->kind == (an_operand_kind)ok_indefinite_function || - operand->kind == (an_operand_kind)ok_undefined_symbol + operand->designated_by_splice = locator->is_splicer; + if (is_indefinite_function_operand(operand) || + is_undefined_symbol_operand(operand) #if MICROSOFT_EXTENSIONS_ALLOWED - || operand->kind == (an_operand_kind)ok_property_ref + || is_property_ref_operand(operand) #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ ) { operand->id_position = locator->source_position; @@ -7422,6 +7424,7 @@ destroyed its source position, etc. Restore such things from orig_operand->selector_is_object_pointer; operand->virtual_function = orig_operand->virtual_function; operand->is_qualified_name = orig_operand->is_qualified_name; + operand->designated_by_splice = orig_operand->designated_by_splice; operand->access_control_error_reported = orig_operand->access_control_error_reported; operand->is_operand_of_address_of = @@ -23641,7 +23644,7 @@ by an "&" in the source, and *ampersand_position gives its position. { an_operand orig_operand; a_symbol_ptr member_sym, fund_sym; - a_boolean has_required_ampersand; + a_boolean has_required_ampersand, check_protected_access; a_boolean allow_addr_of_managed_member = FALSE, force_node = FALSE; has_required_ampersand = (ampersand_position != NULL && @@ -23669,11 +23672,15 @@ by an "&" in the source, and *ampersand_position gives its position. #if MICROSOFT_EXTENSIONS_ALLOWED allow_addr_of_managed_member = operand->allow_addr_of_managed_member; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ + /* The protected member access check does not apply to a member designated + by a splice-expression (N5046 [class.protected]). */ + check_protected_access = !operand->access_control_error_reported && + !operand->designated_by_splice; /* Make an operand for a pointer-to-member constant. */ make_ptr_to_member_constant_operand(member_sym, member_sym, &orig_operand.position, end_position_of_operand(&orig_operand), - !operand->access_control_error_reported, + check_protected_access, (a_boolean)operand->is_qualified_name, has_required_ampersand, allow_addr_of_managed_member, diff --git a/src/exprutil.h b/src/exprutil.h index 9deb1867f5..de7281c947 100644 --- a/src/exprutil.h +++ b/src/exprutil.h @@ -364,6 +364,15 @@ typedef struct an_operand { essentially still just a representation for an identifier, e.g., ok_indefinite_function and ok_sym_for_member. */ + a_bit_field designated_by_splice:1; + /* TRUE if this operand was generated from a + splice-expression designating a class member. + Such a member is accessible from any point, and + the additional protected member access check + (N5046 [class.protected]) does not apply to it. + Like access_control_error_reported, this + remains meaningful only for ok_sym_for_member + operands. */ a_bit_field is_operand_of_address_of:1; /* TRUE if this operand is the immediate operand of an "&" address-of operator. This is diff --git a/src/folding.c b/src/folding.c index 2a40aa2923..7975b542fb 100644 --- a/src/folding.c +++ b/src/folding.c @@ -9058,7 +9058,8 @@ architectures for which these assumptions are not valid. } else if (!type_is(type, tk_union) && field->is_bit_field && field->bit_size != 0 && unnamed_bit_field_is_padding() && - symbol_for(field) == get_unnamed_field_symbol()) { + symbol_for(field) != NULL && + is_unnamed_field_symbol(symbol_for(field))) { /* An unnamed bit-field of non-zero width is padding. (In a union, an unnamed bit-field overlaps the other members, so it is handled by the size test above instead.) */ diff --git a/src/interpret.c b/src/interpret.c index 5ed81b62ab..3a91724e15 100644 --- a/src/interpret.c +++ b/src/interpret.c @@ -11434,15 +11434,20 @@ member and the object are marked initialized as subobjects of complete_obj the function-name field is set as for __builtin_source_location (the enclosing function's __func__ string); otherwise it is set to the empty string, which is what source_location_of uses since an entity's function field is -implementation-defined. On any construction failure *p_result is set to FALSE -(any needed diagnostic having been issued). Return TRUE if the source-location -type was valid (so the builtin/intrinsic was handled), FALSE if it was an error -type (which has already been diagnosed). +implementation-defined. If use_pos is NULL, the result is a value-initialized +std::source_location (whose pointer member is null, so that all its queries +return zero or the empty string); that is only possible for a class result. +On any construction failure *p_result is set to FALSE (any needed diagnostic +having been issued). Return TRUE if the source-location type was valid (so +the builtin/intrinsic was handled), FALSE if it was an error type (which has +already been diagnosed). */ { a_boolean handled = TRUE; a_gnu_source_location_type_info interp_inf; + check_assertion(use_pos != NULL || result_class_type != NULL); + /* Load the type information; if the type is invalid, silently fail (this has already been diagnosed). */ interp_inf = gnu_source_location_impl(); @@ -11451,11 +11456,13 @@ type (which has already been diagnosed). do_constexpr_fail(*p_result); handled = FALSE; } else { - a_byte *obj_storage; - /* Allocate the source location __impl object. */ - alloc_storage_promotable_object(ips, interp_inf.impl_type, &obj_storage, - p_result); - if (*p_result) { + a_byte *obj_storage = NULL; + if (use_pos != NULL) { + /* Allocate the source location __impl object. */ + alloc_storage_promotable_object(ips, interp_inf.impl_type, &obj_storage, + p_result); + } /* if */ + if (*p_result && obj_storage != NULL) { /* Populate the source location __impl object. The fields (and their associated types) are guaranteed to have been validated when the source-location type was first used. */ @@ -11496,14 +11503,16 @@ type (which has already been diagnosed). do_constexpr_write_source_column(ips, use_pos, column_fp->type, column_f_bytes, p_result); mark_subobject_initialized(column_f_bytes, obj_storage); - + } /* if */ + if (*p_result) { if (result_class_type == NULL) { /* Scalar (pointer) result: result_storage is the __impl pointer. */ clear_address(result_storage, obj_storage); mark_complete_object_initialized(result_storage); } else { - /* Class result: store the __impl address in the source_location - object's single pointer member and mark the member and the object + /* Class result: store the __impl address (null for a + value-initialized source_location) in the source_location object's + single pointer member and mark the member and the object initialized as subobjects of complete_obj, mirroring make_reflective_string_view. */ a_field_ptr slfp = next_alloc_field(fields_of(result_class_type)); @@ -16088,7 +16097,10 @@ static a_boolean do_constexpr_std_meta_type_of( a_byte *result_storage, a_byte *complete_obj) /* -Implement std::meta::type_of(info). +Implement std::meta::type_of(info). The type of a nonstatic member function +is a plain function type (N5046 [dcl.fct]): the class of the implicit object +parameter is not part of it, but its cv-qualifiers and ref-qualifier are +(e.g., "void() const" for "void f() const"). See do_constexpr_intrinsic_call for the meaning of the parameters. */ @@ -16133,6 +16145,17 @@ See do_constexpr_intrinsic_call for the meaning of the parameters. default: break; } /* switch */ + if (tp != NULL && type_is(tp, tk_routine)) { + tp = skip_typerefs(tp); + if (tp->variant.routine.extra_info->this_class != NULL) { + /* Drop the class of a nonstatic member function from its type. The new + type must outlive the evaluation. */ + a_memory_region_number region_to_switch_back_to; + switch_to_file_scope_region(®ion_to_switch_back_to); + tp = routine_type_without_this_class(tp, /*copy_default_args=*/FALSE); + switch_back_to_original_region(region_to_switch_back_to); + } /* if */ + } /* if */ if (tp == NULL) { info_with_pos(ec_invalid_reflection_for_intrinsic, &call_node->position, ips); @@ -16186,12 +16209,14 @@ static a_boolean parent_of_reflection(a_reflection_value *rvp, Set *parent_rvp to the reflection of the parent that std::meta::parent_of and std::meta::has_parent report for the entity reflected by rvp. That parent is the enumeration type of an enumerator, the class of which the entity is a -member, or else the nearest namespace or file scope that encloses its -declaration. Return TRUE when there is such a parent, and FALSE (leaving -*parent_rvp untouched) when the entity is of a kind that has none to report, -such as an unnamed constant or a type that is not a class, an enumeration, or -a typedef. rvp is modified in place: It is normalized by applying -strip_template_arg and extract_reflected_entity to it (in that order). +member, the function whose body encloses its declaration, or else the nearest +namespace or file scope that encloses its declaration (for a namespace or +namespace alias, that is the enclosing namespace). Return TRUE when there is +such a parent, and FALSE (leaving *parent_rvp untouched) when the entity is of +a kind that has none to report, such as an unnamed constant, the global +namespace, a structured binding, or a type that is not a class, an +enumeration, or a typedef. rvp is modified in place: It is normalized by +applying strip_template_arg and extract_reflected_entity to it (in that order). */ { a_boolean result = FALSE; @@ -16237,7 +16262,22 @@ strip_template_arg and extract_reflected_entity to it (in that order). } break; case iek_variable: - scp = &((a_variable*)rvp->entity.ptr)->source_corresp; + /* A structured binding has no parent. */ + { a_variable *vp = (a_variable*)rvp->entity.ptr; + if (!vp->is_struct_binding) { + scp = &vp->source_corresp; + } /* if */ + } + break; + case iek_namespace: + /* A namespace alias. */ + scp = &((a_namespace*)rvp->entity.ptr)->source_corresp; + break; + case iek_scope: + /* A namespace is reflected as its scope; the correspondence is that of + the namespace. The global namespace (and any other scope) has + none, and therefore no parent. */ + scp = source_corresp_for_reflection(rvp); break; default: break; @@ -16246,6 +16286,9 @@ strip_template_arg and extract_reflected_entity to it (in that order). if (scp->is_class_member) { parent_rvp->entity.kind = iek_type; parent_rvp->entity.ptr = (char*)scp_parent_class(scp); + } else if (scp->is_local_to_function && scp->enclosing_routine != NULL) { + parent_rvp->entity.kind = iek_routine; + parent_rvp->entity.ptr = (char*)scp->enclosing_routine; } else { parent_rvp->entity.kind = iek_scope; parent_rvp->entity.ptr = (char*)get_parent_scope_of(scp); @@ -17050,19 +17093,23 @@ See do_constexpr_intrinsic_call for the meaning of the parameters. static a_boolean do_constexpr_std_meta_source_location_of( - an_interpreter_state *ips, - a_routine_ptr callee, - an_expr_node_ptr call_node, - a_byte **p_arg_bytes, - a_byte *result_storage, - a_byte *complete_obj) + an_interpreter_state *ips, + a_routine_ptr callee, + ARG_UNUSED an_expr_node_ptr call_node, + a_byte **p_arg_bytes, + a_byte *result_storage, + a_byte *complete_obj) /* Implement std::meta::source_location_of(). It returns a std::source_location describing the declaration of the reflected entity. The file/line/column come from the entity's declaration position; the function-name -field is left empty (it is implementation-defined for an entity). The call -fails to be a constant expression for a reflection that has no associated -declaration position. +field is left empty (it is implementation-defined for an entity). N5046 +[meta.reflection.names] specifies the function without a Throws clause (in +P2996R13, without a "Constant When" clause), so the call is always a constant +expression: 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 yields a value-initialized std::source_location, as +does any other reflection with no associated declaration position. See do_constexpr_intrinsic_call for the meaning of the parameters. */ @@ -17071,26 +17118,57 @@ See do_constexpr_intrinsic_call for the meaning of the parameters. a_reflection_value *rvp = (a_reflection_value*)p_arg_bytes[0]; a_type_ptr rtp = skip_typerefs(callee->type), sl_type; a_source_correspondence_ptr - scp; + scp = NULL; a_source_position *use_pos = NULL; strip_template_arg(rvp); - scp = source_corresp_for_reflection(rvp); - if (scp != NULL && scp->decl_position.seq != 0) { + switch (rvp->entity.kind) { + case iek_constant: + /* A value has no source location; a named constant (an enumerator) is + an entity, which does. */ + scp = source_corresp_for_reflection(rvp); + if (scp != NULL && scp->name == NULL) scp = NULL; + break; + case iek_type: + { a_type_ptr tp = (a_type_ptr)rvp->entity.ptr; + if (type_is_typedef(tp)) { + /* A type alias. */ + scp = source_corresp_for_reflection(rvp); + } else { + tp = skip_typerefs(tp); + if (is_immediate_class_type(tp) || is_immediate_enum_type(tp)) { + scp = &tp->source_corresp; + } /* if */ + } /* if */ + } + break; + case iek_base_class: + use_pos = &((a_base_class*)rvp->entity.ptr)->decl_position; + break; + case iek_attribute: + use_pos = &((an_attribute*)rvp->entity.ptr)->position; + break; + case iek_none: + case iek_data_member_spec: + /* No source location. */ + break; + default: + /* The global namespace has no source correspondence. */ + scp = source_corresp_for_reflection(rvp); + break; + } /* switch */ + if (scp != NULL) { use_pos = &scp->decl_position; } /* if */ - if (use_pos == NULL) { - do_constexpr_fail(result); - info_with_pos(ec_invalid_reflection_for_intrinsic, &call_node->position, - ips); - } else { - check_assertion(type_is(rtp, tk_routine)); - sl_type = skip_typerefs(rtp->variant.routine.return_type); - (void)build_source_location_value(ips, use_pos, - /*use_current_function=*/FALSE, - sl_type, result_storage, complete_obj, - &result); + if (use_pos != NULL && use_pos->seq == 0) { + use_pos = NULL; } /* if */ + check_assertion(type_is(rtp, tk_routine)); + sl_type = skip_typerefs(rtp->variant.routine.return_type); + (void)build_source_location_value(ips, use_pos, + /*use_current_function=*/FALSE, + sl_type, result_storage, complete_obj, + &result); return result; } /* do_constexpr_std_meta_source_location_of */ @@ -17138,6 +17216,120 @@ not the scope). } /* collect_scoped_reflections */ +typedef struct a_member_order_key { + /* The sort key of a member in sort_reflections_in_declaration_order. */ + a_boolean is_implicit; + /* TRUE for an implicitly-declared member, i.e., a + compiler-generated function. */ + a_decl_sequence_number + seq; + /* The declaration sequence number of the symbol of + the member. */ +} a_member_order_key; + +namespace detail { + +template<> +struct Is_trivially_copyable_edg_impl : + Integral_constant { +}; /* Is_trivially_copyable_edg_impl */ + +template<> +struct Is_trivially_destructible_edg_impl : + Integral_constant { +}; /* Is_trivially_destructible_edg_impl */ + +} /* namespace detail */ + + +static a_decl_sequence_number decl_sequence_for_reflection( + a_reflection_value *rvp) +/* +Return the declaration sequence number (see a_symbol::decl_seq) of the symbol +of the entity reflected by rvp, or NO_DECL_SEQUENCE_NUMBER if the entity has +no symbol or its symbol has no sequence number. The latter is the case for +compiler-generated special member functions. +*/ +{ + a_decl_sequence_number seq = NO_DECL_SEQUENCE_NUMBER; + a_source_correspondence *scp = source_corresp_for_reflection(rvp); + + if (scp != NULL && scp->assoc_info != NULL) { + seq = ((a_symbol_ptr)scp->assoc_info)->decl_seq; + } /* if */ + return seq; +} /* decl_sequence_for_reflection */ + + +static int compare_member_order_keys(const a_member_order_key *k1, + const a_member_order_key *k2) +/* +Compare two member sort keys (see sort_reflections_in_declaration_order). +Return a negative value if the member with key k1 comes first, a positive +value if the member with key k2 does, and 0 if the keys are the same. +Implicitly-declared members come after all the others; among the rest, the +members are ordered by their declaration sequence numbers. +*/ +{ + int result; + + if (k1->is_implicit != k2->is_implicit) { + result = k1->is_implicit ? 1 : -1; + } else if (k1->seq != k2->seq) { + result = k1->seq < k2->seq ? -1 : 1; + } else { + result = 0; + } /* if */ + return result; +} /* compare_member_order_keys */ + + +static void sort_reflections_in_declaration_order( + Dyn_array *reflections) +/* +Sort the reflections of the members of a class or namespace in *reflections +into the order in which the members are declared, as std::meta::members_of +requires (N5046 [meta.reflection.member.queries]). The order is that of the +declaration sequence numbers of the symbols of the members (a_symbol::decl_seq; +see decl_sequence_for_reflection). A sequence number is assigned at the first +declaration of a member, so, unlike the declaration positions, the numbers are +not disturbed by macro expansions, and they do not move to the definition of a +member that is defined outside its class or namespace. Implicitly-declared +members (compiler-generated functions) have no sequence number and come after +all the others. Members whose keys are still the same keep their relative +order. +*/ +{ + size_t n = reflections->length(), i; + Dyn_array keys(n); + Dyn_array order(n); + Dyn_array sorted(n); + + for (i = 0; i < n; ++i) { + a_reflection_value *rvp = &(*reflections)[i]; + a_member_order_key key; + key.is_implicit = (rvp->entity.kind == iek_routine && + ((a_routine*)rvp->entity.ptr)->compiler_generated); + key.seq = key.is_implicit ? NO_DECL_SEQUENCE_NUMBER + : decl_sequence_for_reflection(rvp); + keys.push_back(key); + } /* for */ + for (i = 0; i < n; ++i) { + order.push_back(i); + } /* for */ + sort(&order, [&keys](size_t i1, size_t i2) { + int cmp = compare_member_order_keys(&keys[i1], &keys[i2]); + return cmp != 0 ? cmp < 0 : i1 < i2; + }); + for (i = 0; i < n; ++i) { + sorted.push_back((*reflections)[order[i]]); + } /* for */ + for (i = 0; i < n; ++i) { + (*reflections)[i] = sorted[i]; + } /* for */ +} /* sort_reflections_in_declaration_order */ + + static a_constant_ptr info_array_element_pointer( an_interpreter_state *ips, a_variable_ptr vp, @@ -17538,6 +17730,8 @@ See do_constexpr_intrinsic_call for the meaning of the parameters. ips); goto done; } /* if */ + /* The members were collected by kind; put them in declaration order. */ + sort_reflections_in_declaration_order(&all_reflections); /* Keep only members that are accessible from the given access_context. */ keep_accessible_reflections(&all_reflections, &scope_rv, &dc_rv); result = make_info_vector(ips, callee, call_node, &all_reflections, diff --git a/src/layout.c b/src/layout.c index 638258e04d..7050636514 100644 --- a/src/layout.c +++ b/src/layout.c @@ -1072,8 +1072,8 @@ the alignment of the class being laid out (as recorded in lob) as needed. !targ_zero_width_bit_field_affects_struct_alignment) || (union_case && !targ_bit_field_affects_union_alignment) || (!targ_unnamed_bit_field_affects_struct_alignment && - field->source_corresp.assoc_info == - (char *)get_unnamed_field_symbol())) { + symbol_for(field) != NULL && + is_unnamed_field_symbol(symbol_for(field)))) { /* Various cases where the bit field does not affect the alignment of the parent type: - zero-length bit fields when diff --git a/src/lower_name.c b/src/lower_name.c index 122cda012f..db032144a6 100644 --- a/src/lower_name.c +++ b/src/lower_name.c @@ -8607,6 +8607,12 @@ IA-64 ABI to distinguish function-local entities with the same name. goto done; } /* if */ } else if (sym->kind == (a_symbol_kind)sk_variable) { + if (sym->variant.variable.ptr->is_anonymous_parent_object) { + /* The symbol of an anonymous union variable is not entered in the + symbol table (see decl_anonymous_union_variable), so no + discriminator is computed for it. */ + goto done; + } /* if */ discriminator = sym->variant.variable.discriminator; } else if (is_class_struct_union_symbol(sym) && sym->variant.class_struct_union.extra_info != NULL) { diff --git a/src/overload.c b/src/overload.c index 62e5a4f2fe..1987aa772e 100644 --- a/src/overload.c +++ b/src/overload.c @@ -12343,6 +12343,7 @@ compiler_generated is TRUE if the function reference is compiler-generated. a_ref_entry_ptr rep; a_boolean eff_address_taken = address_taken; a_boolean is_qualified_name = FALSE; + a_boolean designated_by_splice = FALSE; a_boolean has_required_ptr_to_member_form = FALSE; a_boolean allow_addr_of_managed_member = FALSE; a_source_position *ampersand_position = NULL; @@ -12351,8 +12352,7 @@ compiler_generated is TRUE if the function reference is compiler-generated. a_source_position *id_position; #if CHECKING - if (base_function_symbol->kind != (a_symbol_kind)sk_routine && - base_function_symbol->kind != (a_symbol_kind)sk_member_function) { + if (!is_simple_function_symbol(base_function_symbol)) { internal_error("overloaded_function_catch_up: bad function_symbol"); } /* if */ #endif /* CHECKING */ @@ -12360,6 +12360,7 @@ compiler_generated is TRUE if the function reference is compiler-generated. if (orig_function_operand != NULL) { check_assertion(orig_function_operand != operand); is_qualified_name = orig_function_operand->is_qualified_name; + designated_by_splice = orig_function_operand->designated_by_splice; if (orig_function_operand->is_operand_of_address_of) { ampersand_position = &orig_function_operand->ampersand_position; has_required_ptr_to_member_form = @@ -12386,10 +12387,8 @@ compiler_generated is TRUE if the function reference is compiler-generated. is not evaluated. */ if (!curr_expr_is_potentially_evaluated()) eff_address_taken = FALSE; /* Check ambiguity and access. */ - if (base_overloaded_function_symbol->kind == - (a_symbol_kind)sk_overloaded_function || - base_overloaded_function_symbol->kind == - (a_symbol_kind)sk_function_template) { + if (symbol_is(base_overloaded_function_symbol, sk_overloaded_function) || + symbol_is(base_overloaded_function_symbol, sk_function_template)) { /* Use a special routine for overloaded functions because (a) overloaded functions are considered always accessible when checked through the normal routine and (b) the projection symbol here may point to the @@ -12397,6 +12396,7 @@ compiler_generated is TRUE if the function reference is compiler-generated. symbol. Templates are also treated like overloaded functions. */ make_locator_for_symbol(function_symbol, &function_symbol_locator); function_symbol_locator.source_position = *id_position; + function_symbol_locator.is_splicer = designated_by_splice; expr_overload_check_ambiguity_and_verify_access(&function_symbol_locator, overloaded_function_symbol); } else { @@ -12413,6 +12413,7 @@ compiler_generated is TRUE if the function reference is compiler-generated. #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ make_locator_for_symbol(sym_to_check, &function_symbol_locator); function_symbol_locator.source_position = *id_position; + function_symbol_locator.is_splicer = designated_by_splice; if (compiler_generated || operator_notation || expr_stack->suppress_diagnostics) { /* For ordinary call notation, access was checked when the name was @@ -12489,7 +12490,8 @@ compiler_generated is TRUE if the function reference is compiler-generated. overloaded_function_symbol, function_position, function_end_position, - !*access_error_reported, + !*access_error_reported && + !designated_by_splice, is_qualified_name, has_required_ptr_to_member_form, allow_addr_of_managed_member, @@ -12504,6 +12506,7 @@ compiler_generated is TRUE if the function reference is compiler-generated. function_position, function_end_position, rep, operand); + operand->designated_by_splice = designated_by_splice; #if MICROSOFT_EXTENSIONS_ALLOWED operand->allow_addr_of_managed_member = allow_addr_of_managed_member; #endif /* MICROSOFT_EXTENSIONS_ALLOWED */ @@ -13883,7 +13886,9 @@ selector is an object pointer, FALSE if it is an object. overloaded_function_symbol, access_error_reported, /*do_protected_member_check=*/ - !is_property, + !is_property && + !function_operand + ->designated_by_splice, &selector_position); } /* if */ } /* if */ diff --git a/src/pending_changes/gh-330-meta-implementation-improvements.txt b/src/pending_changes/gh-330-meta-implementation-improvements.txt new file mode 100644 index 0000000000..fadc286967 --- /dev/null +++ b/src/pending_changes/gh-330-meta-implementation-improvements.txt @@ -0,0 +1,52 @@ +//allowed_words: prot fct +[GH #330] +Various implementation improvements + +The following deviations of the implementation from N5046 have been +fixed: + + 1) A protected member designated by a splice-expression (as in &[:r:] or + obj.[:r:]) is accessible from any point, and the protected member + access check no longer applies to it (N5046 [class.protected], as + clarified by CWG 3109). + 2) std::meta::members_of now returns the members of a class or namespace + in declaration order (instead of grouped by kind), with the + implicitly-declared special members of a class last (N5046 + [meta.reflection.member.queries]). + 3) std::meta::source_location_of is now a constant expression for every + reflection: the result is a value-initialized std::source_location for + a reflection without a declaration (such as that of a value or of int), + and the location of the base-specifier or annotation for a direct base + class relationship or an annotation (N5046 [meta.reflection.names]). + 4) std::meta::parent_of now accepts a reflection of a namespace or + namespace alias, returns the enclosing function for a function-local + entity, and is not a constant expression for a structured binding + (N5046 [meta.reflection.queries]). + 5) std::meta::type_of of a nonstatic member function is now a plain + function type, including its cv-qualifiers and ref-qualifier, without + the class of the function (N5046 [dcl.fct]). + +For example, with --c++26 --set_flag=reflection: + + #include + namespace N { + class C { + protected: + int prot; + public: + void f() const; + int b; + }; + } + constexpr auto unchecked = std::meta::access_context::unchecked(); + constexpr auto r = + std::meta::nonstatic_data_members_of(^^N::C, unchecked)[0]; + int N::C::*pm = &[:r:]; // 1) Previously an error. + static_assert(std::meta::members_of(^^N::C, unchecked)[1] == ^^N::C::f); + // 2) Previously ^^N::C::b. + static_assert(std::meta::source_location_of(^^int).line() == 0); + // 3) Previously an error. + static_assert(std::meta::parent_of(^^N) == ^^::); + // 4) Previously an error. + static_assert(std::meta::type_of(^^N::C::f) == ^^void() const); + // 5) Previously failed. diff --git a/src/symbol_tbl.c b/src/symbol_tbl.c index 46142fc5da..4a73694f42 100644 --- a/src/symbol_tbl.c +++ b/src/symbol_tbl.c @@ -269,12 +269,6 @@ STATIC_THREAD a_symbol_ptr error_class_template_symbol; /* Pointer to a shared error class template entry. */ -STATIC_THREAD a_symbol_ptr - unnamed_field_symbol; - /* Pointer to "the" unnamed field symbol, which exists - only for the sake of identifying a given field entry - as representing an unnamed field. */ - STATIC_THREAD sizeof_t size_of_trans_unit_for_scope; /* Allocated size of the trans_unit_for_scope table. */ @@ -8318,23 +8312,33 @@ Return TRUE if sym represents an unnamed namespace. } /* is_unnamed_namespace_symbol */ -a_symbol_ptr get_unnamed_field_symbol(void) +a_symbol_ptr make_unnamed_field_symbol(a_source_position *pos) /* -Return a pointer to "the" unnamed field symbol, which exists only for the -sake of identifying a given field entry as representing an unnamed field. +Create a symbol for an unnamed bit-field. Do not enter it into the symbol +table. */ { + a_symbol_ptr sym; + if (unnamed_field_symbol_header == NULL) { unnamed_field_symbol_header = alloc_symbol_header(); set_identifier_for_symbol_header(unnamed_field_symbol_header, "", 9, /*is_unnamed=*/TRUE); - unnamed_field_symbol = alloc_symbol((a_symbol_kind)sk_field, - unnamed_field_symbol_header, - &null_source_position); } /* if */ - return unnamed_field_symbol; -} /* get_unnamed_field_symbol */ + sym = alloc_symbol(sk_field, unnamed_field_symbol_header, pos); + set_decl_sequence_number(sym); + return sym; +} /* make_unnamed_field_symbol */ + + +a_boolean is_unnamed_field_symbol(a_symbol_ptr sym) +/* +Return TRUE if sym represents an unnamed bit-field. +*/ +{ + return (sym->header == unnamed_field_symbol_header); +} /* is_unnamed_field_symbol */ a_symbol_ptr make_unnamed_namespace_symbol(a_source_position *pos) @@ -8384,6 +8388,7 @@ into the symbol table. Each unnamed symbol is given a unique symbol header. sym = alloc_symbol(kind, make_unnamed_symbol_header(), pos); sym->decl_scope = scope_stack[decl_scope_level].number; + set_decl_sequence_number(sym); return sym; } /* make_unnamed_symbol */ @@ -8475,6 +8480,7 @@ parent object" for an anonymous union. Do not enter it in the symbol table. } /* if */ sym = alloc_symbol(kind, anonymous_parent_object_symbol_header, pos); sym->decl_scope = decl_scope; + set_decl_sequence_number(sym); db_exit(); return sym; } /* make_anonymous_parent_object_symbol */ @@ -8693,17 +8699,16 @@ for other entities as long as the namespace is never declared). clear_locator(&loc, &null_source_position); (void)find_symbol(name, (sizeof_t)strlen(name), &loc); /* Allocate the symbol. */ - *sym = alloc_symbol((a_symbol_kind)sk_namespace, loc.symbol_header, + *sym = alloc_symbol(sk_namespace, loc.symbol_header, &null_source_position); (*sym)->decl_scope = file_scope_number; - if (parent != NULL) { - (*sym)->parent.namespace_ptr = parent; - } /* if */ /* Create the namespace entry and bind it to the symbol. */ nsp = alloc_namespace(/*is_alias=*/FALSE); set_source_corresp(&nsp->source_corresp, *sym); - nsp->source_corresp.name_linkage = - (a_name_linkage_kind)nlk_cplusplus_external; + if (parent != NULL) { + set_namespace_membership(*sym, &nsp->source_corresp, parent); + } /* if */ + nsp->source_corresp.name_linkage = nlk_cplusplus_external; (*sym)->variant.namespace_info.ptr = nsp; /* Add the namespace to the namespaces list for the file scope. */ add_to_namespaces_list(nsp); @@ -20113,7 +20118,6 @@ are handled in symbol_tbl_init.) pch_saved_var_array_elem(unnamed_namespace_symbol_header), pch_saved_var_array_elem(anonymous_parent_object_symbol_header), pch_saved_var_array_elem(unnamed_field_symbol_header), - pch_saved_var_array_elem(unnamed_field_symbol), pch_saved_var_array_elem(global_namespace_list_entry), pch_saved_var_array_elem(symbol_for_namespace_std), pch_saved_var_array_elem(symbol_for_namespace_std_entered), @@ -20372,7 +20376,6 @@ of the front end. unnamed_namespace_symbol_header = NULL; anonymous_parent_object_symbol_header = NULL; unnamed_field_symbol_header = NULL; - unnamed_field_symbol = NULL; size_t_type = NULL; ptr_to_const_char_type = NULL; dummy_undefined_symbol = NULL; diff --git a/src/symbol_tbl.h b/src/symbol_tbl.h index 8b96e4a8f2..62bdf5b04c 100644 --- a/src/symbol_tbl.h +++ b/src/symbol_tbl.h @@ -5358,7 +5358,9 @@ extern a_symbol_ptr make_module_symbol(const a_string &primary_name, a_boolean is_interface, a_source_position *pos); -extern a_symbol_ptr get_unnamed_field_symbol(void); +extern a_symbol_ptr make_unnamed_field_symbol(a_source_position *pos); + +extern a_boolean is_unnamed_field_symbol(a_symbol_ptr sym); extern a_symbol_ptr make_anonymous_parent_object_symbol( a_symbol_kind kind, diff --git a/src/trans_corresp.c b/src/trans_corresp.c index 4246daba3d..456c90d674 100644 --- a/src/trans_corresp.c +++ b/src/trans_corresp.c @@ -2748,8 +2748,8 @@ is in fact valid. scp->access != corresp_scp->access || scp->name_linkage != corresp_scp->name_linkage)) { match = FALSE; - if (scp->assoc_info != NULL && - scp->assoc_info != (char*)get_unnamed_field_symbol()) { + if (symbol_for(field) != NULL && + !is_unnamed_field_symbol(symbol_for(field))) { /* A named field: */ process_bad_trans_unit_corresp(iek_field, field, corresp_field); } else { diff --git a/tests/expectations/edg_x86_32/reflections.log b/tests/expectations/edg_x86_32/reflections.log index aa2a434ab6..1ea0902b19 100644 --- a/tests/expectations/edg_x86_32/reflections.log +++ b/tests/expectations/edg_x86_32/reflections.log @@ -1 +1,6 @@ demo1.sft.cpp:rp:-DTEST_NUMBER=1:FAIL(COMPILE): +members_of_order.sft.cpp:fp:-DTEST_NUMBER=1:FAIL: +parent_of_namespace.sft.cpp:fp:-DTEST_NUMBER=1:FAIL: +source_location_of.sft.cpp:fp:-DTEST_NUMBER=1:FAIL: +splice_protected_access.sft.cpp:rp:-DTEST_NUMBER=1:FAIL(COMPILE): +type_of_member_function.sft.cpp:fp:-DTEST_NUMBER=1:FAIL: diff --git a/tests/expectations/edg_x86_32_cp/reflections.log b/tests/expectations/edg_x86_32_cp/reflections.log index aa2a434ab6..2af78a03b2 100644 --- a/tests/expectations/edg_x86_32_cp/reflections.log +++ b/tests/expectations/edg_x86_32_cp/reflections.log @@ -1 +1,7 @@ demo1.sft.cpp:rp:-DTEST_NUMBER=1:FAIL(COMPILE): +members_of_order.sft.cpp:fp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: +parent_of_namespace.sft.cpp:fp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: +source_location_of.sft.cpp:fp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: +splice_protected_access.sft.cpp:rp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: +splice_protected_access_neg.sft.cpp:fn:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: +type_of_member_function.sft.cpp:fp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: diff --git a/tests/expectations/edg_x86_64_cp/reflections.log b/tests/expectations/edg_x86_64_cp/reflections.log index aa2a434ab6..2af78a03b2 100644 --- a/tests/expectations/edg_x86_64_cp/reflections.log +++ b/tests/expectations/edg_x86_64_cp/reflections.log @@ -1 +1,7 @@ demo1.sft.cpp:rp:-DTEST_NUMBER=1:FAIL(COMPILE): +members_of_order.sft.cpp:fp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: +parent_of_namespace.sft.cpp:fp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: +source_location_of.sft.cpp:fp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: +splice_protected_access.sft.cpp:rp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: +splice_protected_access_neg.sft.cpp:fn:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: +type_of_member_function.sft.cpp:fp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET: diff --git a/tests/tests/reflections/.members_of_order.rto/default.1.1.txt b/tests/tests/reflections/.members_of_order.rto/default.1.1.txt new file mode 100644 index 0000000000..44c36760c6 --- /dev/null +++ b/tests/tests/reflections/.members_of_order.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++26 --set_flag=reflection Test_name.c diff --git a/tests/tests/reflections/.members_of_order.rto/edg_x86_32.1.1.txt b/tests/tests/reflections/.members_of_order.rto/edg_x86_32.1.1.txt new file mode 100644 index 0000000000..c53979935c --- /dev/null +++ b/tests/tests/reflections/.members_of_order.rto/edg_x86_32.1.1.txt @@ -0,0 +1,34 @@ +fe_only -DTEST_NUMBER=1 --c++26 --set_flag=reflection Test_name.c +"/usr/include/c++/15/type_traits", line 514: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __is_floating_point_helper<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/bits/cpp_type_traits.h", line 348: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __is_floating<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/ext/type_traits.h", line 224: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __promote<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/ext/type_traits.h", line 225: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + { typedef __gnu_cxx::__bfloat16_t __type; }; + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 135: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + constexpr __gnu_cxx::__bfloat16_t + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 136: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + abs(__gnu_cxx::__bfloat16_t __x) + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 137: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + { return __gnu_cxx::__bfloat16_t(__builtin_fabsf(__x)); } + ^ + +"/usr/include/c++/15/bits/ostream.h", line 286: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + operator<<(__gnu_cxx::__bfloat16_t __f) + ^ + +8 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/reflections/.parent_of_namespace.rto/default.1.1.txt b/tests/tests/reflections/.parent_of_namespace.rto/default.1.1.txt new file mode 100644 index 0000000000..44c36760c6 --- /dev/null +++ b/tests/tests/reflections/.parent_of_namespace.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++26 --set_flag=reflection Test_name.c diff --git a/tests/tests/reflections/.parent_of_namespace.rto/edg_x86_32.1.1.txt b/tests/tests/reflections/.parent_of_namespace.rto/edg_x86_32.1.1.txt new file mode 100644 index 0000000000..c53979935c --- /dev/null +++ b/tests/tests/reflections/.parent_of_namespace.rto/edg_x86_32.1.1.txt @@ -0,0 +1,34 @@ +fe_only -DTEST_NUMBER=1 --c++26 --set_flag=reflection Test_name.c +"/usr/include/c++/15/type_traits", line 514: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __is_floating_point_helper<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/bits/cpp_type_traits.h", line 348: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __is_floating<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/ext/type_traits.h", line 224: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __promote<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/ext/type_traits.h", line 225: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + { typedef __gnu_cxx::__bfloat16_t __type; }; + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 135: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + constexpr __gnu_cxx::__bfloat16_t + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 136: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + abs(__gnu_cxx::__bfloat16_t __x) + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 137: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + { return __gnu_cxx::__bfloat16_t(__builtin_fabsf(__x)); } + ^ + +"/usr/include/c++/15/bits/ostream.h", line 286: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + operator<<(__gnu_cxx::__bfloat16_t __f) + ^ + +8 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/reflections/.source_location_of.rto/default.1.1.txt b/tests/tests/reflections/.source_location_of.rto/default.1.1.txt new file mode 100644 index 0000000000..44c36760c6 --- /dev/null +++ b/tests/tests/reflections/.source_location_of.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++26 --set_flag=reflection Test_name.c diff --git a/tests/tests/reflections/.source_location_of.rto/edg_x86_32.1.1.txt b/tests/tests/reflections/.source_location_of.rto/edg_x86_32.1.1.txt new file mode 100644 index 0000000000..c53979935c --- /dev/null +++ b/tests/tests/reflections/.source_location_of.rto/edg_x86_32.1.1.txt @@ -0,0 +1,34 @@ +fe_only -DTEST_NUMBER=1 --c++26 --set_flag=reflection Test_name.c +"/usr/include/c++/15/type_traits", line 514: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __is_floating_point_helper<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/bits/cpp_type_traits.h", line 348: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __is_floating<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/ext/type_traits.h", line 224: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __promote<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/ext/type_traits.h", line 225: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + { typedef __gnu_cxx::__bfloat16_t __type; }; + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 135: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + constexpr __gnu_cxx::__bfloat16_t + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 136: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + abs(__gnu_cxx::__bfloat16_t __x) + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 137: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + { return __gnu_cxx::__bfloat16_t(__builtin_fabsf(__x)); } + ^ + +"/usr/include/c++/15/bits/ostream.h", line 286: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + operator<<(__gnu_cxx::__bfloat16_t __f) + ^ + +8 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/reflections/.splice_protected_access.rto/default.1.1.txt b/tests/tests/reflections/.splice_protected_access.rto/default.1.1.txt new file mode 100644 index 0000000000..30032fc283 --- /dev/null +++ b/tests/tests/reflections/.splice_protected_access.rto/default.1.1.txt @@ -0,0 +1 @@ +eccp -DTEST_NUMBER=1 --c++26 --set_flag=reflection Test_name.c diff --git a/tests/tests/reflections/.splice_protected_access.rto/default.1.2.txt b/tests/tests/reflections/.splice_protected_access.rto/default.1.2.txt new file mode 100644 index 0000000000..a8994540de --- /dev/null +++ b/tests/tests/reflections/.splice_protected_access.rto/default.1.2.txt @@ -0,0 +1,5 @@ +./a.out +1 2 3 4 5 +6 7 +8 9 10 +11 12 diff --git a/tests/tests/reflections/.splice_protected_access.rto/edg_x86_32.1.1.txt b/tests/tests/reflections/.splice_protected_access.rto/edg_x86_32.1.1.txt new file mode 100644 index 0000000000..52299eafc6 --- /dev/null +++ b/tests/tests/reflections/.splice_protected_access.rto/edg_x86_32.1.1.txt @@ -0,0 +1,34 @@ +eccp -DTEST_NUMBER=1 --c++26 --set_flag=reflection Test_name.c +"/usr/include/c++/15/type_traits", line 514: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __is_floating_point_helper<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/bits/cpp_type_traits.h", line 348: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __is_floating<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/ext/type_traits.h", line 224: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __promote<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/ext/type_traits.h", line 225: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + { typedef __gnu_cxx::__bfloat16_t __type; }; + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 135: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + constexpr __gnu_cxx::__bfloat16_t + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 136: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + abs(__gnu_cxx::__bfloat16_t __x) + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 137: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + { return __gnu_cxx::__bfloat16_t(__builtin_fabsf(__x)); } + ^ + +"/usr/include/c++/15/bits/ostream.h", line 286: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + operator<<(__gnu_cxx::__bfloat16_t __f) + ^ + +8 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/reflections/.splice_protected_access_neg.rto/default.1.1.txt b/tests/tests/reflections/.splice_protected_access_neg.rto/default.1.1.txt new file mode 100644 index 0000000000..8459e5388b --- /dev/null +++ b/tests/tests/reflections/.splice_protected_access_neg.rto/default.1.1.txt @@ -0,0 +1,18 @@ +fe_only -DTEST_NUMBER=1 --c++26 --set_flag=reflection Test_name.c +"Test_name.c", line 38: error: conversion to inaccessible base class "C" is not allowed + int a = e.[:^^C::pub:]; // error: inaccessible base + ^ + +"Test_name.c", line 39: error: conversion to inaccessible base class "C" is not allowed + int b = f.[:member_named(^^C, "prot"):]; // error: inaccessible base + ^ + +"Test_name.c", line 40: error: conversion to inaccessible base class "C" is not allowed + int c = (&f)->[:member_named(^^C, "pub"):]; // error: inaccessible base + ^ + +"Test_name.c", line 41: error: member "C::prot" (declared at line 29) is inaccessible + static_assert(^^C::prot != info{}); // error: inaccessible + ^ + +4 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/reflections/.splice_protected_access_neg.rto/edg_x86_32.1.1.txt b/tests/tests/reflections/.splice_protected_access_neg.rto/edg_x86_32.1.1.txt new file mode 100644 index 0000000000..32ef87c65f --- /dev/null +++ b/tests/tests/reflections/.splice_protected_access_neg.rto/edg_x86_32.1.1.txt @@ -0,0 +1,50 @@ +fe_only -DTEST_NUMBER=1 --c++26 --set_flag=reflection Test_name.c +"/usr/include/c++/15/type_traits", line 514: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __is_floating_point_helper<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/bits/cpp_type_traits.h", line 348: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __is_floating<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/ext/type_traits.h", line 224: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __promote<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/ext/type_traits.h", line 225: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + { typedef __gnu_cxx::__bfloat16_t __type; }; + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 135: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + constexpr __gnu_cxx::__bfloat16_t + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 136: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + abs(__gnu_cxx::__bfloat16_t __x) + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 137: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + { return __gnu_cxx::__bfloat16_t(__builtin_fabsf(__x)); } + ^ + +"/usr/include/c++/15/bits/ostream.h", line 286: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + operator<<(__gnu_cxx::__bfloat16_t __f) + ^ + +"Test_name.c", line 38: error: conversion to inaccessible base class "C" is not allowed + int a = e.[:^^C::pub:]; // error: inaccessible base + ^ + +"Test_name.c", line 39: error: conversion to inaccessible base class "C" is not allowed + int b = f.[:member_named(^^C, "prot"):]; // error: inaccessible base + ^ + +"Test_name.c", line 40: error: conversion to inaccessible base class "C" is not allowed + int c = (&f)->[:member_named(^^C, "pub"):]; // error: inaccessible base + ^ + +"Test_name.c", line 41: error: member "C::prot" (declared at line 29) is inaccessible + static_assert(^^C::prot != info{}); // error: inaccessible + ^ + +12 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/reflections/.type_of_member_function.rto/default.1.1.txt b/tests/tests/reflections/.type_of_member_function.rto/default.1.1.txt new file mode 100644 index 0000000000..44c36760c6 --- /dev/null +++ b/tests/tests/reflections/.type_of_member_function.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++26 --set_flag=reflection Test_name.c diff --git a/tests/tests/reflections/.type_of_member_function.rto/edg_x86_32.1.1.txt b/tests/tests/reflections/.type_of_member_function.rto/edg_x86_32.1.1.txt new file mode 100644 index 0000000000..c53979935c --- /dev/null +++ b/tests/tests/reflections/.type_of_member_function.rto/edg_x86_32.1.1.txt @@ -0,0 +1,34 @@ +fe_only -DTEST_NUMBER=1 --c++26 --set_flag=reflection Test_name.c +"/usr/include/c++/15/type_traits", line 514: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __is_floating_point_helper<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/bits/cpp_type_traits.h", line 348: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __is_floating<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/ext/type_traits.h", line 224: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + struct __promote<__gnu_cxx::__bfloat16_t> + ^ + +"/usr/include/c++/15/ext/type_traits.h", line 225: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + { typedef __gnu_cxx::__bfloat16_t __type; }; + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 135: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + constexpr __gnu_cxx::__bfloat16_t + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 136: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + abs(__gnu_cxx::__bfloat16_t __x) + ^ + +"/usr/include/c++/15/bits/std_abs.h", line 137: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + { return __gnu_cxx::__bfloat16_t(__builtin_fabsf(__x)); } + ^ + +"/usr/include/c++/15/bits/ostream.h", line 286: error: namespace "__gnu_cxx" has no member "__bfloat16_t" + operator<<(__gnu_cxx::__bfloat16_t __f) + ^ + +8 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/reflections/members_of_order.sft.cpp b/tests/tests/reflections/members_of_order.sft.cpp new file mode 100644 index 0000000000..d7c9485e78 --- /dev/null +++ b/tests/tests/reflections/members_of_order.sft.cpp @@ -0,0 +1,172 @@ +//type:fp +//use_system_includes: true +//edg_header_pack: meta +//require:DO_IL_LOWERING 1 + +/* +std::meta::members_of returns the members of a class in the order in which +they are declared, whatever their kinds (N5046 +[meta.reflection.member.queries]); implicitly-declared special members come +after the user-declared members. Members of a namespace are also returned in +declaration order. The order is that of the first declarations: a member +defined outside its class or namespace, or declared by a macro expansion, is +placed where it is declared. +*/ + +#include +#include +#include + +using namespace std::meta; + +// The names of the members of r in the order of members_of, except for +// implicitly-declared special members (how many of them are declared is not +// checked here). A member that follows an implicitly-declared one is marked. +consteval std::string names(info r, access_context ctx) { + std::string out; + bool after_implicit = false; + for (info m : members_of(r, ctx)) { + if (is_special_member_function(m) && !is_user_declared(m)) { + after_implicit = true; + continue; + } /* if */ + if (after_implicit) out += ""; + out += has_identifier(m) ? identifier_of(m) : ""; + out += ' '; + } /* for */ + return out; +} + +consteval int implicit_count(info r) { + int n = 0; + for (info m : members_of(r, access_context::unchecked())) { + n += is_special_member_function(m) && !is_user_declared(m); + } /* for */ + return n; +} + +consteval std::string names(info r) { + return names(r, access_context::unchecked()); +} + +struct S { + int a; + void f(); + int b; + static void g(); + using T = int; + int c; + struct N {}; + static int sv; + enum E { e }; + int : 3; + S() = default; + int d; +}; + +static_assert(names(^^S) == "a f b g T c N sv E S d "); +static_assert(implicit_count(^^S) != 0); + +// Members declared in the class and defined outside it are placed where they +// are declared, not where they are defined. +struct O { + void f(); + int x; + static int s; + struct N; + int y; + O(); + int z; +}; +void O::f() {} +int O::s; +struct O::N {}; +O::O() {} + +static_assert(names(^^O) == "f x s N y O z "); + +// Members declared by one macro expansion are in the order of the expansion, +// although they all have the position of the macro invocation. +#define MEMBERS(n) int m1##n; void g##n(); int m2##n; using T##n = int; +struct M { + int a; + MEMBERS(1) MEMBERS(2) + int b; +}; + +static_assert(names(^^M) == "a m11 g1 m21 T1 m12 g2 m22 T2 b "); + +// An unnamed bit-field, unnamed enum, or anonymous union is placed in +// declaration order, including inside a macro expansion. +#define MACRO_BF int m_a; int : 3; void m_f(); int : 2; int m_b; +struct B { + int a : 2; + void h(); + int : 3; + int b : 2; + using U = int; + int : 1; + enum { e_val }; + union { int u_mem; }; + int c : 2; + MACRO_BF +}; + +static_assert( + names(^^B) == + "a h b U c " + "m_a m_f m_b "); + +class P { + int p1; +public: + int q1; + void q2(); +private: + int p2; +public: + using q3 = int; +}; + +static_assert(names(^^P, access_context::current()) == "q1 q2 q3 "); + +// Members of an instantiation follow the declarations in the template, also +// when a member is defined outside the template. +template struct TS { + U x; void h(); U y; + static U z; + void k(); + U w; +}; +template void TS::k() {} + +static_assert(names(^^TS) == "x h y z k w "); +static_assert(implicit_count(^^TS) != 0); + +// Members of a class defined by define_aggregate. +struct Agg; +consteval { + define_aggregate(^^Agg, {data_member_spec(^^int, {.name = "m1"}), + data_member_spec(^^char, {.name = "m2"})}); +} + +static_assert(names(^^Agg) == "m1 m2 "); +static_assert(implicit_count(^^Agg) != 0); + +namespace ns { + void f(); + int v; + struct C; + namespace inner {} + using A = int; + namespace alias = inner; +} +void ns::f() {} +struct ns::C {}; +namespace ns { + void g(); + int w; +} +void ns::g() {} + +static_assert(names(^^ns) == "f v C inner A alias g w "); diff --git a/tests/tests/reflections/parent_of_namespace.sft.cpp b/tests/tests/reflections/parent_of_namespace.sft.cpp new file mode 100644 index 0000000000..5e11c9b0ca --- /dev/null +++ b/tests/tests/reflections/parent_of_namespace.sft.cpp @@ -0,0 +1,73 @@ +//type:fp +//use_system_includes: true +//edg_header_pack: meta +//require:DO_IL_LOWERING 1 + +/* +std::meta::parent_of accepts a reflection of a namespace or namespace alias +other than the global namespace (N5046 [meta.reflection.queries]); the result +is the enclosing namespace. has_parent is true for them and false for ::. +*/ + +#include + +using namespace std::meta; + +namespace outer { + namespace inner { struct S {}; } + inline namespace v1 { int x; } + namespace { int y; } + namespace alias2 = inner; +} +namespace outer::inner { void f(); } +namespace alias1 = outer::inner; + +static_assert(parent_of(^^outer::inner::S) == ^^outer::inner); +static_assert(parent_of(^^outer::inner::f) == ^^outer::inner); +static_assert(parent_of(^^outer::inner) == ^^outer); +static_assert(parent_of(^^outer) == ^^::); +static_assert(parent_of(^^outer::v1) == ^^outer); +static_assert(parent_of(^^outer::x) == ^^outer::v1); +static_assert(parent_of(parent_of(^^outer::y)) == ^^outer); +static_assert(parent_of(^^alias1) == ^^::); +static_assert(parent_of(^^outer::alias2) == ^^outer); +static_assert(is_namespace(parent_of(^^outer::inner))); +static_assert(identifier_of(parent_of(^^outer::inner)) == "outer"); +static_assert(has_parent(^^outer) && has_parent(^^alias1)); +static_assert(!has_parent(^^::)); + +// Walking out to the global namespace. +consteval int depth(info r) { + int n = 0; + for (; r != ^^::; r = parent_of(r)) ++n; + return n; +} +static_assert(depth(^^outer::inner::S) == 3); + +// Predeclared std::meta namespace. +static_assert(parent_of(^^std::meta) == ^^std); +static_assert(parent_of(^^std) == ^^::); + +// Function-local entities and structured bindings. +struct Pair { int a, b; }; +void local_fn() { + [[maybe_unused]] int lv = 0; + struct LocalS { int m; }; + using LocalAlias = int; + enum LocalE { le }; + Pair p{1, 2}; + [[maybe_unused]] auto [sb1, sb2] = p; + extern int block_ext_var; + int block_ext_fn(); + + static_assert(has_parent(^^lv)); + static_assert(parent_of(^^lv) == ^^local_fn); + static_assert(parent_of(^^LocalS) == ^^local_fn); + static_assert(parent_of(^^LocalS::m) == ^^LocalS); + static_assert(parent_of(^^LocalAlias) == ^^local_fn); + static_assert(parent_of(^^LocalE) == ^^local_fn); + static_assert(parent_of(^^le) == ^^LocalE); + static_assert(!has_parent(^^sb1)); + static_assert(parent_of(^^block_ext_var) == ^^::); + static_assert(parent_of(^^block_ext_fn) == ^^::); +} diff --git a/tests/tests/reflections/source_location_of.sft.cpp b/tests/tests/reflections/source_location_of.sft.cpp new file mode 100644 index 0000000000..286e8e3fe2 --- /dev/null +++ b/tests/tests/reflections/source_location_of.sft.cpp @@ -0,0 +1,62 @@ +//type:fp +//use_system_includes: true +//edg_header_pack: meta +//require:DO_IL_LOWERING 1 + +/* +std::meta::source_location_of is a constant expression for every reflection +(N5046 [meta.reflection.names] specifies it without a Throws clause, and +P2996R13 without a "Constant When" clause). +For a value, a type other than a class or enumeration type, the global +namespace, or a data member description, the result is source_location{}; +for an entity, it is the location of a declaration. +*/ + +#include +#include + +using namespace std::meta; + +consteval bool is_empty_location(std::source_location l) { + return l.line() == 0 && l.column() == 0 && l.file_name()[0] == '\0' && + l.function_name()[0] == '\0'; +} + +// No source location. +static_assert(is_empty_location(source_location_of(^^int))); +static_assert(is_empty_location(source_location_of(^^int*))); +static_assert(is_empty_location(source_location_of(^^const int&))); +static_assert(is_empty_location(source_location_of(reflect_constant(42)))); +static_assert(is_empty_location(source_location_of(^^::))); +static_assert(is_empty_location( + source_location_of(data_member_spec(^^int, {.name = "x"})))); +static_assert(is_empty_location(source_location_of(info{}))); + +// Entities: the line of the declaration. +struct S { int m; }; +enum E { e1 }; +using Alias = int; +void f(); +namespace N {} +constexpr int line_S = __LINE__ - 5; + +static_assert(source_location_of(^^S).line() == line_S); +static_assert(source_location_of(^^const S).line() == line_S); +static_assert(source_location_of(^^S::m).line() == line_S); +static_assert(source_location_of(^^E).line() == line_S + 1); +static_assert(source_location_of(^^e1).line() == line_S + 1); +static_assert(source_location_of(^^Alias).line() == line_S + 2); +static_assert(source_location_of(^^f).line() == line_S + 3); +static_assert(source_location_of(^^N).line() == line_S + 4); +static_assert(!is_empty_location(source_location_of(^^S))); + +// Direct base class specifiers and annotations. +struct Base {}; +struct [[=1]] Derived : + Base {}; +constexpr int line_Derived = __LINE__ - 2; +static_assert(source_location_of(annotations_of(^^Derived)[0]).line() == + line_Derived); +static_assert( + source_location_of(bases_of(^^Derived, access_context::current())[0]).line() + == line_Derived + 1); diff --git a/tests/tests/reflections/splice_protected_access.sft.cpp b/tests/tests/reflections/splice_protected_access.sft.cpp new file mode 100644 index 0000000000..4101aa23e5 --- /dev/null +++ b/tests/tests/reflections/splice_protected_access.sft.cpp @@ -0,0 +1,134 @@ +//type:rp +//use_system_includes: true +//edg_header_pack: meta +//require:DO_IL_LOWERING 1 + +/* +A class member designated by a splice-expression is accessible from any +point (N5046 [class.access.base]), and the additional access check for +protected members (N5046 [class.protected], CWG 3109) does not apply to it. +So a pointer to a protected member can be formed with &[:r:], and a protected +member can be named with obj.[:r:] or p->[:r:] where obj or *p is of the +designating class or of any class derived from it, outside of any member or +friend of the class. +The same holds when the splice is template-dependent. + +Output should be: +$ ./a.out +1 2 3 4 5 +6 7 +8 9 10 +11 12 +*/ + +#include +#include +#include + +using namespace std::meta; + +consteval info member_named(info cls, std::string_view name) { + for (info m : members_of(cls, access_context::unchecked())) { + if (has_identifier(m) && identifier_of(m) == name) return m; + } + return info{}; +} + +class C { +protected: + int prot = 1; + int get() const { return 2; } + void set(int v) noexcept { prot = v; } + int def_arg(int x = 1) const { return prot + x; } + int explicit_this(this const C &self) { return self.prot; } +private: + int priv = 6; +}; + +template +class TC { +protected: + int def_arg(T x = 1) const { return x; } +}; + +struct D : C {}; +struct TD : TC {}; + +// Template-dependent splices. +template constexpr auto member_pointer = &[:M:]; + +template int read(T *p) { + return p->[:M:]; +} + +template int call(T &obj) { + return obj.[:M:](); +} + +// The pattern of a CRTP base that reaches the non-public members of the +// class derived from it. +template struct Base { + int sum() { + Derived &d = static_cast(*this); + constexpr auto pm = &[:member_named(^^Derived, "value"):]; + constexpr auto pf = &[:member_named(^^Derived, "twice"):]; + return d.*pm + (d.*pf)(); + } +}; + +class Widget : public Base { + friend struct Base; // For the static_cast only. +protected: + int value = 4; +private: + int twice() const { return 2 * value; } +}; + +int main() { + constexpr info prot = member_named(^^C, "prot"); + constexpr info get = member_named(^^C, "get"); + constexpr info set = member_named(^^C, "set"); + constexpr info def_arg = member_named(^^C, "def_arg"); + constexpr info explicit_this = member_named(^^C, "explicit_this"); + constexpr info tc_def_arg = member_named(^^TC, "def_arg"); + constexpr info priv = member_named(^^C, "priv"); + C c; + D d; + TD td; + C *pc = &c; + using U1 = decltype(c.[:get:]()); + using U2 = decltype(c.[:def_arg:]()); + using U3 = decltype(c.[:explicit_this:]()); + using U4 = decltype(td.[:tc_def_arg:]()); + (void)sizeof(U1); + (void)sizeof(U2); + (void)sizeof(U3); + (void)sizeof(U4); + if (c.[:def_arg:]() != 2 || c.[:explicit_this:]() != 1 || + td.[:tc_def_arg:]() != 1) { + return 1; + } + + int C::*pm = &[:prot:]; + int (C::*pf)() const = &[:get:]; + void (C::*ps)(int) noexcept = &[:set:]; + int a = c.*pm; + int b = (c.*pf)(); + int e = c.[:get:]() + 1; + (c.*ps)(4); + int f = pc->[:prot:]; + d.[:set:](5); + int g = d.[:prot:]; + printf("%d %d %d %d %d\n", a, b, e, f, g); + + printf("%d %d\n", c.*member_pointer, + d.*member_pointer + 2); + + printf("%d %d %d\n", read(&d) + 3, call(d) + 7, + read(&c) + 4); + + Widget w; + printf("%d %d\n", w.sum() - 1, + w.*member_pointer + 8); + return 0; +} diff --git a/tests/tests/reflections/splice_protected_access_neg.sft.cpp b/tests/tests/reflections/splice_protected_access_neg.sft.cpp new file mode 100644 index 0000000000..6bd6ca9786 --- /dev/null +++ b/tests/tests/reflections/splice_protected_access_neg.sft.cpp @@ -0,0 +1,43 @@ +//type:fn +//use_system_includes: true +//edg_header_pack: meta +//require:DO_IL_LOWERING 1 + +/* +A member designated by a splice-expression is accessible from any point, but +a class member access whose right operand is such a splice is still +ill-formed if the left operand (considered as a pointer) cannot be implicitly +converted to a pointer to the designating class (N5046 [class.access.base]). +A reflect-expression naming a member is subject to the usual access checks. +*/ + +#include +#include + +using namespace std::meta; + +consteval info member_named(info cls, std::string_view name) { + for (info m : members_of(cls, access_context::unchecked())) { + if (has_identifier(m) && identifier_of(m) == name) return m; + } + return info{}; +} + +struct C { + int pub = 1; +protected: + int prot = 2; +}; + +struct E : private C {}; +struct F : protected C {}; + +int main() { + E e; + F f; + int a = e.[:^^C::pub:]; // error: inaccessible base + int b = f.[:member_named(^^C, "prot"):]; // error: inaccessible base + int c = (&f)->[:member_named(^^C, "pub"):]; // error: inaccessible base + static_assert(^^C::prot != info{}); // error: inaccessible + return a + b + c; +} diff --git a/tests/tests/reflections/type_of_member_function.sft.cpp b/tests/tests/reflections/type_of_member_function.sft.cpp new file mode 100644 index 0000000000..786156c9bf --- /dev/null +++ b/tests/tests/reflections/type_of_member_function.sft.cpp @@ -0,0 +1,70 @@ +//type:fp +//use_system_includes: true +//edg_header_pack: meta +//require:DO_IL_LOWERING 1 + +/* +std::meta::type_of of a nonstatic member function is a plain function type +(N5046 [dcl.fct]): the class is not part of it, but the cv-qualifiers, the +ref-qualifier, and the exception specification are. So it compares equal to +the reflection of that function type, and the spliced type matches partial +specializations for function types. +*/ + +#include + +using namespace std::meta; + +struct M { + int f(int, double); + void g() const noexcept; + void h() volatile &&; + static int s(int); + void d(int = 1); + void e(this M &, int); + virtual void v(); +}; + +static_assert(type_of(^^M::f) == ^^int(int, double)); +static_assert(type_of(^^M::g) == ^^void() const noexcept); +static_assert(type_of(^^M::h) == ^^void() volatile &&); +static_assert(type_of(^^M::s) == ^^int(int)); +static_assert(type_of(^^M::d) == ^^void(int)); +static_assert(type_of(^^M::e) == ^^void(M &, int)); +static_assert(type_of(^^M::v) == ^^void()); +static_assert(is_function_type(type_of(^^M::f))); + +template struct Kind { static constexpr int value = 0; }; +template struct Kind { + static constexpr int value = 1; +}; +template struct Kind { + static constexpr int value = 2; +}; +template struct Kind { + static constexpr int value = 3; +}; + +static_assert(Kind::value == 1); +static_assert(Kind::value == 2); +static_assert(Kind::value == 3); + +// The function type gives back the pointer-to-member type. +using F = [:type_of(^^M::f):]; +using G = [:type_of(^^M::g):]; +using PF = F M::*; +using PG = G M::*; +static_assert(dealias(^^PF) == ^^int (M::*)(int, double)); +static_assert(dealias(^^PG) == ^^void (M::*)() const noexcept); + +// The result does not depend on where type_of is evaluated. +consteval info type_of_g() { return type_of(^^M::g); } +constexpr info g_type = type_of_g(); +static_assert(g_type == type_of(^^M::g)); + +// A nonstatic member function declared with a function typedef type. +using FnAlias = int(int) const; +struct M2 { + FnAlias fa; +}; +static_assert(type_of(^^M2::fa) == ^^int(int) const);