From f9fbf93dc82525a0f54a2293b7ec92d65776bf19 Mon Sep 17 00:00:00 2001 From: waffl3x Date: Sun, 7 Jan 2024 00:00:10 +0000 Subject: c++: P0847R7 (deducing this) - prerequisite changes. [PR102609] Adds the xobj_flag member to lang_decl_fn and a corresponding member access macro and predicate to support the addition of explicit object member functions. Additionally, since explicit object member functions are also non-static member functions, we need to change uses of DECL_NONSTATIC_MEMBER_FUNCTION_P to clarify whether they intend to include or exclude them. PR c++/102609 gcc/cp/ChangeLog: * cp-tree.h (struct lang_decl_fn): New data member. (DECL_NONSTATIC_MEMBER_FUNCTION_P): Poison. (DECL_IOBJ_MEMBER_FUNCTION_P): Define. (DECL_FUNCTION_XOBJ_FLAG): Define. (DECL_XOBJ_MEMBER_FUNCTION_P): Define. (DECL_OBJECT_MEMBER_FUNCTION_P): Define. (DECL_FUNCTION_MEMBER_P): Don't use DECL_NONSTATIC_MEMBER_FUNCTION_P. (DECL_CONST_MEMFUNC_P): Likewise. (DECL_VOLATILE_MEMFUNC_P): Likewise. (DECL_NONSTATIC_MEMBER_P): Likewise. * module.cc (trees_out::lang_decl_bools): Handle xobj_flag. (trees_in::lang_decl_bools): Handle xobj_flag. * call.cc (build_this_conversion) (add_function_candidate) (add_template_candidate_real) (add_candidates) (maybe_warn_class_memaccess) (cand_parms_match) (joust) (do_warn_dangling_reference) * class.cc (finalize_literal_type_property) (finish_struct) (resolve_address_of_overloaded_function) * constexpr.cc (is_valid_constexpr_fn) (cxx_bind_parameters_in_call) * contracts.cc (build_contract_condition_function) * cp-objcp-common.cc (cp_decl_dwarf_attribute) * cxx-pretty-print.cc (cxx_pretty_printer::postfix_expression) (cxx_pretty_printer::declaration_specifiers) (cxx_pretty_printer::direct_declarator) * decl.cc (cp_finish_decl) (grok_special_member_properties) (start_preparsed_function) (record_key_method_defined) * decl2.cc (cp_handle_deprecated_or_unavailable) * init.cc (find_uninit_fields_r) (build_offset_ref) * lambda.cc (lambda_expr_this_capture) (maybe_generic_this_capture) (nonlambda_method_basetype) * mangle.cc (write_nested_name) * method.cc (early_check_defaulted_comparison) (skip_artificial_parms_for) (num_artificial_parms_for) * pt.cc (is_specialization_of_friend) (determine_specialization) (copy_default_args_to_explicit_spec) (check_explicit_specialization) (tsubst_contract_attribute) (check_non_deducible_conversions) (more_specialized_fn) (maybe_instantiate_noexcept) (register_parameter_specializations) (value_dependent_expression_p) * search.cc (shared_member_p) (lookup_member) (field_access_p) * semantics.cc (finish_omp_declare_simd_methods) * tree.cc (lvalue_kind) * typeck.cc (invalid_nonstatic_memfn_p): Don't use DECL_NONSTATIC_MEMBER_FUNCTION_P. libcc1/ChangeLog: * libcp1plugin.cc (plugin_pragma_push_user_expression): Don't use DECL_NONSTATIC_MEMBER_FUNCTION_P. Signed-off-by: Waffl3x Co-authored-by: Jason Merrill --- gcc/cp/call.cc | 25 +++++++++++++------------ gcc/cp/class.cc | 6 +++--- gcc/cp/constexpr.cc | 4 ++-- gcc/cp/contracts.cc | 6 +++--- gcc/cp/cp-objcp-common.cc | 4 ++-- gcc/cp/cp-tree.h | 37 ++++++++++++++++++++++++++++++------- gcc/cp/cxx-pretty-print.cc | 6 +++--- gcc/cp/decl.cc | 8 ++++---- gcc/cp/decl2.cc | 2 +- gcc/cp/init.cc | 4 ++-- gcc/cp/lambda.cc | 9 +++++---- gcc/cp/mangle.cc | 4 ++-- gcc/cp/method.cc | 8 ++++---- gcc/cp/module.cc | 6 ++++-- gcc/cp/pt.cc | 38 +++++++++++++++++++------------------- gcc/cp/search.cc | 5 +++-- gcc/cp/semantics.cc | 2 +- gcc/cp/tree.cc | 2 +- gcc/cp/typeck.cc | 2 +- libcc1/libcp1plugin.cc | 2 +- 20 files changed, 104 insertions(+), 76 deletions(-) diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 1c67572..be89ff2 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -2323,7 +2323,7 @@ build_this_conversion (tree fn, tree ctype, tree& parmtype, tree& argtype, tree& arg, int flags, tsubst_flags_t complain) { - gcc_assert (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) + gcc_assert (DECL_IOBJ_MEMBER_FUNCTION_P (fn) && !DECL_CONSTRUCTOR_P (fn)); /* The type of the implicit object parameter ('this') for @@ -2536,7 +2536,7 @@ add_function_candidate (struct z_candidate **candidates, { tree parmtype = TREE_VALUE (parmnode); if (i == 0 - && DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) + && DECL_IOBJ_MEMBER_FUNCTION_P (fn) && !DECL_CONSTRUCTOR_P (fn)) t = build_this_conversion (fn, ctype, parmtype, argtype, arg, flags, complain); @@ -3493,7 +3493,7 @@ add_template_candidate_real (struct z_candidate **candidates, tree tmpl, /* We don't do deduction on the in-charge parameter, the VTT parameter or 'this'. */ - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (tmpl)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (tmpl)) { if (first_arg_without_in_chrg != NULL_TREE) first_arg_without_in_chrg = NULL_TREE; @@ -3603,7 +3603,7 @@ add_template_candidate_real (struct z_candidate **candidates, tree tmpl, convs = alloc_conversions (nargs); if (shortcut_bad_convs - && DECL_NONSTATIC_MEMBER_FUNCTION_P (tmpl) + && DECL_IOBJ_MEMBER_FUNCTION_P (tmpl) && !DECL_CONSTRUCTOR_P (tmpl)) { /* Check the 'this' conversion before proceeding with deduction. @@ -6631,7 +6631,7 @@ add_candidates (tree fns, tree first_arg, const vec *args, tree fn_first_arg = NULL_TREE; const vec *fn_args = args; - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)) + if (DECL_OBJECT_MEMBER_FUNCTION_P (fn)) { /* Figure out where the object arg comes from. If this function is a non-static member and we didn't get an @@ -10798,7 +10798,8 @@ maybe_warn_class_memaccess (location_t loc, tree fndecl, type. If so, and if the class has no non-trivial bases or members, be more permissive. */ if (current_function_decl - && DECL_NONSTATIC_MEMBER_FUNCTION_P (current_function_decl) + && DECL_OBJECT_MEMBER_FUNCTION_P (current_function_decl) + /* ??? is_object_parameter? */ && is_this_parameter (tree_strip_nop_conversions (dest))) { tree ctx = DECL_CONTEXT (current_function_decl); @@ -12698,8 +12699,8 @@ cand_parms_match (z_candidate *c1, z_candidate *c2) tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (fn2)); if (DECL_FUNCTION_MEMBER_P (fn1) && DECL_FUNCTION_MEMBER_P (fn2) - && (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn1) - != DECL_NONSTATIC_MEMBER_FUNCTION_P (fn2))) + && (DECL_STATIC_FUNCTION_P (fn1) + != DECL_STATIC_FUNCTION_P (fn2))) { /* Ignore 'this' when comparing the parameters of a static member function with those of a non-static one. */ @@ -12884,7 +12885,7 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, print_z_candidate (input_location, N_("candidate 2:"), l); if (w->fn == l->fn - && DECL_NONSTATIC_MEMBER_FUNCTION_P (w->fn) + && DECL_IOBJ_MEMBER_FUNCTION_P (w->fn) && (type_memfn_quals (TREE_TYPE (w->fn)) & TYPE_QUAL_CONST) == 0) { @@ -14033,7 +14034,7 @@ do_warn_dangling_reference (tree expr, bool arg_p) because R refers to one of the int elements of V, not to a temporary object. Member operator* may return a reference but probably not to one of its arguments. */ - || (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl) + || (DECL_OBJECT_MEMBER_FUNCTION_P (fndecl) && DECL_OVERLOADED_OPERATOR_P (fndecl) && DECL_OVERLOADED_OPERATOR_IS (fndecl, INDIRECT_REF))) return NULL_TREE; @@ -14059,7 +14060,7 @@ do_warn_dangling_reference (tree expr, bool arg_p) tree arg = CALL_EXPR_ARG (expr, i); /* Check that this argument initializes a reference, except for the argument initializing the object of a member function. */ - if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl) + if (!DECL_IOBJ_MEMBER_FUNCTION_P (fndecl) && !TYPE_REF_P (TREE_TYPE (arg))) continue; STRIP_NOPS (arg); @@ -14079,7 +14080,7 @@ do_warn_dangling_reference (tree expr, bool arg_p) const S& s = S().self(); where 's' dangles. If we've gotten here, the object this function is invoked on is not a temporary. */ - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl)) + if (DECL_OBJECT_MEMBER_FUNCTION_P (fndecl)) break; } return NULL_TREE; diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index a45c0d6..5bfdabe 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -5968,7 +5968,7 @@ finalize_literal_type_property (tree t) for (fn = TYPE_FIELDS (t); fn; fn = DECL_CHAIN (fn)) if (TREE_CODE (fn) == FUNCTION_DECL && DECL_DECLARED_CONSTEXPR_P (fn) - && DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) + && DECL_IOBJ_MEMBER_FUNCTION_P (fn) && !DECL_CONSTRUCTOR_P (fn)) { DECL_DECLARED_CONSTEXPR_P (fn) = false; @@ -7932,7 +7932,7 @@ finish_struct (tree t, tree attributes) if (flag_openmp) for (tree decl = TYPE_FIELDS (t); decl; decl = DECL_CHAIN (decl)) if (TREE_CODE (decl) == FUNCTION_DECL - && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)) + && DECL_OBJECT_MEMBER_FUNCTION_P (decl)) if (tree attr = lookup_attribute ("omp declare variant base", DECL_ATTRIBUTES (decl))) omp_declare_variant_finalize (decl, attr); @@ -8727,7 +8727,7 @@ resolve_address_of_overloaded_function (tree target_type, /* Good, exactly one match. Now, convert it to the correct type. */ fn = TREE_PURPOSE (matches); - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) + if (DECL_OBJECT_MEMBER_FUNCTION_P (fn) && !(complain & tf_ptrmem_ok) && !flag_ms_extensions) { static int explained; diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 37500b7..6350fe154 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -291,7 +291,7 @@ is_valid_constexpr_fn (tree fun, bool complain) /* C++14 DR 1684 removed this restriction. */ if (cxx_dialect < cxx14 - && DECL_NONSTATIC_MEMBER_FUNCTION_P (fun) + && DECL_IOBJ_MEMBER_FUNCTION_P (fun) && !CLASSTYPE_LITERAL_P (DECL_CONTEXT (fun))) { ret = false; @@ -1886,7 +1886,7 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, non_constant_p, overflow_p); /* Check we aren't dereferencing a null pointer when calling a non-static member function, which is undefined behaviour. */ - if (i == 0 && DECL_NONSTATIC_MEMBER_FUNCTION_P (fun) + if (i == 0 && DECL_OBJECT_MEMBER_FUNCTION_P (fun) && integer_zerop (arg) /* But ignore calls from within compiler-generated code, to handle cases like lambda function pointer conversion operator thunks diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc index 6c23f9d..634e3cf 100644 --- a/gcc/cp/contracts.cc +++ b/gcc/cp/contracts.cc @@ -1398,7 +1398,7 @@ build_contract_condition_function (tree fndecl, bool pre) { if (TREE_TYPE (fndecl) == error_mark_node) return error_mark_node; - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl) + if (DECL_IOBJ_MEMBER_FUNCTION_P (fndecl) && !TYPE_METHOD_BASETYPE (TREE_TYPE (fndecl))) return error_mark_node; @@ -1421,7 +1421,7 @@ build_contract_condition_function (tree fndecl, bool pre) arg_type && arg_type != void_list_node; arg_type = TREE_CHAIN (arg_type)) { - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl) + if (DECL_IOBJ_MEMBER_FUNCTION_P (fndecl) && TYPE_ARG_TYPES (TREE_TYPE (fn)) == arg_type) { class_type = TREE_TYPE (TREE_VALUE (arg_type)); @@ -1451,7 +1451,7 @@ build_contract_condition_function (tree fndecl, bool pre) } TREE_TYPE (fn) = build_function_type (value_type, arg_types); - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (fndecl)) TREE_TYPE (fn) = build_method_type (class_type, TREE_TYPE (fn)); DECL_NAME (fn) = copy_node (DECL_NAME (fn)); diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc index 2bec8e6..f06edf0 100644 --- a/gcc/cp/cp-objcp-common.cc +++ b/gcc/cp/cp-objcp-common.cc @@ -349,7 +349,7 @@ cp_decl_dwarf_attribute (const_tree decl, int attr) case DW_AT_reference: if (TREE_CODE (decl) == FUNCTION_DECL - && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl) + && DECL_IOBJ_MEMBER_FUNCTION_P (decl) && FUNCTION_REF_QUALIFIED (TREE_TYPE (decl)) && !FUNCTION_RVALUE_QUALIFIED (TREE_TYPE (decl))) return 1; @@ -357,7 +357,7 @@ cp_decl_dwarf_attribute (const_tree decl, int attr) case DW_AT_rvalue_reference: if (TREE_CODE (decl) == FUNCTION_DECL - && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl) + && DECL_IOBJ_MEMBER_FUNCTION_P (decl) && FUNCTION_REF_QUALIFIED (TREE_TYPE (decl)) && FUNCTION_RVALUE_QUALIFIED (TREE_TYPE (decl))) return 1; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 86084b5..f0bddb7 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -2962,8 +2962,9 @@ struct GTY(()) lang_decl_fn { unsigned coroutine_p : 1; unsigned implicit_constexpr : 1; unsigned escalated_p : 1; + unsigned xobj_func : 1; - unsigned spare : 8; + unsigned spare : 7; /* 32-bits padding on 64-bit host. */ @@ -3361,33 +3362,55 @@ struct GTY(()) lang_decl { #define DECL_STATIC_FUNCTION_P(NODE) \ (LANG_DECL_FN_CHECK (NODE)->static_function) -/* Nonzero for FUNCTION_DECL means that this decl is a non-static +/* Nonzero for FUNCTION_DECL means that this decl is a non-static member + function. C++23 explicit object member functions are also considered + non-static, but most former uses of this macro meant implicit object member + function. Instead of this macro, use DECL_IOBJ_MEMBER_FUNCTION_P or + DECL_OBJECT_MEMBER_FUNCTION_P. */ +#define DECL_NONSTATIC_MEMBER_FUNCTION_P(NODE) did_you_mean_object_or_iobj + +/* Nonzero for FUNCTION_DECL means that this decl is an implicit object member function. */ -#define DECL_NONSTATIC_MEMBER_FUNCTION_P(NODE) \ +#define DECL_IOBJ_MEMBER_FUNCTION_P(NODE) \ (TREE_CODE (TREE_TYPE (NODE)) == METHOD_TYPE) +/* Simple member access, only valid for FUNCTION_DECL nodes. */ +#define DECL_FUNCTION_XOBJ_FLAG(NODE) \ + (LANG_DECL_FN_CHECK (NODE)->xobj_func) + +/* Nonzero if NODE is an xobj member function, + safely evaluates to false for all non FUNCTION_DECL nodes. */ +#define DECL_XOBJ_MEMBER_FUNCTION_P(NODE) \ + (TREE_CODE (STRIP_TEMPLATE (NODE)) == FUNCTION_DECL \ + && DECL_FUNCTION_XOBJ_FLAG (NODE) == 1) + +/* Nonzero if NODE is a member function with an object argument, + in other words, a non-static member function. */ +#define DECL_OBJECT_MEMBER_FUNCTION_P(NODE) \ + (DECL_IOBJ_MEMBER_FUNCTION_P (NODE) || DECL_XOBJ_MEMBER_FUNCTION_P (NODE)) + /* Nonzero for FUNCTION_DECL means that this decl is a member function (static or non-static). */ #define DECL_FUNCTION_MEMBER_P(NODE) \ - (DECL_NONSTATIC_MEMBER_FUNCTION_P (NODE) || DECL_STATIC_FUNCTION_P (NODE)) + (DECL_OBJECT_MEMBER_FUNCTION_P (NODE) || DECL_STATIC_FUNCTION_P (NODE)) \ /* Nonzero for FUNCTION_DECL means that this member function has `this' as const X *const. */ #define DECL_CONST_MEMFUNC_P(NODE) \ - (DECL_NONSTATIC_MEMBER_FUNCTION_P (NODE) \ + (DECL_IOBJ_MEMBER_FUNCTION_P (NODE) \ && CP_TYPE_CONST_P (TREE_TYPE (TREE_VALUE \ (TYPE_ARG_TYPES (TREE_TYPE (NODE)))))) /* Nonzero for FUNCTION_DECL means that this member function has `this' as volatile X *const. */ #define DECL_VOLATILE_MEMFUNC_P(NODE) \ - (DECL_NONSTATIC_MEMBER_FUNCTION_P (NODE) \ + (DECL_IOBJ_MEMBER_FUNCTION_P (NODE) \ && CP_TYPE_VOLATILE_P (TREE_TYPE (TREE_VALUE \ (TYPE_ARG_TYPES (TREE_TYPE (NODE)))))) /* Nonzero for a DECL means that this member is a non-static member. */ #define DECL_NONSTATIC_MEMBER_P(NODE) \ - (DECL_NONSTATIC_MEMBER_FUNCTION_P (NODE) \ + (DECL_OBJECT_MEMBER_FUNCTION_P (NODE) \ || TREE_CODE (NODE) == FIELD_DECL) /* Nonzero for a FIELD_DECL means that this member object type diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc index c7c4ae5..c6d8cc8 100644 --- a/gcc/cp/cxx-pretty-print.cc +++ b/gcc/cp/cxx-pretty-print.cc @@ -553,7 +553,7 @@ cxx_pretty_printer::postfix_expression (tree t) instantiation time. */ if (TREE_CODE (fun) != FUNCTION_DECL) ; - else if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fun)) + else if (DECL_OBJECT_MEMBER_FUNCTION_P (fun)) { tree object = (code == AGGR_INIT_EXPR ? (AGGR_INIT_VIA_CTOR_P (t) @@ -1342,7 +1342,7 @@ cxx_pretty_printer::declaration_specifiers (tree t) do not have a type-specifier in their return types. */ if (DECL_CONSTRUCTOR_P (t) || DECL_CONV_FN_P (t)) function_specifier (t); - else if (DECL_NONSTATIC_MEMBER_FUNCTION_P (t)) + else if (DECL_IOBJ_MEMBER_FUNCTION_P (t)) declaration_specifiers (TREE_TYPE (TREE_TYPE (t))); else c_pretty_printer::declaration_specifiers (t); @@ -1700,7 +1700,7 @@ cxx_pretty_printer::direct_declarator (tree t) expression (t); pp_cxx_parameter_declaration_clause (this, t); - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (t)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (t)) { padding = pp_before; pp_cxx_cv_qualifier_seq (this, pp_cxx_implicit_parameter_type (t)); diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 80d0327..398ec6b 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -8655,7 +8655,7 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, && TREE_CODE (decl) == FUNCTION_DECL /* #pragma omp declare variant on methods handled in finish_struct instead. */ - && (!DECL_NONSTATIC_MEMBER_FUNCTION_P (decl) + && (!DECL_OBJECT_MEMBER_FUNCTION_P (decl) || COMPLETE_TYPE_P (DECL_CONTEXT (decl)))) if (tree attr = lookup_attribute ("omp declare variant base", DECL_ATTRIBUTES (decl))) @@ -15645,7 +15645,7 @@ grok_special_member_properties (tree decl) tree class_type; if (TREE_CODE (decl) == USING_DECL - || !DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)) + || !DECL_OBJECT_MEMBER_FUNCTION_P (decl)) return; class_type = DECL_CONTEXT (decl); @@ -17835,7 +17835,7 @@ start_preparsed_function (tree decl1, tree attrs, int flags) /* Start the statement-tree, start the tree now. */ DECL_SAVED_TREE (decl1) = push_stmt_list (); - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl1)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (decl1)) { /* We know that this was set up by `grokclassfn'. We do not wait until `store_parm_decls', since evil parse errors may @@ -18304,7 +18304,7 @@ outer_curly_brace_block (tree fndecl) static void record_key_method_defined (tree fndecl) { - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl) + if (DECL_OBJECT_MEMBER_FUNCTION_P (fndecl) && DECL_VIRTUAL_P (fndecl) && !processing_template_decl) { diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index 5ceed56..fdc52a0 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -5676,7 +5676,7 @@ cp_handle_deprecated_or_unavailable (tree decl, tsubst_flags_t complain) if (cxx_dialect >= cxx11 && DECL_P (decl) && DECL_ARTIFICIAL (decl) - && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl) + && DECL_IOBJ_MEMBER_FUNCTION_P (decl) && copy_fn_p (decl)) { /* Don't warn if the flag was disabled around the class definition diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc index 0958471..bbcd187 100644 --- a/gcc/cp/init.cc +++ b/gcc/cp/init.cc @@ -866,7 +866,7 @@ find_uninit_fields_r (tree *tp, int *walk_subtrees, void *data) else if (code == CALL_EXPR) { tree fn = get_callee_fndecl (init); - if (fn && DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)) + if (fn && DECL_IOBJ_MEMBER_FUNCTION_P (fn)) { tree op = CALL_EXPR_ARG (init, 0); if (TREE_CODE (op) == ADDR_EXPR) @@ -2477,7 +2477,7 @@ build_offset_ref (tree type, tree member, bool address_p, -- in a mem-initializer for a constructor for that class or for a class derived from that class (_class.base.init_). */ - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (member)) + if (DECL_OBJECT_MEMBER_FUNCTION_P (member)) { /* Build a representation of the qualified name suitable for use as the operand to "&" -- even though the "&" is diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc index f995522..04f159fe8 100644 --- a/gcc/cp/lambda.cc +++ b/gcc/cp/lambda.cc @@ -834,8 +834,9 @@ lambda_expr_this_capture (tree lambda, int add_capture_p) if (!LAMBDA_FUNCTION_P (containing_function)) { - /* We found a non-lambda function. */ - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (containing_function)) + /* We found a non-lambda function. + There is no this pointer in xobj member functions. */ + if (DECL_IOBJ_MEMBER_FUNCTION_P (containing_function)) /* First parameter is 'this'. */ init = DECL_ARGUMENTS (containing_function); break; @@ -969,7 +970,7 @@ maybe_generic_this_capture (tree object, tree fns) for (lkp_iterator iter (fns); iter; ++iter) if (((!id_expr && TREE_CODE (*iter) != USING_DECL) || TREE_CODE (*iter) == TEMPLATE_DECL) - && DECL_NONSTATIC_MEMBER_FUNCTION_P (*iter)) + && DECL_IOBJ_MEMBER_FUNCTION_P (*iter)) { /* Found a non-static member. Capture this. */ lambda_expr_this_capture (lam, /*maybe*/-1); @@ -1012,7 +1013,7 @@ nonlambda_method_basetype (void) tree fn = TYPE_CONTEXT (type); if (!fn || TREE_CODE (fn) != FUNCTION_DECL - || !DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)) + || !DECL_IOBJ_MEMBER_FUNCTION_P (fn)) /* No enclosing non-lambda method. */ return NULL_TREE; if (!LAMBDA_FUNCTION_P (fn)) diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc index 0546860..7d8f443 100644 --- a/gcc/cp/mangle.cc +++ b/gcc/cp/mangle.cc @@ -1231,9 +1231,9 @@ write_nested_name (const tree decl) write_char ('N'); - /* Write CV-qualifiers, if this is a member function. */ + /* Write CV-qualifiers, if this is an iobj member function. */ if (TREE_CODE (decl) == FUNCTION_DECL - && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)) + && DECL_IOBJ_MEMBER_FUNCTION_P (decl)) { if (DECL_VOLATILE_MEMFUNC_P (decl)) write_char ('V'); diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc index fcf6cfa..aa5a044 100644 --- a/gcc/cp/method.cc +++ b/gcc/cp/method.cc @@ -1187,7 +1187,7 @@ early_check_defaulted_comparison (tree fn) ok = false; } - bool mem = DECL_NONSTATIC_MEMBER_FUNCTION_P (fn); + bool mem = DECL_IOBJ_MEMBER_FUNCTION_P (fn); if (mem && type_memfn_quals (TREE_TYPE (fn)) != TYPE_QUAL_CONST) { error_at (loc, "defaulted %qD must be %", fn); @@ -1230,7 +1230,7 @@ early_check_defaulted_comparison (tree fn) if (saw_bad || (saw_byval && saw_byref)) { - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (fn)) error_at (loc, "defaulted member %qD must have parameter type " "%", fn, ctx); else if (saw_bad) @@ -3606,7 +3606,7 @@ lazily_declare_fn (special_function_kind sfk, tree type) tree skip_artificial_parms_for (const_tree fn, tree list) { - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (fn)) list = TREE_CHAIN (list); else return list; @@ -3626,7 +3626,7 @@ num_artificial_parms_for (const_tree fn) { int count = 0; - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (fn)) count++; else return 0; diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 9bb6d26..aa75e28 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -5683,8 +5683,9 @@ trees_out::lang_decl_bools (tree t) WB (lang->u.fn.has_dependent_explicit_spec_p); WB (lang->u.fn.immediate_fn_p); WB (lang->u.fn.maybe_deleted); - WB (lang->u.fn.escalated_p); /* We do not stream lang->u.fn.implicit_constexpr. */ + WB (lang->u.fn.escalated_p); + WB (lang->u.fn.xobj_func); goto lds_min; case lds_decomp: /* lang_decl_decomp. */ @@ -5753,8 +5754,9 @@ trees_in::lang_decl_bools (tree t) RB (lang->u.fn.has_dependent_explicit_spec_p); RB (lang->u.fn.immediate_fn_p); RB (lang->u.fn.maybe_deleted); - RB (lang->u.fn.escalated_p); /* We do not stream lang->u.fn.implicit_constexpr. */ + RB (lang->u.fn.escalated_p); + RB (lang->u.fn.xobj_func); goto lds_min; case lds_decomp: /* lang_decl_decomp. */ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 7237bd3..da4b7fa 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -1446,9 +1446,9 @@ is_specialization_of_friend (tree decl, tree friend_decl) `this' parameter. */ friend_args_type = TYPE_ARG_TYPES (friend_type); decl_args_type = TYPE_ARG_TYPES (decl_type); - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (friend_decl)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (friend_decl)) friend_args_type = TREE_CHAIN (friend_args_type); - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (decl)) decl_args_type = TREE_CHAIN (decl_args_type); return compparms (decl_args_type, friend_args_type); @@ -2236,7 +2236,7 @@ determine_specialization (tree template_id, that the const qualification is the same. Since get_bindings does not try to merge the "this" parameter, we must do the comparison explicitly. */ - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (fn)) { if (!same_type_p (TREE_VALUE (fn_arg_types), TREE_VALUE (decl_arg_types))) @@ -2359,14 +2359,14 @@ determine_specialization (tree template_id, /* Adjust the type of DECL in case FN is a static member. */ decl_arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl)); if (DECL_STATIC_FUNCTION_P (fn) - && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)) + && DECL_IOBJ_MEMBER_FUNCTION_P (decl)) decl_arg_types = TREE_CHAIN (decl_arg_types); if (!compparms (TYPE_ARG_TYPES (TREE_TYPE (fn)), decl_arg_types)) continue; - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) + if (DECL_IOBJ_MEMBER_FUNCTION_P (fn) && (type_memfn_rqual (TREE_TYPE (decl)) != type_memfn_rqual (TREE_TYPE (fn)))) continue; @@ -2552,7 +2552,7 @@ copy_default_args_to_explicit_spec (tree decl) old_type = TREE_TYPE (decl); spec_types = TYPE_ARG_TYPES (old_type); - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (decl)) { /* Remove the this pointer, but remember the object's type for CV quals. */ @@ -3137,7 +3137,7 @@ check_explicit_specialization (tree declarator, make DECL a static member function as well. */ if (DECL_FUNCTION_TEMPLATE_P (tmpl) && DECL_STATIC_FUNCTION_P (tmpl) - && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)) + && DECL_IOBJ_MEMBER_FUNCTION_P (decl)) revert_static_member_fn (decl); /* If this is a specialization of a member template of a @@ -11814,7 +11814,7 @@ tsubst_contract_attribute (tree decl, tree t, tree args, /* For member functions, make this available for semantic analysis. */ tree save_ccp = current_class_ptr; tree save_ccr = current_class_ref; - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (decl)) { tree arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl)); tree this_type = TREE_TYPE (TREE_VALUE (arg_types)); @@ -22130,7 +22130,7 @@ check_non_deducible_conversions (tree parms, const tree *args, unsigned nargs, { /* Non-constructor methods need to leave a conversion for 'this', which isn't included in nargs here. */ - unsigned offset = (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) + unsigned offset = (DECL_IOBJ_MEMBER_FUNCTION_P (fn) && !DECL_CONSTRUCTOR_P (fn)); for (unsigned ia = 0; @@ -25357,16 +25357,16 @@ more_specialized_fn (tree pat1, tree pat2, int len) I think think the old G++ behavior of just skipping the object parameter when comparing to a static member function was better, so let's stick with that for now. This is CWG2834. --jason 2023-12 */ - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl1)) /* FIXME or explicit */ + if (DECL_OBJECT_MEMBER_FUNCTION_P (decl1)) { len--; /* LEN is the number of significant arguments for DECL1 */ args1 = TREE_CHAIN (args1); } - else if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl2)) /* FIXME or explicit */ + else if (DECL_OBJECT_MEMBER_FUNCTION_P (decl2)) args2 = TREE_CHAIN (args2); } - else if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl1) /* FIXME implicit only */ - && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl2)) + else if (DECL_IOBJ_MEMBER_FUNCTION_P (decl1) + && DECL_IOBJ_MEMBER_FUNCTION_P (decl2)) { /* Note DR2445 also (IMO wrongly) removed the "only one" above, which would break e.g. cpp1y/lambda-generic-variadic5.C. */ @@ -25374,12 +25374,12 @@ more_specialized_fn (tree pat1, tree pat2, int len) args1 = TREE_CHAIN (args1); args2 = TREE_CHAIN (args2); } - else if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl1) /* FIXME implicit only */ - || DECL_NONSTATIC_MEMBER_FUNCTION_P (decl2)) + else if (DECL_IOBJ_MEMBER_FUNCTION_P (decl1) + || DECL_IOBJ_MEMBER_FUNCTION_P (decl2)) { /* The other is a non-member or explicit object member function; rewrite the implicit object parameter to a reference. */ - tree ns = DECL_NONSTATIC_MEMBER_FUNCTION_P (decl2) ? decl2 : decl1; + tree ns = DECL_IOBJ_MEMBER_FUNCTION_P (decl2) ? decl2 : decl1; tree &nsargs = ns == decl2 ? args2 : args1; tree obtype = TREE_TYPE (TREE_VALUE (nsargs)); @@ -26712,7 +26712,7 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) push_deferring_access_checks (dk_no_deferred); input_location = DECL_SOURCE_LOCATION (fn); - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) + if (DECL_IOBJ_MEMBER_FUNCTION_P (fn) && !DECL_LOCAL_DECL_P (fn)) { /* If needed, set current_class_ptr for the benefit of @@ -26775,7 +26775,7 @@ register_parameter_specializations (tree pattern, tree inst) { tree tmpl_parm = DECL_ARGUMENTS (pattern); tree spec_parm = DECL_ARGUMENTS (inst); - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (inst)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (inst)) { register_local_specialization (spec_parm, tmpl_parm); spec_parm = skip_artificial_parms_for (inst, spec_parm); @@ -28118,7 +28118,7 @@ value_dependent_expression_p (tree expression) cause the call to be considered value-dependent. We also look through it in potential_constant_expression. */ if (i == 0 && fn && DECL_DECLARED_CONSTEXPR_P (fn) - && DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) + && DECL_IOBJ_MEMBER_FUNCTION_P (fn) && TREE_CODE (op) == ADDR_EXPR) op = TREE_OPERAND (op, 0); if (value_dependent_expression_p (op)) diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc index 9432200..f7be4a1 100644 --- a/gcc/cp/search.cc +++ b/gcc/cp/search.cc @@ -1008,7 +1008,7 @@ shared_member_p (tree t) /* Conservatively assume a dependent using-declaration might resolve to a non-static member. */ return false; - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)) + if (DECL_OBJECT_MEMBER_FUNCTION_P (decl)) return false; } return true; @@ -1264,7 +1264,7 @@ lookup_member (tree xbasetype, tree name, int protect, bool want_type, decl = strip_using_decl (decl); /* A dependent USING_DECL will be checked after tsubsting. */ if (TREE_CODE (decl) != USING_DECL - && !DECL_NONSTATIC_MEMBER_FUNCTION_P (decl) + && !DECL_IOBJ_MEMBER_FUNCTION_P (decl) && !perform_or_defer_access_check (basetype_path, decl, decl, complain, afi)) return error_mark_node; @@ -1737,6 +1737,7 @@ field_access_p (tree component_ref, tree field_decl, tree field_type) return false; tree ptr = STRIP_NOPS (TREE_OPERAND (indirect_ref, 0)); + /* ??? is_object_parameter? */ if (!is_this_parameter (ptr)) return false; diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 2162c57..8ff6962 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -6727,7 +6727,7 @@ finish_omp_declare_simd_methods (tree t) for (tree x = TYPE_FIELDS (t); x; x = DECL_CHAIN (x)) { if (TREE_CODE (x) == USING_DECL - || !DECL_NONSTATIC_MEMBER_FUNCTION_P (x)) + || !DECL_IOBJ_MEMBER_FUNCTION_P (x)) continue; tree ods = lookup_attribute ("omp declare simd", DECL_ATTRIBUTES (x)); if (!ods || !TREE_VALUE (ods)) diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 5dc495e..f2e0f28 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -298,7 +298,7 @@ lvalue_kind (const_tree ref) case FUNCTION_DECL: /* All functions (except non-static-member functions) are lvalues. */ - return (DECL_NONSTATIC_MEMBER_FUNCTION_P (ref) + return (DECL_IOBJ_MEMBER_FUNCTION_P (ref) ? clk_none : clk_ordinary); case BASELINK: diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index cfcaf12..d101cf0 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -2352,7 +2352,7 @@ invalid_nonstatic_memfn_p (location_t loc, tree expr, tsubst_flags_t complain) if (is_overloaded_fn (expr) && !really_overloaded_fn (expr)) expr = get_first_fn (expr); if (TREE_TYPE (expr) - && DECL_NONSTATIC_MEMBER_FUNCTION_P (expr)) + && DECL_IOBJ_MEMBER_FUNCTION_P (expr)) { if (complain & tf_error) { diff --git a/libcc1/libcp1plugin.cc b/libcc1/libcp1plugin.cc index 5b73e36..7f4e4c9 100644 --- a/libcc1/libcp1plugin.cc +++ b/libcc1/libcp1plugin.cc @@ -468,7 +468,7 @@ plugin_pragma_push_user_expression (cpp_reader *) } } - if (unchanged_cfun || DECL_NONSTATIC_MEMBER_FUNCTION_P (changed_func_decl)) + if (unchanged_cfun || DECL_OBJECT_MEMBER_FUNCTION_P (changed_func_decl)) { /* Check whether the oracle supplies us with a "this", and if so, arrange for data members and this itself to be -- cgit v1.1 From fbc980d85149409ce62c22f48d3693113803929e Mon Sep 17 00:00:00 2001 From: waffl3x Date: Sun, 7 Jan 2024 00:01:48 +0000 Subject: c++: P0847R7 (deducing this) - initial functionality. [PR102609] This implements the initial functionality for P0847R7. CWG2789 is implemented, but instead of "same type" for the object parameters we take correspondence into account instead. Without this alteration, the behavior here would be slightly different than the behavior with constrained member function templates, which I believe would be undesirable. There are a few outstanding issues related to xobj member functions overloading iobj member functions that are introduced by using declarations. Unfortunately, fixing this will be a little more involved and will have to be pushed back until later. Most diagnostics have been split out into another patch to improve its clarity and allow all the strictly functional changes to be more distinct. Explicit object lambdas and CWG2586 are addressed in a follow up patch. PR c++/102609 gcc/cp/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - initial functionality. * class.cc (xobj_iobj_parameters_correspond): New function, checks for corresponding object parameters between xobj and iobj member functions. (add_method): Handle object parameters of xobj member functions, use xobj_iobj_parameters_correspond. * call.cc (build_over_call): Refactor, handle xobj member functions. (cand_parms_match): Handle object parameters of xobj and iobj member functions, use xobj_iobj_parameters_correspond. * cp-tree.h (enum cp_decl_spec): Add ds_this, add comments. * decl.cc (grokfndecl): Add xobj_func_p parameter. For xobj member functions, Set xobj_flag, don't set static_function flag. (grokdeclarator): Handle xobj member functions, tell grokfndecl. (grok_op_properties): Don't error for xobj operators. * parser.cc (cp_parser_decl_specifier_seq): Handle this specifier. (cp_parser_parameter_declaration): Set default argument to "this_identifier" for xobj parameters. (set_and_check_decl_spec_loc): Add "this", add comments. * tree.cc (build_min_non_dep_op_overload): Handle xobj operators. * typeck.cc (cp_build_addr_expr_1): Handle address-of xobj member functions. gcc/testsuite/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - initial functionality. * g++.dg/cpp23/explicit-obj-basic1.C: New test. * g++.dg/cpp23/explicit-obj-basic2.C: New test. * g++.dg/cpp23/explicit-obj-basic3.C: New test. * g++.dg/cpp23/explicit-obj-basic4.C: New test. * g++.dg/cpp23/explicit-obj-basic5.C: New test. * g++.dg/cpp23/explicit-obj-by-value1.C: New test. * g++.dg/cpp23/explicit-obj-by-value2.C: New test. * g++.dg/cpp23/explicit-obj-by-value3.C: New test. * g++.dg/cpp23/explicit-obj-by-value4.C: New test. * g++.dg/cpp23/explicit-obj-constraints.C: New test. * g++.dg/cpp23/explicit-obj-constraints2.C: New test. * g++.dg/cpp23/explicit-obj-ops-mem-arrow.C: New test. * g++.dg/cpp23/explicit-obj-ops-mem-assignment.C: New test. * g++.dg/cpp23/explicit-obj-ops-mem-call.C: New test. * g++.dg/cpp23/explicit-obj-ops-mem-subscript.C: New test. * g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C: New test. * g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C: New test. * g++.dg/cpp23/explicit-obj-ops-non-mem.h: New test. * g++.dg/cpp23/explicit-obj-ops-requires-mem.C: New test. * g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C: New test. * g++.dg/cpp23/explicit-obj-redecl.C: New test. * g++.dg/cpp23/explicit-obj-redecl2.C: New test. * g++.dg/cpp23/explicit-obj-redecl3.C: New test. * g++.dg/cpp23/explicit-obj-redecl4.C: New test. Signed-off-by: Waffl3x --- gcc/cp/call.cc | 184 +++++--- gcc/cp/class.cc | 244 +++++++++- gcc/cp/cp-tree.h | 7 +- gcc/cp/decl.cc | 44 +- gcc/cp/parser.cc | 22 +- gcc/cp/tree.cc | 10 +- gcc/cp/typeck.cc | 9 + gcc/testsuite/g++.dg/cpp23/explicit-obj-basic1.C | 114 +++++ gcc/testsuite/g++.dg/cpp23/explicit-obj-basic2.C | 28 ++ gcc/testsuite/g++.dg/cpp23/explicit-obj-basic3.C | 496 +++++++++++++++++++++ gcc/testsuite/g++.dg/cpp23/explicit-obj-basic4.C | 113 +++++ gcc/testsuite/g++.dg/cpp23/explicit-obj-basic5.C | 33 ++ .../g++.dg/cpp23/explicit-obj-by-value1.C | 48 ++ .../g++.dg/cpp23/explicit-obj-by-value2.C | 58 +++ .../g++.dg/cpp23/explicit-obj-by-value3.C | 41 ++ .../g++.dg/cpp23/explicit-obj-by-value4.C | 20 + .../g++.dg/cpp23/explicit-obj-constraints.C | 418 +++++++++++++++++ .../g++.dg/cpp23/explicit-obj-constraints2.C | 462 +++++++++++++++++++ .../g++.dg/cpp23/explicit-obj-ops-mem-arrow.C | 28 ++ .../g++.dg/cpp23/explicit-obj-ops-mem-assignment.C | 27 ++ .../g++.dg/cpp23/explicit-obj-ops-mem-call.C | 40 ++ .../g++.dg/cpp23/explicit-obj-ops-mem-subscript.C | 40 ++ .../g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C | 58 +++ .../cpp23/explicit-obj-ops-non-mem-non-dep.C | 57 +++ .../g++.dg/cpp23/explicit-obj-ops-non-mem.h | 210 +++++++++ .../g++.dg/cpp23/explicit-obj-ops-requires-mem.C | 171 +++++++ .../cpp23/explicit-obj-ops-requires-non-mem.C | 237 ++++++++++ gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl.C | 246 ++++++++++ gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl2.C | 161 +++++++ gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl3.C | 206 +++++++++ gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl4.C | 97 ++++ 31 files changed, 3832 insertions(+), 97 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-basic1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-basic2.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-basic3.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-basic4.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-basic5.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value2.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value3.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value4.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-constraints.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-constraints2.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-arrow.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-assignment.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-call.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-subscript.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem.h create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-mem.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl2.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl3.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl4.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index be89ff2..dca8e50 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -9870,14 +9870,9 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) const vec *args = cand->args; tree first_arg = cand->first_arg; conversion **convs = cand->convs; - conversion *conv; tree parm = TYPE_ARG_TYPES (TREE_TYPE (fn)); int parmlen; tree val; - int i = 0; - int j = 0; - unsigned int arg_index = 0; - int is_method = 0; int nargs; tree *argarray; bool already_used = false; @@ -10074,45 +10069,46 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (immediate_invocation_p (STRIP_TEMPLATE (fn))) in_consteval_if_p = true; + int argarray_size = 0; + unsigned int arg_index = 0; + int conv_index = 0; + int param_index = 0; + + auto consume_object_arg = [&arg_index, &first_arg, args]() + { + if (!first_arg) + return (*args)[arg_index++]; + tree object_arg = first_arg; + first_arg = NULL_TREE; + return object_arg; + }; + /* The implicit parameters to a constructor are not considered by overload resolution, and must be of the proper type. */ if (DECL_CONSTRUCTOR_P (fn)) { - tree object_arg; - if (first_arg != NULL_TREE) - { - object_arg = first_arg; - first_arg = NULL_TREE; - } - else - { - object_arg = (*args)[arg_index]; - ++arg_index; - } - argarray[j++] = build_this (object_arg); + tree object_arg = consume_object_arg (); + argarray[argarray_size++] = build_this (object_arg); parm = TREE_CHAIN (parm); /* We should never try to call the abstract constructor. */ gcc_assert (!DECL_HAS_IN_CHARGE_PARM_P (fn)); if (DECL_HAS_VTT_PARM_P (fn)) { - argarray[j++] = (*args)[arg_index]; + argarray[argarray_size++] = (*args)[arg_index]; ++arg_index; parm = TREE_CHAIN (parm); } } /* Bypass access control for 'this' parameter. */ - else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE) + else if (DECL_IOBJ_MEMBER_FUNCTION_P (fn)) { - tree arg = build_this (first_arg != NULL_TREE - ? first_arg - : (*args)[arg_index]); + tree arg = build_this (consume_object_arg ()); tree argtype = TREE_TYPE (arg); if (arg == error_mark_node) return error_mark_node; - - if (convs[i]->bad_p) + if (convs[conv_index++]->bad_p) { if (complain & tf_error) { @@ -10187,25 +10183,57 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) tree converted_arg = build_base_path (PLUS_EXPR, arg, base_binfo, 1, complain); - argarray[j++] = converted_arg; + argarray[argarray_size++] = converted_arg; parm = TREE_CHAIN (parm); - if (first_arg != NULL_TREE) - first_arg = NULL_TREE; + } + + auto handle_arg = [fn, flags, complain](tree type, + tree arg, + int const param_index, + conversion *conv, + bool const conversion_warning) + { + /* Set user_conv_p on the argument conversions, so rvalue/base handling + knows not to allow any more UDCs. This needs to happen after we + process cand->warnings. */ + if (flags & LOOKUP_NO_CONVERSION) + conv->user_conv_p = true; + + tsubst_flags_t const arg_complain + = conversion_warning ? complain : complain & ~tf_warning; + + if (arg_complain & tf_warning) + maybe_warn_pessimizing_move (arg, type, /*return_p=*/false); + + tree val = convert_like_with_context (conv, arg, fn, + param_index, arg_complain); + val = convert_for_arg_passing (type, val, arg_complain); + return val; + }; + + if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)) + { + gcc_assert (cand->num_convs > 0); + static constexpr bool conversion_warning = true; + tree object_arg = consume_object_arg (); + val = handle_arg (TREE_VALUE (parm), + object_arg, + param_index++, + convs[conv_index++], + conversion_warning); + + if (val == error_mark_node) + return error_mark_node; else - ++arg_index; - ++i; - is_method = 1; + argarray[argarray_size++] = val; + parm = TREE_CHAIN (parm); } gcc_assert (first_arg == NULL_TREE); for (; arg_index < vec_safe_length (args) && parm; - parm = TREE_CHAIN (parm), ++arg_index, ++i) + parm = TREE_CHAIN (parm), ++arg_index, ++param_index, ++conv_index) { - tree type = TREE_VALUE (parm); - tree arg = (*args)[arg_index]; - bool conversion_warning = true; - - conv = convs[i]; + tree current_arg = (*args)[arg_index]; /* If the argument is NULL and used to (implicitly) instantiate a template function (and bind one of the template arguments to @@ -10227,47 +10255,35 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) func(NULL); } */ - if (null_node_p (arg) - && DECL_TEMPLATE_INFO (fn) - && cand->template_decl - && !cand->explicit_targs) - conversion_warning = false; - - /* Set user_conv_p on the argument conversions, so rvalue/base handling - knows not to allow any more UDCs. This needs to happen after we - process cand->warnings. */ - if (flags & LOOKUP_NO_CONVERSION) - conv->user_conv_p = true; + bool const conversion_warning = !(null_node_p (current_arg) + && DECL_TEMPLATE_INFO (fn) + && cand->template_decl + && !cand->explicit_targs); - tsubst_flags_t arg_complain = complain; - if (!conversion_warning) - arg_complain &= ~tf_warning; - - if (arg_complain & tf_warning) - maybe_warn_pessimizing_move (arg, type, /*return_p*/false); - - val = convert_like_with_context (conv, arg, fn, i - is_method, - arg_complain); - val = convert_for_arg_passing (type, val, arg_complain); + val = handle_arg (TREE_VALUE (parm), + current_arg, + param_index, + convs[conv_index], + conversion_warning); if (val == error_mark_node) - return error_mark_node; + return error_mark_node; else - argarray[j++] = val; + argarray[argarray_size++] = val; } /* Default arguments */ - for (; parm && parm != void_list_node; parm = TREE_CHAIN (parm), i++) + for (; parm && parm != void_list_node; parm = TREE_CHAIN (parm), param_index++) { if (TREE_VALUE (parm) == error_mark_node) return error_mark_node; val = convert_default_arg (TREE_VALUE (parm), TREE_PURPOSE (parm), - fn, i - is_method, + fn, param_index, complain); if (val == error_mark_node) - return error_mark_node; - argarray[j++] = val; + return error_mark_node; + argarray[argarray_size++] = val; } /* Ellipsis */ @@ -10304,11 +10320,11 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) a = convert_arg_to_ellipsis (a, complain); if (a == error_mark_node) return error_mark_node; - argarray[j++] = a; + argarray[argarray_size++] = a; } - gcc_assert (j <= nargs); - nargs = j; + gcc_assert (argarray_size <= nargs); + nargs = argarray_size; icip.reset (); /* Avoid performing argument transformation if warnings are disabled. @@ -10324,7 +10340,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) { tree *fargs = (!nargs ? argarray : (tree *) alloca (nargs * sizeof (tree))); - for (j = 0; j < nargs; j++) + for (int j = 0; j < nargs; j++) { /* For -Wformat undo the implicit passing by hidden reference done by convert_arg_to_ellipsis. */ @@ -12697,15 +12713,37 @@ cand_parms_match (z_candidate *c1, z_candidate *c2) } tree parms1 = TYPE_ARG_TYPES (TREE_TYPE (fn1)); tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (fn2)); - if (DECL_FUNCTION_MEMBER_P (fn1) - && DECL_FUNCTION_MEMBER_P (fn2) - && (DECL_STATIC_FUNCTION_P (fn1) - != DECL_STATIC_FUNCTION_P (fn2))) + auto skip_parms = [](tree fn, tree parms){ + if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)) + return TREE_CHAIN (parms); + else + return skip_artificial_parms_for (fn, parms); + }; + if (!(DECL_FUNCTION_MEMBER_P (fn1) + && DECL_FUNCTION_MEMBER_P (fn2))) + /* Early escape. */; + else if ((DECL_STATIC_FUNCTION_P (fn1) + != DECL_STATIC_FUNCTION_P (fn2))) { /* Ignore 'this' when comparing the parameters of a static member function with those of a non-static one. */ - parms1 = skip_artificial_parms_for (fn1, parms1); - parms2 = skip_artificial_parms_for (fn2, parms2); + parms1 = skip_parms (fn1, parms1); + parms2 = skip_parms (fn2, parms2); + } + else if ((DECL_XOBJ_MEMBER_FUNCTION_P (fn1) + || DECL_XOBJ_MEMBER_FUNCTION_P (fn2)) + && (DECL_IOBJ_MEMBER_FUNCTION_P (fn1) + || DECL_IOBJ_MEMBER_FUNCTION_P (fn2))) + { + bool xobj_iobj_parameters_correspond (tree, tree); + /* CWG2789 is not adequate, it should specify corresponding object + parameters, not same typed object parameters. */ + if (!xobj_iobj_parameters_correspond (fn1, fn2)) + return false; + /* We just compared the object parameters, if they don't correspond + we already return false. */ + parms1 = skip_parms (fn1, parms1); + parms2 = skip_parms (fn2, parms2); } return compparms (parms1, parms2); } diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index 5bfdabe..6f924d5 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -1019,6 +1019,217 @@ modify_vtable_entry (tree t, } +/* Check if the object parameters of an xobj and iobj member function + correspond. This function assumes that the iobj parameter has been correctly + adjusted when the function is introduced by a using declaration per + [over.match.funcs.general.4]. */ + +bool +xobj_iobj_parameters_correspond (tree fn1, tree fn2) +{ + gcc_assert (DECL_IOBJ_MEMBER_FUNCTION_P (fn1) + || DECL_IOBJ_MEMBER_FUNCTION_P (fn2)); + gcc_assert (DECL_XOBJ_MEMBER_FUNCTION_P (fn1) + || DECL_XOBJ_MEMBER_FUNCTION_P (fn2)); + gcc_assert (fn1 != fn2); + + tree xobj_fn = DECL_XOBJ_MEMBER_FUNCTION_P (fn1) ? fn1 : fn2; + /* A reference, pointer, or something else. */ + tree xobj_param = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (xobj_fn))); + + tree iobj_fn = DECL_IOBJ_MEMBER_FUNCTION_P (fn1) ? fn1 : fn2; + tree iobj_fn_type = TREE_TYPE (iobj_fn); + /* Will work for a pointer or reference param type. So this will continue + to work even if we change how the object parameter of an iobj member + function is represented. */ + tree iobj_param_type + = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (iobj_fn_type))); + + /* If the iobj member function was introduced with a using declaration, the + type of its object parameter is considered to be that of the class it was + introduced into. + + [over.match.funcs.general.4] + For non-conversion functions that are implicit object member + functions nominated by a using-declaration in a derived class, the + function is considered to be a member of the derived class for the purpose + of defining the type of the implicit object parameter. + + Unfortunately, because of this rule, we can't just compare the xobj member + function's DECL_CONTEXT to its object parameter. + + struct S; + + struct B { + int f(this S&) { return 5; } + }; + + struct S : B { + using B::f; + int f() { return 10; } + }; + + The using declaration does not change the object parameter of B::f as it + is an xobj member function. However, its object parameter still + corresponds to S::f as it was declared with an object parameter of type + S const&. The DECL_CONTEXT of B::f is B, so if we compare the type of the + object parameter to that, it will not match. If we naively assume a + different type from the DECL_CONTEXT for an xobj parameter means that the + object parameters do not correspond, then the object parameters in the + above example will be considered non-corresponding. + + As a result of this, B::f would incorrectly not be discarded, causing an + ambiguity when f is called on an object of type S. + + This also impacts member functions with constraints as in the following + example. + + template + struct S; + + template + struct B { + int f(this S<>&) requires true { return 5; } + }; + + template + struct S : B<> { + using B<>::f; + int f() { return 10; } + }; + + Once again, if we compare the DECL_CONTEXT of B<>::f to it's xobj + parameter, it would not match. If the object parameters do not + correspond, constraints are not taken into account, so in this example we + would (probably) get an ambiguous lookup instead of correctly picking + B<>::f. + + Because of this caveat, we must actually compare the type of the iobj + parameter to the type of the xobj parameter, shortcuts will have these + edge cases. + + Aside from the more complex reasons above, this logic also implicitly + handles xobj parameters of pointer type, we don't have to explicitly + check for that case. */ + + /* FIXME: + + template + struct S; + + template + struct B { + int f(this S&) requires true { return 5; } + }; + + template + struct S : B { + using B::f; + int f() { return 10; } + }; + + This case is broken, the incomplete type seems to screw with things. + I'm not sure how to fix that so I'm just noting the issue here, I have a + feeling it's trivial to do if you know how. */ + + if (TYPE_MAIN_VARIANT (iobj_param_type) + != TYPE_MAIN_VARIANT (non_reference (xobj_param))) + return false; + /* We don't get to bail yet even if we have a by-value xobj parameter, + a by-value xobj parameter can correspond to an iobj parameter provided the + iobj member function is not declared with a reference qualifier. + + From this point on, we know we are dealing with an xobj parameter that has + an object parameter of the same type as the class it was declared in. + We still don't know if we have a reference or by-value parameter yet + though. */ + + cp_ref_qualifier const iobj_ref_qual = type_memfn_rqual (iobj_fn_type); + /* We only care about cv qualifiers when determining correspondence. */ + static constexpr cp_cv_quals cv_bits = TYPE_QUAL_VOLATILE + | TYPE_QUAL_CONST; + cp_cv_quals const iobj_cv_quals = type_memfn_quals (iobj_fn_type) & cv_bits; + /* We need to ignore the ref qualifier of the xobj parameter if the iobj + member function lacks a ref qualifier. + + [basic.scope.scope.3] + Two non-static member functions have corresponding object parameters if: + -- exactly one is an implicit object member function with no ref-qualifier + and the types of their object parameters ([dcl.fct]), after removing + top-level references, are the same, or + -- their object parameters have the same type. + + The cv qualifiers of a by-value parameter are supposed to be discarded, so + we ignore them. + + [dcl.fct.5] + After producing the list of parameter types, any top-level cv-qualifiers + modifying a parameter type are deleted when forming the function type. + + However, they still need to be taken into account when our xobj parameter + is a reference that is being ignored (according to [basic.scope.scope.3] + quoted above), but when we are actually dealing with a by-value xobj + parameter we can proceed following this table. + | iobj | xobj | equal | + | none | none | X | + | none | c | X | + | none | v | X | + | none | cv | X | + | c | none | O | + | c | c | O | + | c | v | O | + | c | cv | O | + | v | none | O | + | v | c | O | + | v | v | O | + | v | cv | O | + | cv | none | O | + | cv | c | O | + | cv | v | O | + | cv | cv | O | + + Additionally, if the iobj member function is ref qualified, we aren't + ignoring the ref qualifier of the iobj parameter, so we can't be dealing + with correspondence in that case either. + + So to recap, if we have a by-value xobj parameter, we know for sure that + we aren't dealing with corresponding object parameters if the iobj member + function has any cv-ref qualifiers. The only case where we might still be + dealing with corresponding object parameters is when the iobj member + function lacks any cv-ref qualification. */ + if (!TYPE_REF_P (xobj_param)) + { + if (iobj_ref_qual || iobj_cv_quals) + return false; + } + else + { + /* We are dealing with an xobj parameter that is a reference now, but due + to [basic.scope.scope.3] we need to ignore its ref qual. */ + cp_ref_qualifier const xobj_ref_qual = [&](){ + if (!TYPE_REF_P (xobj_param) || !iobj_ref_qual) + return REF_QUAL_NONE; + return TYPE_REF_IS_RVALUE (xobj_param) ? REF_QUAL_RVALUE + : REF_QUAL_LVALUE; + }(); /* IILE. */ + + /* Even if we are ignoring the reference qualifier, the xobj parameter + was still a reference so we still take the cv qualifiers into + account. */ + cp_cv_quals const xobj_cv_quals + = cp_type_quals (TREE_TYPE (xobj_param)) & cv_bits; + + /* Finally, if the qualifications don't match exactly, the object + parameters don't correspond. */ + if (iobj_ref_qual != xobj_ref_qual + || iobj_cv_quals != xobj_cv_quals) + return false; + } + /* If we got past everything else, the object parameters of fn1 and fn2 + definitely correspond. */ + return true; +} + /* Add method METHOD to class TYPE. If VIA_USING indicates whether METHOD is being injected via a using_decl. Returns true if the method could be added to the method vec. */ @@ -1079,8 +1290,8 @@ add_method (tree type, tree method, bool via_using) /* Compare the quals on the 'this' parm. Don't compare the whole types, as used functions are treated as coming from the using class in overload resolution. */ - if (! DECL_STATIC_FUNCTION_P (fn) - && ! DECL_STATIC_FUNCTION_P (method) + if (DECL_IOBJ_MEMBER_FUNCTION_P (fn) + && DECL_IOBJ_MEMBER_FUNCTION_P (method) /* Either both or neither need to be ref-qualified for differing quals to allow overloading. */ && (FUNCTION_REF_QUALIFIED (fn_type) @@ -1089,6 +1300,35 @@ add_method (tree type, tree method, bool via_using) || type_memfn_rqual (fn_type) != type_memfn_rqual (method_type))) continue; + /* Handle special correspondence rules for xobj vs xobj and xobj vs iobj + member function declarations. + We don't worry about static member functions here. */ + if ((!DECL_XOBJ_MEMBER_FUNCTION_P (fn) + && !DECL_XOBJ_MEMBER_FUNCTION_P (method)) + || DECL_STATIC_FUNCTION_P (fn) || DECL_STATIC_FUNCTION_P (method)) + /* Early escape. */; + else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn) + && DECL_XOBJ_MEMBER_FUNCTION_P (method)) + { + auto get_object_param = [](tree fn){ + return TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fn))); + }; + /* We skip the object parameter below, check it here instead of + making changes to that code. */ + tree fn_param = get_object_param (fn); + tree method_param = get_object_param (method); + if (!same_type_p (fn_param, method_param)) + continue; + } + else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn) + || DECL_XOBJ_MEMBER_FUNCTION_P (method)) + { + if (!xobj_iobj_parameters_correspond (fn, method)) + continue; + } + else + gcc_unreachable (); + tree real_fn = fn; tree real_method = method; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index f0bddb7..ce57716 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6341,11 +6341,13 @@ enum cp_storage_class { /* An individual decl-specifier. This is used to index the array of locations for the declspecs in struct cp_decl_specifier_seq - below. */ + below. + A subset of these enums also corresponds to elements of + cp_parser_set_decl_spec_type:decl_spec_names in parser.cc. */ enum cp_decl_spec { ds_first, - ds_signed = ds_first, + ds_signed = ds_first, /* Index of first element of decl_spec_names. */ ds_unsigned, ds_short, ds_long, @@ -6362,6 +6364,7 @@ enum cp_decl_spec { ds_complex, ds_constinit, ds_consteval, + ds_this, /* Index of last element of decl_spec_names. */ ds_thread, ds_type_spec, ds_redefined_builtin_type_spec, diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 398ec6b..16ccdb3 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -10516,6 +10516,7 @@ grokfndecl (tree ctype, int publicp, int inlinep, bool deletedp, + bool xobj_func_p, special_function_kind sfk, bool funcdef_flag, bool late_return_type_p, @@ -10525,7 +10526,6 @@ grokfndecl (tree ctype, location_t location) { tree decl; - int staticp = ctype && TREE_CODE (type) == FUNCTION_TYPE; tree t; if (location == UNKNOWN_LOCATION) @@ -10723,12 +10723,9 @@ grokfndecl (tree ctype, (IDENTIFIER_POINTER (declarator)))))) SET_DECL_LANGUAGE (decl, lang_c); - /* Should probably propagate const out from type to decl I bet (mrs). */ - if (staticp) - { - DECL_STATIC_FUNCTION_P (decl) = 1; - DECL_CONTEXT (decl) = ctype; - } + DECL_STATIC_FUNCTION_P (decl) + = !xobj_func_p && ctype && TREE_CODE (type) == FUNCTION_TYPE; + DECL_FUNCTION_XOBJ_FLAG (decl) = xobj_func_p; if (deletedp) DECL_DELETED_FN (decl) = 1; @@ -13198,6 +13195,8 @@ grokdeclarator (const cp_declarator *declarator, if (attrlist) diagnose_misapplied_contracts (*attrlist); + /* Skip over build_memfn_type when a FUNCTION_DECL is an xobj memfn. */ + bool is_xobj_member_function = false; /* Determine the type of the entity declared by recurring on the declarator. */ for (; declarator; declarator = declarator->declarator) @@ -13313,6 +13312,22 @@ grokdeclarator (const cp_declarator *declarator, if (raises == error_mark_node) raises = NULL_TREE; + auto find_xobj_parm = [](tree parm_list) + { + /* There is no need to iterate over the list, + only the first parm can be a valid xobj parm. */ + if (!parm_list || TREE_PURPOSE (parm_list) != this_identifier) + return false; + /* If we make it here, we are looking at an xobj parm. + + Non-null 'purpose' usually means the parm has a default + argument, we don't want to violate this assumption. */ + TREE_PURPOSE (parm_list) = NULL_TREE; + return true; + }; + + is_xobj_member_function + = find_xobj_parm (declarator->u.function.parameters); if (reqs) error_at (location_of (reqs), "requires-clause on return type"); reqs = declarator->u.function.requires_clause; @@ -14386,6 +14401,8 @@ grokdeclarator (const cp_declarator *declarator, } if (ctype && TREE_CODE (type) == FUNCTION_TYPE && staticp < 2 + /* Don't convert xobj member functions to METHOD_TYPE. */ + && !is_xobj_member_function && !(unqualified_id && identifier_p (unqualified_id) && IDENTIFIER_NEWDEL_OP_P (unqualified_id))) @@ -14607,7 +14624,8 @@ grokdeclarator (const cp_declarator *declarator, friendp ? -1 : 0, friendp, publicp, inlinep | (2 * constexpr_p) | (4 * concept_p) | (8 * consteval_p), - initialized == SD_DELETED, sfk, + initialized == SD_DELETED, + is_xobj_member_function, sfk, funcdef_flag, late_return_type_p, template_count, in_namespace, attrlist, id_loc); @@ -14942,8 +14960,8 @@ grokdeclarator (const cp_declarator *declarator, inlinep | (2 * constexpr_p) | (4 * concept_p) | (8 * consteval_p), initialized == SD_DELETED, - sfk, - funcdef_flag, + is_xobj_member_function, sfk, + funcdef_flag, late_return_type_p, template_count, in_namespace, attrlist, id_loc); @@ -15744,7 +15762,7 @@ bool grok_op_properties (tree decl, bool complain) { tree argtypes = TYPE_ARG_TYPES (TREE_TYPE (decl)); - bool methodp = TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE; + bool const methodp = DECL_IOBJ_MEMBER_FUNCTION_P (decl); tree name = DECL_NAME (decl); location_t loc = DECL_SOURCE_LOCATION (decl); @@ -15837,7 +15855,7 @@ grok_op_properties (tree decl, bool complain) /* An operator function must either be a non-static member function or have at least one parameter of a class, a reference to a class, an enumeration, or a reference to an enumeration. 13.4.0.6 */ - if (! methodp || DECL_STATIC_FUNCTION_P (decl)) + if (!DECL_OBJECT_MEMBER_FUNCTION_P (decl)) { if (operator_code == TYPE_EXPR || operator_code == COMPONENT_REF @@ -15926,7 +15944,7 @@ grok_op_properties (tree decl, bool complain) } ++arity; } - + /* FIXME: We need tests for these errors with xobj member functions. */ /* Verify correct number of arguments. */ switch (op_flags) { diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 706ed21..046c48d 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -16260,6 +16260,16 @@ cp_parser_decl_specifier_seq (cp_parser* parser, decl_specs->locations[ds_attribute] = token->location; continue; } + /* Special case for "this" specifier, indicating a parm is an xobj parm. + The "this" specifier must be the first specifier in the declaration, + after any attributes. */ + if (token->keyword == RID_THIS) + { + cp_lexer_consume_token (parser->lexer); + set_and_check_decl_spec_loc (decl_specs, ds_this, token); + continue; + } + /* Assume we will find a decl-specifier keyword. */ found_decl_spec = true; /* If the next token is an appropriate keyword, we can simply @@ -25721,6 +25731,13 @@ cp_parser_parameter_declaration (cp_parser *parser, if (default_argument) STRIP_ANY_LOCATION_WRAPPER (default_argument); + if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_this)) + { + /* Xobj parameters can not have default arguments, thus + we can reuse the default argument field to flag the param as such. */ + default_argument = this_identifier; + } + /* Generate a location for the parameter, ranging from the start of the initial token to the end of the final token (using input_location for the latter, set up by cp_lexer_set_source_position_from_token when @@ -34091,6 +34108,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs, } else { + /* These correspond to cp-tree.h:cp_decl_spec, + changes here should also be reflected there. */ static const char *const decl_spec_names[] = { "signed", "unsigned", @@ -34108,7 +34127,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs, "constexpr", "__complex", "constinit", - "consteval" + "consteval", + "this" }; gcc_rich_location richloc (location); richloc.add_fixit_remove (); diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index f2e0f28..77f57e0 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -3677,7 +3677,7 @@ build_min_non_dep_op_overload (enum tree_code op, nargs = call_expr_nargs (non_dep); expected_nargs = cp_tree_code_length (op); - if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE + if (DECL_OBJECT_MEMBER_FUNCTION_P (overload) /* For ARRAY_REF, operator[] is either a non-static member or newly static member, never out of class and for the static member case if user uses single index the operator[] needs to have a single @@ -3695,7 +3695,7 @@ build_min_non_dep_op_overload (enum tree_code op, releasing_vec args; va_start (p, overload); - if (TREE_CODE (TREE_TYPE (overload)) == FUNCTION_TYPE) + if (!DECL_OBJECT_MEMBER_FUNCTION_P (overload)) { fn = overload; if (op == ARRAY_REF) @@ -3706,7 +3706,7 @@ build_min_non_dep_op_overload (enum tree_code op, vec_safe_push (args, arg); } } - else if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE) + else { tree object = va_arg (p, tree); tree binfo = TYPE_BINFO (TREE_TYPE (object)); @@ -3719,8 +3719,6 @@ build_min_non_dep_op_overload (enum tree_code op, vec_safe_push (args, arg); } } - else - gcc_unreachable (); va_end (p); call = build_min_non_dep_call_vec (non_dep, fn, args); @@ -3747,7 +3745,7 @@ build_min_non_dep_op_overload (tree non_dep, tree overload, tree object, unsigned int nargs = call_expr_nargs (non_dep); tree fn = overload; - if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE) + if (DECL_OBJECT_MEMBER_FUNCTION_P (overload)) { tree binfo = TYPE_BINFO (TREE_TYPE (object)); tree method = build_baselink (binfo, binfo, overload, NULL_TREE); diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index d101cf0..7e66913 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -7265,6 +7265,15 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain) && !mark_used (t, complain) && !(complain & tf_error)) return error_mark_node; + /* Pull out the function_decl for a single xobj member function, and + let the rest of this function handle it. This is similar to how + static member functions are handled in the BASELINK case above. */ + if (DECL_XOBJ_MEMBER_FUNCTION_P (t)) + { + arg = t; + break; + } + type = build_ptrmem_type (context_for_name_lookup (t), TREE_TYPE (t)); t = make_ptrmem_cst (type, t); diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic1.C new file mode 100644 index 0000000..134182c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic1.C @@ -0,0 +1,114 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// basic use cases and calling + +// non-trailing return +// definitions +struct S0 { + void f0(this S0) {} + void f1(this S0&) {} + void f2(this S0&&) {} + void f3(this S0 const&) {} + void f4(this S0 const&&) {} + template + void d0(this Self&&) {} + void d1(this auto&&) {} +}; +// declarations +struct S1 { + void f0(this S1); + void f1(this S1&); + void f2(this S1&&); + void f3(this S1 const&); + void f4(this S1 const&&); + template + void d0(this Self&&); + void d1(this auto&&); +}; +// out of line definitions +void S1::f0(this S1) {} +void S1::f1(this S1&) {} +void S1::f2(this S1&&) {} +void S1::f3(this S1 const&) {} +void S1::f4(this S1 const&&) {} +template +void S1::d0(this Self&&) {} +void S1::d1(this auto&&) {} + +// trailing return +// definitions +struct S2 { + auto f0(this S2) -> void {} + auto f1(this S2&) -> void {} + auto f2(this S2&&) -> void {} + auto f3(this S2 const&) -> void {} + auto f4(this S2 const&&) -> void {} + template + auto d0(this Self&&) -> void {} + + auto d1(this auto&&) -> void {} +}; +// declarations +struct S3 { + auto f0(this S3) -> void; + auto f1(this S3&) -> void; + auto f2(this S3&&) -> void; + auto f3(this S3 const&) -> void; + auto f4(this S3 const&&) -> void; + template + auto d0(this Self&&) -> void; + auto d1(this auto&&) -> void; +}; +// out of line definitions +auto S3::f0(this S3) -> void {} +auto S3::f1(this S3&) -> void {} +auto S3::f2(this S3&&) -> void {} +auto S3::f3(this S3 const&) -> void {} +auto S3::f4(this S3 const&&) -> void {} +template +auto S3::d0(this Self&&) -> void {} +auto S3::d1(this auto&&) -> void {} + +template +void call_with_qualification() +{ + T obj{}; + // by value should take any qualification (f0) + T{}.f0(); + obj.f0(); + static_cast(obj).f0(); + static_cast(obj).f0(); + static_cast(obj).f0(); + // specific qualification (f1 - f4) + T{}.f2(); + T{}.f3(); + T{}.f4(); + obj.f1(); + obj.f3(); + static_cast(obj).f2(); + static_cast(obj).f3(); + static_cast(obj).f4(); + static_cast(obj).f3(); + static_cast(obj).f4(); + // deduced should (obviously) take any qualification (d0, d1) + T{}.d0(); + obj.d0(); + static_cast(obj).d0(); + static_cast(obj).d0(); + static_cast(obj).d0(); + T{}.d1(); + obj.d1(); + static_cast(obj).d1(); + static_cast(obj).d1(); + static_cast(obj).d1(); +} + +void perform_calls() +{ + call_with_qualification(); + call_with_qualification(); + call_with_qualification(); + call_with_qualification(); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic2.C new file mode 100644 index 0000000..6b2cad1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic2.C @@ -0,0 +1,28 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// explicit object member function pointer type deduction, +// conversion to function pointer, +// and calling through pointer to function + +struct S { + int _n; + int f(this S& self) { return self._n; } +}; + +using f_type = int(*)(S&); + +static_assert (__is_same (f_type, decltype (&S::f))); + +int main() +{ + auto fp0 = &S::f; + f_type fp1 = &S::f; + static_assert (__is_same (decltype (fp0), decltype (fp1))); + S s{42}; + if (fp0 (s) != 42) + __builtin_abort (); + if (fp1 (s) != 42) + __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic3.C new file mode 100644 index 0000000..e466cc9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic3.C @@ -0,0 +1,496 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// bogus diagnosis of valid declarations as redeclarations +// tests for by-value are elsewhere (todo: add filename) + +// each group has 8 overloads that each take +// lvalue ref to S +// rvalue ref to S +// lvalue c ref to S +// rvalue c ref to S +// lvalue v ref to S +// rvalue v ref to S +// lvalue cv ref to S +// rvalue cv ref to S +// where S is the struct the function is declared in + +// only xobj (the most basic case) + +struct S { + void f(this S &); + void f(this S &&); + void f(this S const&); + void f(this S const&&); + void f(this S volatile&); + void f(this S volatile&&); + void f(this S const volatile&); + void f(this S const volatile&&); +}; + +// I* has the 1 xobj 7 iobj cases +// X* has the 7 xobj 1 iobj cases +// *0 has the unique function first, the rest after +// *1 has the unique function last, the rest after +// *2 has the functions in the order stated above +// xobj first, 1 xobj, 7 iobj + +// (yes there are some redundant cases) + +// unique first, 1 xobj 7 iobj + +struct I0 { + void f0(this I0&); + void f0() &&; + void f0() const&; + void f0() const&&; + void f0() volatile&; + void f0() volatile&&; + void f0() const volatile&; + void f0() const volatile&&; + + void f1(this I0&&); + void f1() &; + void f1() const&; + void f1() const&&; + void f1() volatile&; + void f1() volatile&&; + void f1() const volatile&; + void f1() const volatile&&; + + void fc0(this I0 const&); + void fc0() &; + void fc0() &&; + void fc0() const&&; + void fc0() volatile&; + void fc0() volatile&&; + void fc0() const volatile&; + void fc0() const volatile&&; + + void fc1(this I0 const&&); + void fc1() &; + void fc1() &&; + void fc1() const&; + void fc1() volatile&; + void fc1() volatile&&; + void fc1() const volatile&; + void fc1() const volatile&&; + + void fv0(this I0 volatile&); + void fv0() &; + void fv0() &&; + void fv0() const&; + void fv0() const&&; + void fv0() volatile&&; + void fv0() const volatile&; + void fv0() const volatile&&; + + void fv1(this I0 volatile&&); + void fv1() &; + void fv1() &&; + void fv1() const&; + void fv1() const&&; + void fv1() volatile&; + void fv1() const volatile&; + void fv1() const volatile&&; + + void fcv0(this I0 const volatile&); + void fcv0() &; + void fcv0() &&; + void fcv0() const&; + void fcv0() const&&; + void fcv0() volatile&; + void fcv0() volatile&&; + void fcv0() const volatile&&; + + void fcv1(this I0 const volatile&&); + void fcv1() &; + void fcv1() &&; + void fcv1() const&; + void fcv1() const&&; + void fcv1() volatile&; + void fcv1() volatile&&; + void fcv1() const volatile&; +}; + +// unique last, 1 xobj 7 iobj + +struct I1 { + void f0() &&; + void f0() const&; + void f0() const&&; + void f0() volatile&; + void f0() volatile&&; + void f0() const volatile&; + void f0() const volatile&&; + void f0(this I1&); + + void f1() &; + void f1() const&; + void f1() const&&; + void f1() volatile&; + void f1() volatile&&; + void f1() const volatile&; + void f1() const volatile&&; + void f1(this I1&&); + + void fc0() &; + void fc0() &&; + void fc0() const&&; + void fc0() volatile&; + void fc0() volatile&&; + void fc0() const volatile&; + void fc0() const volatile&&; + void fc0(this I1 const&); + + void fc1() &; + void fc1() &&; + void fc1() const&; + void fc1() volatile&; + void fc1() volatile&&; + void fc1() const volatile&; + void fc1() const volatile&&; + void fc1(this I1 const&&); + + void fv0() &; + void fv0() &&; + void fv0() const&; + void fv0() const&&; + void fv0() volatile&&; + void fv0() const volatile&; + void fv0() const volatile&&; + void fv0(this I1 volatile&); + + void fv1() &; + void fv1() &&; + void fv1() const&; + void fv1() const&&; + void fv1() volatile&; + void fv1() const volatile&; + void fv1() const volatile&&; + void fv1(this I1 volatile&&); + + void fcv0() &; + void fcv0() &&; + void fcv0() const&; + void fcv0() const&&; + void fcv0() volatile&; + void fcv0() volatile&&; + void fcv0() const volatile&&; + void fcv0(this I1 const volatile&); + + void fcv1() &; + void fcv1() &&; + void fcv1() const&; + void fcv1() const&&; + void fcv1() volatile&; + void fcv1() volatile&&; + void fcv1() const volatile&; + void fcv1(this I1 const volatile&&); +}; + +// ordered, 1 xobj 7 iobj + +struct I2 { + void f0(this I2&); + void f0() &&; + void f0() const&; + void f0() const&&; + void f0() volatile&; + void f0() volatile&&; + void f0() const volatile&; + void f0() const volatile&&; + + void f1() &; + void f1(this I2&&); + void f1() const&; + void f1() const&&; + void f1() volatile&; + void f1() volatile&&; + void f1() const volatile&; + void f1() const volatile&&; + + void fc0() &; + void fc0() &&; + void fc0(this I2 const&); + void fc0() const&&; + void fc0() volatile&; + void fc0() volatile&&; + void fc0() const volatile&; + void fc0() const volatile&&; + + void fc1() &; + void fc1() &&; + void fc1() const&; + void fc1(this I2 const&&); + void fc1() volatile&; + void fc1() volatile&&; + void fc1() const volatile&; + void fc1() const volatile&&; + + void fv0() &; + void fv0() &&; + void fv0() const&; + void fv0() const&&; + void fv0(this I2 volatile&); + void fv0() volatile&&; + void fv0() const volatile&; + void fv0() const volatile&&; + + void fv1() &; + void fv1() &&; + void fv1() const&; + void fv1() const&&; + void fv1() volatile&; + void fv1(this I2 volatile&&); + void fv1() const volatile&; + void fv1() const volatile&&; + + void fcv0() &; + void fcv0() &&; + void fcv0() const&; + void fcv0() const&&; + void fcv0() volatile&; + void fcv0() volatile&&; + void fcv0(this I2 const volatile&); + void fcv0() const volatile&&; + + void fcv1() &; + void fcv1() &&; + void fcv1() const&; + void fcv1() const&&; + void fcv1() volatile&; + void fcv1() volatile&&; + void fcv1() const volatile&; + void fcv1(this I2 const volatile&&); +}; + + +// iobj first, 7 xobj, 1 iobj + +struct X0 { + void f0() &; + void f0(this X0 &&); + void f0(this X0 const&); + void f0(this X0 const&&); + void f0(this X0 volatile&); + void f0(this X0 volatile&&); + void f0(this X0 const volatile&); + void f0(this X0 const volatile&&); + + void f1() &&; + void f1(this X0 &); + void f1(this X0 const&); + void f1(this X0 const&&); + void f1(this X0 volatile&); + void f1(this X0 volatile&&); + void f1(this X0 const volatile&); + void f1(this X0 const volatile&&); + + void fc0() const&; + void fc0(this X0 &); + void fc0(this X0 &&); + void fc0(this X0 const&&); + void fc0(this X0 volatile&); + void fc0(this X0 volatile&&); + void fc0(this X0 const volatile&); + void fc0(this X0 const volatile&&); + + void fc1() const&&; + void fc1(this X0 &); + void fc1(this X0 &&); + void fc1(this X0 const&); + void fc1(this X0 volatile&); + void fc1(this X0 volatile&&); + void fc1(this X0 const volatile&); + void fc1(this X0 const volatile&&); + + void fv0() volatile&; + void fv0(this X0 &); + void fv0(this X0 &&); + void fv0(this X0 const&); + void fv0(this X0 const&&); + void fv0(this X0 volatile&&); + void fv0(this X0 const volatile&); + void fv0(this X0 const volatile&&); + + void fv1() volatile&&; + void fv1(this X0 &); + void fv1(this X0 &&); + void fv1(this X0 const&); + void fv1(this X0 const&&); + void fv1(this X0 volatile&); + void fv1(this X0 const volatile&); + void fv1(this X0 const volatile&&); + + void fcv0() const volatile&; + void fcv0(this X0 &); + void fcv0(this X0 &&); + void fcv0(this X0 const&); + void fcv0(this X0 const&&); + void fcv0(this X0 volatile&); + void fcv0(this X0 volatile&&); + void fcv0(this X0 const volatile&&); + + void fcv1() const volatile&&; + void fcv1(this X0 &); + void fcv1(this X0 &&); + void fcv1(this X0 const&); + void fcv1(this X0 const&&); + void fcv1(this X0 volatile&); + void fcv1(this X0 volatile&&); + void fcv1(this X0 const volatile&); +}; + +// iobj last, 7 xobj 1 iobj + +struct X1 { + void f0(this X1 &&); + void f0(this X1 const&); + void f0(this X1 const&&); + void f0(this X1 volatile&); + void f0(this X1 volatile&&); + void f0(this X1 const volatile&); + void f0(this X1 const volatile&&); + void f0() &; + + void f1(this X1 &); + void f1(this X1 const&); + void f1(this X1 const&&); + void f1(this X1 volatile&); + void f1(this X1 volatile&&); + void f1(this X1 const volatile&); + void f1(this X1 const volatile&&); + void f1() &&; + + void fc0(this X1 &); + void fc0(this X1 &&); + void fc0(this X1 const&&); + void fc0(this X1 volatile&); + void fc0(this X1 volatile&&); + void fc0(this X1 const volatile&); + void fc0(this X1 const volatile&&); + void fc0() const&; + + void fc1(this X1 &); + void fc1(this X1 &&); + void fc1(this X1 const&); + void fc1(this X1 volatile&); + void fc1(this X1 volatile&&); + void fc1(this X1 const volatile&); + void fc1(this X1 const volatile&&); + void fc1() const&&; + + void fv0(this X1 &); + void fv0(this X1 &&); + void fv0(this X1 const&); + void fv0(this X1 const&&); + void fv0(this X1 volatile&&); + void fv0(this X1 const volatile&); + void fv0(this X1 const volatile&&); + void fv0() volatile&; + + void fv1(this X1 &); + void fv1(this X1 &&); + void fv1(this X1 const&); + void fv1(this X1 const&&); + void fv1(this X1 volatile&); + void fv1(this X1 const volatile&); + void fv1(this X1 const volatile&&); + void fv1() volatile&&; + + void fcv0(this X1 &); + void fcv0(this X1 &&); + void fcv0(this X1 const&); + void fcv0(this X1 const&&); + void fcv0(this X1 volatile&); + void fcv0(this X1 volatile&&); + void fcv0(this X1 const volatile&&); + void fcv0() const volatile&; + + void fcv1(this X1 &); + void fcv1(this X1 &&); + void fcv1(this X1 const&); + void fcv1(this X1 const&&); + void fcv1(this X1 volatile&); + void fcv1(this X1 volatile&&); + void fcv1(this X1 const volatile&); + void fcv1() const volatile&&; +}; + +// ordered, 7 xobj 1 iobj + +struct X2 { + void f0() &; + void f0(this X2 &&); + void f0(this X2 const&); + void f0(this X2 const&&); + void f0(this X2 volatile&); + void f0(this X2 volatile&&); + void f0(this X2 const volatile&); + void f0(this X2 const volatile&&); + + void f1(this X2 &); + void f1() &&; + void f1(this X2 const&); + void f1(this X2 const&&); + void f1(this X2 volatile&); + void f1(this X2 volatile&&); + void f1(this X2 const volatile&); + void f1(this X2 const volatile&&); + + void fc0(this X2 &); + void fc0(this X2 &&); + void fc0() const&; + void fc0(this X2 const&&); + void fc0(this X2 volatile&); + void fc0(this X2 volatile&&); + void fc0(this X2 const volatile&); + void fc0(this X2 const volatile&&); + + void fc1(this X2 &); + void fc1(this X2 &&); + void fc1(this X2 const&); + void fc1() const&&; + void fc1(this X2 volatile&); + void fc1(this X2 volatile&&); + void fc1(this X2 const volatile&); + void fc1(this X2 const volatile&&); + + void fv0(this X2 &); + void fv0(this X2 &&); + void fv0(this X2 const&); + void fv0(this X2 const&&); + void fv0() volatile&; + void fv0(this X2 volatile&&); + void fv0(this X2 const volatile&); + void fv0(this X2 const volatile&&); + + void fv1(this X2 &); + void fv1(this X2 &&); + void fv1(this X2 const&); + void fv1(this X2 const&&); + void fv1(this X2 volatile&); + void fv1() volatile&&; + void fv1(this X2 const volatile&); + void fv1(this X2 const volatile&&); + + void fcv0(this X2 &); + void fcv0(this X2 &&); + void fcv0(this X2 const&); + void fcv0(this X2 const&&); + void fcv0(this X2 volatile&); + void fcv0(this X2 volatile&&); + void fcv0() const volatile&; + void fcv0(this X2 const volatile&&); + + void fcv1(this X2 &); + void fcv1(this X2 &&); + void fcv1(this X2 const&); + void fcv1(this X2 const&&); + void fcv1(this X2 volatile&); + void fcv1(this X2 volatile&&); + void fcv1(this X2 const volatile&); + void fcv1() const volatile&&; +}; + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic4.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic4.C new file mode 100644 index 0000000..691ef68 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic4.C @@ -0,0 +1,113 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// valid overloading of iobj member functions without ref qualifiers +// with xobj member functions (and vice-versa) + +// this is the most you can mix these, it may look short but it does test +// all allowed cases (other than by-value and unrelated types) + +// [over.match.funcs.general.4] +// For implicit object member functions, the type of the implicit +// object parameter is +// -- “lvalue reference to cv X” for functions declared +// without a ref-qualifier or with the & ref-qualifier +// -- “rvalue reference to cv X” for functions declared with +// the && ref-qualifier + +// [basic.scope.scope.3] +// Two non-static member functions have corresponding object +// parameters if: +// -- exactly one is an implicit object member function with no +// ref-qualifier and the types of their object parameters +// ([dcl.fct]), after removing top-level references, are the +// same, or + +// in simpler terms, only the cv qualification of the explicit/implicit object +// parameter matter for determining whether these are redeclarations or overloads +// (when a ref qualifier is not present on the iobj member function) + +// xobj first, iobj last + +struct S0 { + void f(this S0 &); + void f(this S0 &&); + void f() const; + void f() volatile; + void f() const volatile; + + void fc(this S0 const&); + void fc(this S0 const&&); + void fc(); + void fc() volatile; + void fc() const volatile; + + void fv(this S0 volatile&); + void fv(this S0 volatile&&); + void fv(); + void fv() const; + void fv() const volatile; + + void fcv(this S0 const volatile&); + void fcv(this S0 const volatile&&); + void fcv(); + void fcv() const; + void fcv() volatile; +}; + +// iobj first, xobj last + +struct S1 { + void f() const; + void f() volatile; + void f() const volatile; + void f(this S1 &); + void f(this S1 &&); + + void fc(); + void fc() volatile; + void fc() const volatile; + void fc(this S1 const&); + void fc(this S1 const&&); + + void fv(); + void fv() const; + void fv() const volatile; + void fv(this S1 volatile&); + void fv(this S1 volatile&&); + + void fcv(); + void fcv() const; + void fcv() volatile; + void fcv(this S1 const volatile&); + void fcv(this S1 const volatile&&); +}; + +// in order + +struct S2 { + void f(this S2 &); + void f(this S2 &&); + void f() const; + void f() volatile; + void f() const volatile; + + void fc(); + void fc(this S2 const&); + void fc(this S2 const&&); + void fc() volatile; + void fc() const volatile; + + void fv(); + void fv() const; + void fv(this S2 volatile&); + void fv(this S2 volatile&&); + void fv() const volatile; + + void fcv(); + void fcv() const; + void fcv() volatile; + void fcv(this S2 const volatile&); + void fcv(this S2 const volatile&&); +}; + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic5.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic5.C new file mode 100644 index 0000000..9cf23d9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic5.C @@ -0,0 +1,33 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// conversion operators with xobj parameter + +inline constexpr int magic = 42; + +struct S0 { + operator int(this S0 const&) { + return magic; + } +}; + +struct S1 { + int _v; + int f(this int self) { + return self; + } + operator int(this S1 const& self) { + return self._v; + } +}; + +int main() +{ + if (S0{} != magic) + __builtin_abort (); + + S1 s{42}; + if (static_cast(s) != magic) + __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value1.C new file mode 100644 index 0000000..5ea5bcb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value1.C @@ -0,0 +1,48 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// conversion of the implicit object argument to an xobj parameter +// when calling by value xobj member functions + +// The initial implementation of xobj member functions incorrectly did not +// convert the implicit object argument when binding to the xobj +// parameter. In spite of this, it did correctly check to see if such a +// conversion would be valid, thus no diagnostic would be emitted when a +// conversion was valid, but instead of applying the conversion, the +// argument would silently be reinterpreted as the type of the parameter. + +// This is why we use uintptr_t for the value in S and compare the result +// of f to &s, we want to test for simple reinterpretation of the +// argument. To accurately test for this we make sure to use an object +// that has a different address than the value of our magic number. It's +// an impossibly improbable edge case but it's trivial to work around. We +// still compare against both the address of s and the magic number so we +// can additionally test for bugged conversions, while also +// differentiating that case from reinterpretation of the argument. + +using uintptr_t = __UINTPTR_TYPE__; +inline constexpr uintptr_t magic = 42; + +struct S { + uintptr_t _v; + uintptr_t f(this S self) { + return self._v; + } +}; + +int main() +{ + S s0{magic}; + S s1{magic}; + // prevent (absurdly improbable) bogus failures + S& s = magic != (uintptr_t)(&s0) ? s0 : s1; + + uintptr_t const ret = s.f(); + // check for reinterpretation of the object argument + if (ret == (uintptr_t)(&s)) + __builtin_abort (); + // check for a bugged conversion + if (ret != magic) + __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value2.C new file mode 100644 index 0000000..b8e8e73df --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value2.C @@ -0,0 +1,58 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// conversion of the implicit object argument to an xobj parameter +// using a user defined conversion or converting constructor +// when calling by value xobj member functions + +// see explicit-obj-by-value1.C for details on this test + +using uintptr_t = __UINTPTR_TYPE__; +inline constexpr uintptr_t magic = 42; + +struct S; + +struct FromS { + uintptr_t _v; + FromS(S); +}; + +struct S { + operator uintptr_t() const { + return magic; + } + uintptr_t f(this uintptr_t n) { + return n; + } + uintptr_t g(this FromS from_s) { + return from_s._v; + } +}; + +FromS::FromS(S) : _v(magic) {} + + +int main() +{ + S s0{}; + S s1{}; + // prevent (absurdly improbable) bogus failures + S& s = magic != (uintptr_t)(&s0) ? s0 : s1; + + uintptr_t const ret0 = s.f(); + // check for reinterpretation of the object argument + if (ret0 == (uintptr_t)(&s)) + __builtin_abort (); + // check for a bugged conversion + if (ret0 != magic) + __builtin_abort (); + + uintptr_t const ret1 = s.g(); + // check for reinterpretation of the object argument + if (ret1 == (uintptr_t)(&s)) + __builtin_abort (); + // check for a bugged conversion + if (ret1 != magic) + __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value3.C new file mode 100644 index 0000000..e6aff01 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value3.C @@ -0,0 +1,41 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// correct constructor selection when initializing a by value xobj parameter + +// see explicit-obj-by-value1.C for details on this test + +using uintptr_t = __UINTPTR_TYPE__; +inline constexpr uintptr_t magic = 42; +inline constexpr uintptr_t copy_magic = 5; +inline constexpr uintptr_t move_magic = 10; + +struct S { + uintptr_t _v; + explicit S(uintptr_t v) : _v(v) {} + S(S const& other) : _v(other._v + copy_magic) {} + S(S&& other) : _v(other._v + move_magic) {} + uintptr_t f(this S self) { + return self._v; + } +}; + +int main() +{ + S s0{magic}; + S s1{magic}; + // prevent (absurdly improbable (^2)) bogus results + // it's virtually impossible for both to have a bogus result, + // but we can guarantee correct results from both easily, so why not? + S& s_copy_from = magic + copy_magic != (uintptr_t)(&s0) ? s0 : s1; + S& s_move_from = magic + move_magic != (uintptr_t)(&s0) ? s0 : s1; + uintptr_t const copy_ret = static_cast(s_copy_from).f(); + uintptr_t const move_ret = static_cast(s_move_from).f(); + // we test specifically for reinterpretation in other + // by value tests, it's unnecessary to do it again here + if (copy_ret != magic + copy_magic) + __builtin_abort (); + if (move_ret != magic + move_magic) + __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value4.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value4.C new file mode 100644 index 0000000..9b4e005 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value4.C @@ -0,0 +1,20 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// diagnosis of ill-formed calls to by-value xobj member functions +// due to an absence of valid conversion functions + +struct NotFromS {}; + +struct S { + void f(this int) {} + void g(this NotFromS) {} +}; + +void test() +{ + S s{}; + s.f(); // { dg-error {cannot convert 'S' to 'int'} } + s.g(); // { dg-error {cannot convert 'S' to 'NotFromS'} } +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-constraints.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-constraints.C new file mode 100644 index 0000000..eb8a855 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-constraints.C @@ -0,0 +1,418 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// overload resolution of static/xobj and iobj/xobj member functions +// with constraints + +template +concept Constrain = true; + +inline constexpr int iobj_fn = 5; +inline constexpr int xobj_fn = 10; +inline constexpr int static_fn = 20; + +// first 2 letters are the order of the definitions +// the constraint applies to the first definition, +// for *_r cases the constraint applies to the second + +struct S { + // xobj/static + int f_xs_v(this S, Constrain auto) { return xobj_fn; }; + static int f_xs_v(auto) { return static_fn; }; + + int f_xs_ref(this S&, Constrain auto) { return xobj_fn; }; + static int f_xs_ref(auto) { return static_fn; }; + + int f_xs_cref(this S const&, Constrain auto) { return xobj_fn; }; + static int f_xs_cref(auto) { return static_fn; }; + + int f_xs_rref(this S&&, Constrain auto) { return xobj_fn; }; + static int f_xs_rref(auto) { return static_fn; }; + + int f_xs_crref(this S const&&, Constrain auto) { return xobj_fn; }; + static int f_xs_crref(auto) { return static_fn; }; + + // _r + int f_xs_v_r(this S, auto) { return xobj_fn; }; + static int f_xs_v_r(Constrain auto) { return static_fn; }; + + int f_xs_ref_r(this S&, auto) { return xobj_fn; }; + static int f_xs_ref_r(Constrain auto) { return static_fn; }; + + int f_xs_cref_r(this S const&, auto) { return xobj_fn; }; + static int f_xs_cref_r(Constrain auto) { return static_fn; }; + + int f_xs_rref_r(this S&&, auto) { return xobj_fn; }; + static int f_xs_rref_r(Constrain auto) { return static_fn; }; + + int f_xs_crref_r(this S const&&, auto) { return xobj_fn; }; + static int f_xs_crref_r(Constrain auto) { return static_fn; }; + + // static/xobj + static int f_sx_v(Constrain auto) { return static_fn; }; + int f_sx_v(this S, auto) { return xobj_fn; }; + + static int f_sx_ref(Constrain auto) { return static_fn; }; + int f_sx_ref(this S&, auto) { return xobj_fn; }; + + static int f_sx_cref(Constrain auto) { return static_fn; }; + int f_sx_cref(this S const&, auto) { return xobj_fn; }; + + static int f_sx_rref(Constrain auto) { return static_fn; }; + int f_sx_rref(this S&&, auto) { return xobj_fn; }; + + static int f_sx_crref(Constrain auto) { return static_fn; }; + int f_sx_crref(this S const&&, auto) { return xobj_fn; }; + + // _r + static int f_sx_v_r(auto) { return static_fn; }; + int f_sx_v_r(this S, Constrain auto) { return xobj_fn; }; + + static int f_sx_ref_r(auto) { return static_fn; }; + int f_sx_ref_r(this S&, Constrain auto) { return xobj_fn; }; + + static int f_sx_cref_r(auto) { return static_fn; }; + int f_sx_cref_r(this S const&, Constrain auto) { return xobj_fn; }; + + static int f_sx_rref_r(auto) { return static_fn; }; + int f_sx_rref_r(this S&&, Constrain auto) { return xobj_fn; }; + + static int f_sx_crref_r(auto) { return static_fn; }; + int f_sx_crref_r(this S const&&, Constrain auto) { return xobj_fn; }; + + // xobj/iobj with matching object parameters + + // We are only testing constraints here, so we need parameter lists + // to match, which means we need corresponding object parameters. + // Remember, the rules for object parameter correspondence are weird. + // ([basic.scope.scope-3.1]) + + // *_refqual the iobj member function has a reference qualifier + // *_r the constraint applies to the second definition + + // ix + int f_ix_m0(Constrain auto) { return iobj_fn; }; + int f_ix_m0(this S&, auto) { return xobj_fn; }; + // See note + int f_ix_m1(Constrain auto) { return iobj_fn; }; + int f_ix_m1(this S&&, auto) { return xobj_fn; }; + + int f_ix_c0(Constrain auto) const { return iobj_fn; }; + int f_ix_c0(this S const&, auto) { return xobj_fn; }; + // See note + int f_ix_c1(Constrain auto) const { return iobj_fn; }; + int f_ix_c1(this S const&&, auto) { return xobj_fn; }; + + // xi + int f_xi_m0(this S&, Constrain auto) { return xobj_fn; }; + int f_xi_m0(auto) { return iobj_fn; }; + // See note + int f_xi_m1(this S&&, Constrain auto) { return xobj_fn; }; + int f_xi_m1(auto) { return iobj_fn; }; + + int f_xi_c0(this S const&, Constrain auto) { return xobj_fn; }; + int f_xi_c0(auto) const { return iobj_fn; }; + // See note + int f_xi_c1(this S const&&, Constrain auto) { return xobj_fn; }; + int f_xi_c1(auto) const { return iobj_fn; }; + + // with ref qualifier + + // ix + int f_ix_m0_refqual(Constrain auto) & { return iobj_fn; }; + int f_ix_m0_refqual(this S&, auto) { return xobj_fn; }; + + int f_ix_m1_refqual(Constrain auto) && { return iobj_fn; }; + int f_ix_m1_refqual(this S&&, auto) { return xobj_fn; }; + + int f_ix_c0_refqual(Constrain auto) const& { return iobj_fn; }; + int f_ix_c0_refqual(this S const&, auto) { return xobj_fn; }; + + int f_ix_c1_refqual(Constrain auto) const&& { return iobj_fn; }; + int f_ix_c1_refqual(this S const&&, auto) { return xobj_fn; }; + + // xi + int f_xi_m0_refqual(this S&, Constrain auto) { return xobj_fn; }; + int f_xi_m0_refqual(auto) & { return iobj_fn; }; + + int f_xi_m1_refqual(this S&&, Constrain auto) { return xobj_fn; }; + int f_xi_m1_refqual(auto) && { return iobj_fn; }; + + int f_xi_c0_refqual(this S const&, Constrain auto) { return xobj_fn; }; + int f_xi_c0_refqual(auto) const& { return iobj_fn; }; + + int f_xi_c1_refqual(this S const&&, Constrain auto) { return xobj_fn; }; + int f_xi_c1_refqual(auto) const&& { return iobj_fn; }; + + // _r without ref qualifier + + // ix + int f_ix_m0_r(auto) { return iobj_fn; }; + int f_ix_m0_r(this S&, Constrain auto) { return xobj_fn; }; + // See note + int f_ix_m1_r(auto) { return iobj_fn; }; + int f_ix_m1_r(this S&&, Constrain auto) { return xobj_fn; }; + + int f_ix_c0_r(auto) const { return iobj_fn; }; + int f_ix_c0_r(this S const&, Constrain auto) { return xobj_fn; }; + // See note + int f_ix_c1_r(auto) const { return iobj_fn; }; + int f_ix_c1_r(this S const&&, Constrain auto) { return xobj_fn; }; + + // xi + int f_xi_m0_r(this S&, auto) { return xobj_fn; }; + int f_xi_m0_r(Constrain auto) { return iobj_fn; }; + // See note + int f_xi_m1_r(this S&&, auto) { return xobj_fn; }; + int f_xi_m1_r(Constrain auto) { return iobj_fn; }; + + int f_xi_c0_r(this S const&, auto) { return xobj_fn; }; + int f_xi_c0_r(Constrain auto) const { return iobj_fn; }; + // See note + int f_xi_c1_r(this S const&&, auto) { return xobj_fn; }; + int f_xi_c1_r(Constrain auto) const { return iobj_fn; }; + + // _r with ref qualifier + // ix + int f_ix_m0_refqual_r(auto) & { return iobj_fn; }; + int f_ix_m0_refqual_r(this S&, Constrain auto) { return xobj_fn; }; + + int f_ix_m1_refqual_r(auto) && { return iobj_fn; }; + int f_ix_m1_refqual_r(this S&&, Constrain auto) { return xobj_fn; }; + + int f_ix_c0_refqual_r(auto) const& { return iobj_fn; }; + int f_ix_c0_refqual_r(this S const&, Constrain auto) { return xobj_fn; }; + + int f_ix_c1_refqual_r(auto) const&& { return iobj_fn; }; + int f_ix_c1_refqual_r(this S const&&, Constrain auto) { return xobj_fn; }; + + // xi + int f_xi_m0_refqual_r(this S&, auto) { return xobj_fn; }; + int f_xi_m0_refqual_r(Constrain auto) & { return iobj_fn; }; + + int f_xi_m1_refqual_r(this S&&, auto) { return xobj_fn; }; + int f_xi_m1_refqual_r(Constrain auto) && { return iobj_fn; }; + + int f_xi_c0_refqual_r(this S const&, auto) { return xobj_fn; }; + int f_xi_c0_refqual_r(Constrain auto) const& { return iobj_fn; }; + + int f_xi_c1_refqual_r(this S const&&, auto) { return xobj_fn; }; + int f_xi_c1_refqual_r(Constrain auto) const&& { return iobj_fn; }; +}; + + +int main() +{ + // The commented out cases are ambiguous, which is most likely the correct + // behavior. It is something that I want to propose to change, and I want + // to leave them in as they are a little weird. + // + // Furthermore, as the comment at the top of this file indicates, I am not + // clear on the correct behavior of the static/xobj cases in general. + + S s{}; + if (s.f_xs_v (0) != xobj_fn) + __builtin_abort (); + if (s.f_xs_ref (0) != xobj_fn) + __builtin_abort (); + if (s.f_xs_cref (0) != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_xs_rref (0) != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_xs_crref (0) != xobj_fn) + __builtin_abort (); + // if (s.f_xs_dv (0) != xobj_fn) + // __builtin_abort (); + // if (s.f_xs_dcref (0) != xobj_fn) + // __builtin_abort (); + // if (s.f_xs_dfwdref (0) != xobj_fn) + // __builtin_abort (); + + if (s.f_xs_v_r (0) != static_fn) + __builtin_abort (); + if (s.f_xs_ref_r (0) != static_fn) + __builtin_abort (); + if (s.f_xs_cref_r (0) != static_fn) + __builtin_abort (); + if (static_cast(s).f_xs_rref_r (0) != static_fn) + __builtin_abort (); + if (static_cast(s).f_xs_crref_r (0) != static_fn) + __builtin_abort (); + // if (s.f_xs_dv_r (0) != static_fn) + // __builtin_abort (); + // if (s.f_xs_dcref_r (0) != static_fn) + // __builtin_abort (); + // if (s.f_xs_dfwdref_r (0) != static_fn) + // __builtin_abort (); + + if (s.f_sx_v (0) != static_fn) + __builtin_abort (); + if (s.f_sx_ref (0) != static_fn) + __builtin_abort (); + if (s.f_sx_cref (0) != static_fn) + __builtin_abort (); + if (static_cast(s).f_sx_rref (0) != static_fn) + __builtin_abort (); + if (static_cast(s).f_sx_crref (0) != static_fn) + __builtin_abort (); + // if (s.f_sx_dv (0) != static_fn) + // __builtin_abort (); + // if (s.f_sx_dcref (0) != static_fn) + // __builtin_abort (); + // if (s.f_sx_dfwdref (0) != static_fn) + // __builtin_abort (); + + if (s.f_sx_v_r (0) != xobj_fn) + __builtin_abort (); + if (s.f_sx_ref_r (0) != xobj_fn) + __builtin_abort (); + if (s.f_sx_cref_r (0) != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_sx_rref_r (0) != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_sx_crref_r (0) != xobj_fn) + __builtin_abort (); + // if (s.f_sx_dv_r (0) != xobj_fn) + // __builtin_abort (); + // if (s.f_sx_dcref_r (0) != xobj_fn) + // __builtin_abort (); + // if (s.f_sx_dfwdref_r (0) != xobj_fn) + // __builtin_abort (); + + // iobj/xobj + + // The commented out cases are tested below as their correct behavior is + // unintuitive, see the note below for details. + + if (s.f_ix_m0 (0) != iobj_fn) + __builtin_abort (); + // s.f_ix_m1 + if (s.f_ix_c0 (0) != iobj_fn) + __builtin_abort (); + // s.f_ix_c1 + if (s.f_xi_m0 (0) != xobj_fn) + __builtin_abort (); + // s.f_xi_m1 + if (s.f_xi_c0 (0) != xobj_fn) + __builtin_abort (); + // s.f_xi_c1 + if (s.f_ix_m0_refqual (0) != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_ix_m1_refqual (0) != iobj_fn) + __builtin_abort (); + if (s.f_ix_c0_refqual (0) != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_ix_c1_refqual (0) != iobj_fn) + __builtin_abort (); + if (s.f_xi_m0_refqual (0) != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_m1_refqual (0) != xobj_fn) + __builtin_abort (); + if (s.f_xi_c0_refqual (0) != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_c1_refqual (0) != xobj_fn) + __builtin_abort (); + if (s.f_ix_m0_r (0) != xobj_fn) + __builtin_abort (); + // s.f_ix_m1_r + if (s.f_ix_c0_r (0) != xobj_fn) + __builtin_abort (); + // s.f_ix_c1_r + if (s.f_xi_m0_r (0) != iobj_fn) + __builtin_abort (); + // s.f_xi_m1_r + if (s.f_xi_c0_r (0) != iobj_fn) + __builtin_abort (); + // s.f_xi_c1_r + if (s.f_ix_m0_refqual_r (0) != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_ix_m1_refqual_r (0) != xobj_fn) + __builtin_abort (); + if (s.f_ix_c0_refqual_r (0) != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_ix_c1_refqual_r (0) != xobj_fn) + __builtin_abort (); + if (s.f_xi_m0_refqual_r (0) != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_m1_refqual_r (0) != iobj_fn) + __builtin_abort (); + if (s.f_xi_c0_refqual_r (0) != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_c1_refqual_r (0) != iobj_fn) + __builtin_abort (); + + +/* Note: + These cases are weird, the object argument correspond, but are not the same + type ([basic.scope.scope-3.1]), so we get this funny edge case where the + constraint stops them from being considered redeclarations, but isn't taken + into account for the lvalue case. You can't bind an lvalue to an rvalue + reference so the iobj member function is always taken regardless of which + overload is constrained. + + [over.match.funcs.general-4] + For implicit object member functions, the type of the implicit object + parameter is + (4.1) “lvalue reference to cv X” for functions declared without a + ref-qualifier or with the & ref-qualifier + + You would think that calling these functions with an rvalue would be the + same then, always taking the xobj member function. However, for backwards + compatibility reasons, an unqualified member function can be called on an + object that is an rvalue. + + [over.match.funcs.general-5] + For implicit object member functions declared without a ref-qualifier, even + if the implicit object parameter is not const-qualified, an rvalue can be + bound to the parameter as long as in all other respects the argument can be + converted to the type of the implicit object parameter. + + And finally, since the object parameters correspond ([basic.scope.scope-3.1]) + the constraints are taken into account. + + So in conclusion, calling these functions with an lvalue always resolves to + the iobj member function, and calling them with rvalues take the constraints + into account. + + As wacky as this is, this is the correct behavior. */ + + // Always takes the iobj member function, can't bind an lvalue to an rvalue + // reference. + if (s.f_ix_m1 (0) != iobj_fn) + __builtin_abort (); + if (s.f_ix_c1 (0) != iobj_fn) + __builtin_abort (); + if (s.f_xi_m1 (0) != iobj_fn) + __builtin_abort (); + if (s.f_xi_c1 (0) != iobj_fn) + __builtin_abort (); + + if (s.f_ix_m1_r (0) != iobj_fn) + __builtin_abort (); + if (s.f_ix_c1_r (0) != iobj_fn) + __builtin_abort (); + if (s.f_xi_m1_r (0) != iobj_fn) + __builtin_abort (); + if (s.f_xi_c1_r (0) != iobj_fn) + __builtin_abort (); + + // Constraints are taken into account here, see note for more information. + if (static_cast(s).f_ix_m1 (0) != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_ix_c1 (0) != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_m1 (0) != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_c1 (0) != xobj_fn) + __builtin_abort (); + + if (static_cast(s).f_ix_m1_r (0) != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_ix_c1_r (0) != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_m1_r (0) != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_c1_r (0) != iobj_fn) + __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-constraints2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-constraints2.C new file mode 100644 index 0000000..d3909ec --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-constraints2.C @@ -0,0 +1,462 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// overload resolution of static/xobj and iobj/xobj non-template member functions +// with constraints in a class template + +template +concept Constrain = true; + +inline constexpr int iobj_fn = 5; +inline constexpr int xobj_fn = 10; +inline constexpr int static_fn = 20; + +// first 2 letters are the order of the definitions +// the constraint applies to the first definition, +// for *_r cases the constraint applies to the second + +template +struct S { + // xobj/static + int f_xs_v(this S) requires Constrain { return xobj_fn; }; + static int f_xs_v() { return static_fn; }; + + int f_xs_ref(this S&) requires Constrain { return xobj_fn; }; + static int f_xs_ref() { return static_fn; }; + + int f_xs_cref(this S const&) requires Constrain { return xobj_fn; }; + static int f_xs_cref() { return static_fn; }; + + int f_xs_rref(this S&&) requires Constrain { return xobj_fn; }; + static int f_xs_rref() { return static_fn; }; + + int f_xs_crref(this S const&&) requires Constrain { return xobj_fn; }; + static int f_xs_crref() { return static_fn; }; + + int f_xs_dv(this auto) requires Constrain { return xobj_fn; }; + static int f_xs_dv() { return static_fn; }; + + int f_xs_dcref(this auto const&) requires Constrain { return xobj_fn; }; + static int f_xs_dcref() { return static_fn; }; + + int f_xs_dfwdref(this auto&&) requires Constrain { return xobj_fn; }; + static int f_xs_dfwdref() { return static_fn; }; + + // _r + int f_xs_v_r(this S) { return xobj_fn; }; + static int f_xs_v_r() requires Constrain { return static_fn; }; + + int f_xs_ref_r(this S&) { return xobj_fn; }; + static int f_xs_ref_r() requires Constrain { return static_fn; }; + + int f_xs_cref_r(this S const&) { return xobj_fn; }; + static int f_xs_cref_r() requires Constrain { return static_fn; }; + + int f_xs_rref_r(this S&&) { return xobj_fn; }; + static int f_xs_rref_r() requires Constrain { return static_fn; }; + + int f_xs_crref_r(this S const&&) { return xobj_fn; }; + static int f_xs_crref_r() requires Constrain { return static_fn; }; + + int f_xs_dv_r(this auto) { return xobj_fn; }; + static int f_xs_dv_r() requires Constrain { return static_fn; }; + + int f_xs_dcref_r(this auto const&) { return xobj_fn; }; + static int f_xs_dcref_r() requires Constrain { return static_fn; }; + + int f_xs_dfwdref_r(this auto&&) { return xobj_fn; }; + static int f_xs_dfwdref_r() requires Constrain { return static_fn; }; + + // static/xobj + static int f_sx_v() requires Constrain { return static_fn; }; + int f_sx_v(this S) { return xobj_fn; }; + + static int f_sx_ref() requires Constrain { return static_fn; }; + int f_sx_ref(this S&) { return xobj_fn; }; + + static int f_sx_cref() requires Constrain { return static_fn; }; + int f_sx_cref(this S const&) { return xobj_fn; }; + + static int f_sx_rref() requires Constrain { return static_fn; }; + int f_sx_rref(this S&&) { return xobj_fn; }; + + static int f_sx_crref() requires Constrain { return static_fn; }; + int f_sx_crref(this S const&&) { return xobj_fn; }; + + static int f_sx_dv() requires Constrain { return static_fn; }; + int f_sx_dv(this auto) { return xobj_fn; }; + + static int f_sx_dcref() requires Constrain { return static_fn; }; + int f_sx_dcref(this auto const&) { return xobj_fn; }; + + static int f_sx_dfwdref() requires Constrain { return static_fn; }; + int f_sx_dfwdref(this auto&&) { return xobj_fn; }; + + // _r + static int f_sx_v_r() { return static_fn; }; + int f_sx_v_r(this S) requires Constrain { return xobj_fn; }; + + static int f_sx_ref_r() { return static_fn; }; + int f_sx_ref_r(this S&) requires Constrain { return xobj_fn; }; + + static int f_sx_cref_r() { return static_fn; }; + int f_sx_cref_r(this S const&) requires Constrain { return xobj_fn; }; + + static int f_sx_rref_r() { return static_fn; }; + int f_sx_rref_r(this S&&) requires Constrain { return xobj_fn; }; + + static int f_sx_crref_r() { return static_fn; }; + int f_sx_crref_r(this S const&&) requires Constrain { return xobj_fn; }; + + static int f_sx_dv_r() { return static_fn; }; + int f_sx_dv_r(this auto) requires Constrain { return xobj_fn; }; + + static int f_sx_dcref_r() { return static_fn; }; + int f_sx_dcref_r(this auto const&) requires Constrain { return xobj_fn; }; + + static int f_sx_dfwdref_r() { return static_fn; }; + int f_sx_dfwdref_r(this auto&&) requires Constrain { return xobj_fn; }; + + // xobj/iobj with matching object parameters + + // We are only testing constraints here, so we need parameter lists + // to match, which means we need corresponding object parameters. + // Remember, the rules for object parameter correspondence are weird. + // ([basic.scope.scope-3.1]) + // + // NOTE: CWG2789 does not specify this properly, I am implementing it + // assuming the above correspondence rules + + // *_refqual the iobj member function has a reference qualifier + // *_r the constraint applies to the second definition + + // ix + int f_ix_m0() requires Constrain { return iobj_fn; }; + int f_ix_m0(this S&) { return xobj_fn; }; + // See note + int f_ix_m1() requires Constrain { return iobj_fn; }; + int f_ix_m1(this S&&) { return xobj_fn; }; + + int f_ix_c0() const requires Constrain { return iobj_fn; }; + int f_ix_c0(this S const&) { return xobj_fn; }; + // See note + int f_ix_c1() const requires Constrain { return iobj_fn; }; + int f_ix_c1(this S const&&) { return xobj_fn; }; + + // xi + int f_xi_m0(this S&) requires Constrain { return xobj_fn; }; + int f_xi_m0() { return iobj_fn; }; + // See note + int f_xi_m1(this S&&) requires Constrain { return xobj_fn; }; + int f_xi_m1() { return iobj_fn; }; + + int f_xi_c0(this S const&) requires Constrain { return xobj_fn; }; + int f_xi_c0() const { return iobj_fn; }; + // See note + int f_xi_c1(this S const&&) requires Constrain { return xobj_fn; }; + int f_xi_c1() const { return iobj_fn; }; + + // with ref qualifier + + // ix + int f_ix_m0_refqual() & requires Constrain { return iobj_fn; }; + int f_ix_m0_refqual(this S&) { return xobj_fn; }; + + int f_ix_m1_refqual() && requires Constrain { return iobj_fn; }; + int f_ix_m1_refqual(this S&&) { return xobj_fn; }; + + int f_ix_c0_refqual() const& requires Constrain { return iobj_fn; }; + int f_ix_c0_refqual(this S const&) { return xobj_fn; }; + + int f_ix_c1_refqual() const&& requires Constrain { return iobj_fn; }; + int f_ix_c1_refqual(this S const&&) { return xobj_fn; }; + + // xi + int f_xi_m0_refqual(this S&) requires Constrain { return xobj_fn; }; + int f_xi_m0_refqual() & { return iobj_fn; }; + + int f_xi_m1_refqual(this S&&) requires Constrain { return xobj_fn; }; + int f_xi_m1_refqual() && { return iobj_fn; }; + + int f_xi_c0_refqual(this S const&) requires Constrain { return xobj_fn; }; + int f_xi_c0_refqual() const& { return iobj_fn; }; + + int f_xi_c1_refqual(this S const&&) requires Constrain { return xobj_fn; }; + int f_xi_c1_refqual() const&& { return iobj_fn; }; + + // _r without ref qualifier + + // ix + int f_ix_m0_r() { return iobj_fn; }; + int f_ix_m0_r(this S&) requires Constrain { return xobj_fn; }; + // See note + int f_ix_m1_r() { return iobj_fn; }; + int f_ix_m1_r(this S&&) requires Constrain { return xobj_fn; }; + + int f_ix_c0_r() const { return iobj_fn; }; + int f_ix_c0_r(this S const&) requires Constrain { return xobj_fn; }; + // See note + int f_ix_c1_r() const { return iobj_fn; }; + int f_ix_c1_r(this S const&&) requires Constrain { return xobj_fn; }; + + // xi + int f_xi_m0_r(this S&) { return xobj_fn; }; + int f_xi_m0_r() requires Constrain { return iobj_fn; }; + // See note + int f_xi_m1_r(this S&&) { return xobj_fn; }; + int f_xi_m1_r() requires Constrain { return iobj_fn; }; + + int f_xi_c0_r(this S const&) { return xobj_fn; }; + int f_xi_c0_r() const requires Constrain { return iobj_fn; }; + // See note + int f_xi_c1_r(this S const&&) { return xobj_fn; }; + int f_xi_c1_r() const requires Constrain { return iobj_fn; }; + + // _r with ref qualifier + // ix + int f_ix_m0_refqual_r() & { return iobj_fn; }; + int f_ix_m0_refqual_r(this S&) requires Constrain { return xobj_fn; }; + + int f_ix_m1_refqual_r() && { return iobj_fn; }; + int f_ix_m1_refqual_r(this S&&) requires Constrain { return xobj_fn; }; + + int f_ix_c0_refqual_r() const& { return iobj_fn; }; + int f_ix_c0_refqual_r(this S const&) requires Constrain { return xobj_fn; }; + + int f_ix_c1_refqual_r() const&& { return iobj_fn; }; + int f_ix_c1_refqual_r(this S const&&) requires Constrain { return xobj_fn; }; + + // xi + int f_xi_m0_refqual_r(this S&) { return xobj_fn; }; + int f_xi_m0_refqual_r() & requires Constrain { return iobj_fn; }; + + int f_xi_m1_refqual_r(this S&&) { return xobj_fn; }; + int f_xi_m1_refqual_r() && requires Constrain { return iobj_fn; }; + + int f_xi_c0_refqual_r(this S const&) { return xobj_fn; }; + int f_xi_c0_refqual_r() const& requires Constrain { return iobj_fn; }; + + int f_xi_c1_refqual_r(this S const&&) { return xobj_fn; }; + int f_xi_c1_refqual_r() const&& requires Constrain { return iobj_fn; }; +}; + +int main() +{ + // The commented out cases are ambiguous, which is most likely the correct + // behavior. It is something that I want to propose to change, and I want + // to leave them in as they are a little weird. + // + // Furthermore, as the comment at the top of this file indicates, I am not + // clear on the correct behavior of the static/xobj cases in general. + using S = S; + S s{}; + if (s.f_xs_v () != xobj_fn) + __builtin_abort (); + if (s.f_xs_ref () != xobj_fn) + __builtin_abort (); + if (s.f_xs_cref () != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_xs_rref () != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_xs_crref () != xobj_fn) + __builtin_abort (); + // if (s.f_xs_dv () != xobj_fn) + // __builtin_abort (); + // if (s.f_xs_dcref () != xobj_fn) + // __builtin_abort (); + // if (s.f_xs_dfwdref () != xobj_fn) + // __builtin_abort (); + + if (s.f_xs_v_r () != static_fn) + __builtin_abort (); + if (s.f_xs_ref_r () != static_fn) + __builtin_abort (); + if (s.f_xs_cref_r () != static_fn) + __builtin_abort (); + if (static_cast(s).f_xs_rref_r () != static_fn) + __builtin_abort (); + if (static_cast(s).f_xs_crref_r () != static_fn) + __builtin_abort (); + // if (s.f_xs_dv_r () != static_fn) + // __builtin_abort (); + // if (s.f_xs_dcref_r () != static_fn) + // __builtin_abort (); + // if (s.f_xs_dfwdref_r () != static_fn) + // __builtin_abort (); + + if (s.f_sx_v () != static_fn) + __builtin_abort (); + if (s.f_sx_ref () != static_fn) + __builtin_abort (); + if (s.f_sx_cref () != static_fn) + __builtin_abort (); + if (static_cast(s).f_sx_rref () != static_fn) + __builtin_abort (); + if (static_cast(s).f_sx_crref () != static_fn) + __builtin_abort (); + // if (s.f_sx_dv () != static_fn) + // __builtin_abort (); + // if (s.f_sx_dcref () != static_fn) + // __builtin_abort (); + // if (s.f_sx_dfwdref () != static_fn) + // __builtin_abort (); + + if (s.f_sx_v_r () != xobj_fn) + __builtin_abort (); + if (s.f_sx_ref_r () != xobj_fn) + __builtin_abort (); + if (s.f_sx_cref_r () != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_sx_rref_r () != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_sx_crref_r () != xobj_fn) + __builtin_abort (); + // if (s.f_sx_dv_r () != xobj_fn) + // __builtin_abort (); + // if (s.f_sx_dcref_r () != xobj_fn) + // __builtin_abort (); + // if (s.f_sx_dfwdref_r () != xobj_fn) + // __builtin_abort (); + + // iobj/xobj + + // The commented out cases are tested below as their correct behavior is + // unintuitive, see the note below for details. + + if (s.f_ix_m0 () != iobj_fn) + __builtin_abort (); + // s.f_ix_m1 + if (s.f_ix_c0 () != iobj_fn) + __builtin_abort (); + // s.f_ix_c1 + if (s.f_xi_m0 () != xobj_fn) + __builtin_abort (); + // s.f_xi_m1 + if (s.f_xi_c0 () != xobj_fn) + __builtin_abort (); + // s.f_xi_c1 + if (s.f_ix_m0_refqual () != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_ix_m1_refqual () != iobj_fn) + __builtin_abort (); + if (s.f_ix_c0_refqual () != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_ix_c1_refqual () != iobj_fn) + __builtin_abort (); + if (s.f_xi_m0_refqual () != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_m1_refqual () != xobj_fn) + __builtin_abort (); + if (s.f_xi_c0_refqual () != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_c1_refqual () != xobj_fn) + __builtin_abort (); + if (s.f_ix_m0_r () != xobj_fn) + __builtin_abort (); + // s.f_ix_m1_r + if (s.f_ix_c0_r () != xobj_fn) + __builtin_abort (); + // s.f_ix_c1_r + if (s.f_xi_m0_r () != iobj_fn) + __builtin_abort (); + // s.f_xi_m1_r + if (s.f_xi_c0_r () != iobj_fn) + __builtin_abort (); + // s.f_xi_c1_r + if (s.f_ix_m0_refqual_r () != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_ix_m1_refqual_r () != xobj_fn) + __builtin_abort (); + if (s.f_ix_c0_refqual_r () != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_ix_c1_refqual_r () != xobj_fn) + __builtin_abort (); + if (s.f_xi_m0_refqual_r () != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_m1_refqual_r () != iobj_fn) + __builtin_abort (); + if (s.f_xi_c0_refqual_r () != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_c1_refqual_r () != iobj_fn) + __builtin_abort (); + + +/* Foreword: CWG2789 does not specifiy this correctly, it needs to be changed + to consider correspondence instead of "same type" or else the following + does not make sense. My implementation assumes correspondence should be + considered. + + Note: + These cases are weird, the object argument correspond, but are not the same + type ([basic.scope.scope-3.1]), so we get this funny edge case where the + constraint stops them from being considered redeclarations, but isn't taken + into account for the lvalue case. You can't bind an lvalue to an rvalue + reference so the iobj member function is always taken regardless of which + overload is constrained. + + [over.match.funcs.general-4] + For implicit object member functions, the type of the implicit object + parameter is + (4.1) “lvalue reference to cv X” for functions declared without a + ref-qualifier or with the & ref-qualifier + + You would think that calling these functions with an rvalue would be the + same then, always taking the xobj member function. However, for backwards + compatibility reasons, an unqualified member function can be called on an + object that is an rvalue. + + [over.match.funcs.general-5] + For implicit object member functions declared without a ref-qualifier, even + if the implicit object parameter is not const-qualified, an rvalue can be + bound to the parameter as long as in all other respects the argument can be + converted to the type of the implicit object parameter. + + And finally, since the object parameters correspond ([basic.scope.scope-3.1]) + the constraints are taken into account. + + So in conclusion, calling these functions with an lvalue always resolves to + the iobj member function, and calling them with rvalues take the constraints + into account. + + As wacky as this is, this is the correct behavior. */ + + // Always takes the iobj member function, can't bind an lvalue to an rvalue + // reference. + if (s.f_ix_m1 () != iobj_fn) + __builtin_abort (); + if (s.f_ix_c1 () != iobj_fn) + __builtin_abort (); + if (s.f_xi_m1 () != iobj_fn) + __builtin_abort (); + if (s.f_xi_c1 () != iobj_fn) + __builtin_abort (); + + if (s.f_ix_m1_r () != iobj_fn) + __builtin_abort (); + if (s.f_ix_c1_r () != iobj_fn) + __builtin_abort (); + if (s.f_xi_m1_r () != iobj_fn) + __builtin_abort (); + if (s.f_xi_c1_r () != iobj_fn) + __builtin_abort (); + + // Constraints are taken into account here, see note for more information. + if (static_cast(s).f_ix_m1 () != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_ix_c1 () != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_m1 () != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_c1 () != xobj_fn) + __builtin_abort (); + + if (static_cast(s).f_ix_m1_r () != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_ix_c1_r () != xobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_m1_r () != iobj_fn) + __builtin_abort (); + if (static_cast(s).f_xi_c1_r () != iobj_fn) + __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-arrow.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-arrow.C new file mode 100644 index 0000000..eb86077 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-arrow.C @@ -0,0 +1,28 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// uses of member only operators (arrow) + +struct S { + int _v; + S* operator->(this S& self) { return &self; } +}; + +void non_dep() +{ + S s{}; + (void)s->_v; +} + +template +void dependent() +{ + S s{}; + (void)s->_v; +} + +void call() +{ + dependent(); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-assignment.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-assignment.C new file mode 100644 index 0000000..bb43a0a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-assignment.C @@ -0,0 +1,27 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// uses of member only operators (assignment) + +struct S { + void operator=(this S&, int) {} +}; + +void non_dep() +{ + S s{}; + s = 0; +} + +template +void dependent() +{ + S s{}; + s = 0; +} + +void call() +{ + dependent(); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-call.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-call.C new file mode 100644 index 0000000..ecd6bdf --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-call.C @@ -0,0 +1,40 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// uses of member only operators (call op) + +// execution paths for subscript with 1 argument and 0 and 2+ arguments are different +// just to be safe, also test 0 and 2 argument cases here too + +struct S { + void operator()(this S&) {} + void operator()(this S&, int) {} + void operator()(this S&, int, int) {} + template + void operator()(this S&, Args... args) {} +}; + +void non_dep() +{ + S s{}; + s(); + s(0); + s(0, 0); + s(0, 0, 0); +} + +template +void dependent() +{ + S s{}; + s(); + s(0); + s(0, 0); + s(0, 0, 0); +} + +void call() +{ + dependent(); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-subscript.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-subscript.C new file mode 100644 index 0000000..3eb0030 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-subscript.C @@ -0,0 +1,40 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// uses of member only operators (subscript) + +// execution paths for subscript with 1 argument and 0 and 2+ arguments are different +// therefore we should additionally test the 0 and 2 argument cases as well + +struct S { + void operator[](this S&) {} + void operator[](this S&, int) {} + void operator[](this S&, int, int) {} + template + void operator[](this S&, Args... args) {} +}; + +void non_dep() +{ + S s{}; + s[]; + s[0]; + s[0, 0]; + s[0, 0, 0]; +} + +template +void dependent() +{ + S s{}; + s[]; + s[0]; + s[0, 0]; + s[0, 0, 0]; +} + +void call() +{ + dependent(); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C new file mode 100644 index 0000000..f38615e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C @@ -0,0 +1,58 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// operators that are not required to be members +// called in a dependent context (as non dependent exprs) +// see header +#include "explicit-obj-ops-non-mem.h" + +// noop, indicates which versions are ill-formed +// I could not find a way to test the invalid cases +// without requires expressions +#define TEST_INVALID(X) + +template +void do_calls() +{ + Value value{}; + TEST_OPS(value) + TEST_OPS(static_cast(value)) + TEST_OPS(static_cast(value)) + TEST_OPS(static_cast(value)) + + LRef l_ref{}; + TEST_OPS(l_ref) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) + + RRef r_ref{}; + TEST_INVALID(r_ref) + TEST_OPS(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) + + ConstLRef const_l_ref{}; + TEST_OPS(const_l_ref) + TEST_OPS(static_cast(const_l_ref)) + TEST_OPS(static_cast(const_l_ref)) + TEST_OPS(static_cast(const_l_ref)) + + ConstRRef const_r_ref{}; + TEST_INVALID(const_r_ref) + TEST_OPS(static_cast(const_r_ref)) + TEST_INVALID(static_cast(const_r_ref)) + TEST_OPS(static_cast(const_r_ref)) + + Deduced deduced{}; + TEST_OPS(deduced) + TEST_OPS(static_cast(deduced)) + TEST_OPS(static_cast(deduced)) + TEST_OPS(static_cast(deduced)) + + VALIDATE_RETURN_TYPES(deduced, Deduced&) + VALIDATE_RETURN_TYPES(static_cast(deduced), Deduced&&) + VALIDATE_RETURN_TYPES(static_cast(deduced), Deduced const&) + VALIDATE_RETURN_TYPES(static_cast(deduced), Deduced const&&) +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C new file mode 100644 index 0000000..634e878 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C @@ -0,0 +1,57 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// operators that are not required to be members +// called in a non-dependent context +// see header +#include "explicit-obj-ops-non-mem.h" + +// noop, indicates which versions are ill-formed +// I could not find a way to test the invalid cases +// without requires expressions +#define TEST_INVALID(X) + +void do_calls() +{ + Value value{}; + TEST_OPS(value) + TEST_OPS(static_cast(value)) + TEST_OPS(static_cast(value)) + TEST_OPS(static_cast(value)) + + LRef l_ref{}; + TEST_OPS(l_ref) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) + + RRef r_ref{}; + TEST_INVALID(r_ref) + TEST_OPS(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) + + ConstLRef const_l_ref{}; + TEST_OPS(const_l_ref) + TEST_OPS(static_cast(const_l_ref)) + TEST_OPS(static_cast(const_l_ref)) + TEST_OPS(static_cast(const_l_ref)) + + ConstRRef const_r_ref{}; + TEST_INVALID(const_r_ref) + TEST_OPS(static_cast(const_r_ref)) + TEST_INVALID(static_cast(const_r_ref)) + TEST_OPS(static_cast(const_r_ref)) + + Deduced deduced{}; + TEST_OPS(deduced) + TEST_OPS(static_cast(deduced)) + TEST_OPS(static_cast(deduced)) + TEST_OPS(static_cast(deduced)) + + VALIDATE_RETURN_TYPES(deduced, Deduced&) + VALIDATE_RETURN_TYPES(static_cast(deduced), Deduced&&) + VALIDATE_RETURN_TYPES(static_cast(deduced), Deduced const&) + VALIDATE_RETURN_TYPES(static_cast(deduced), Deduced const&&) +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem.h b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem.h new file mode 100644 index 0000000..b897a4b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem.h @@ -0,0 +1,210 @@ +// tests for ops that must be member functions are seperate + +// the name of the class refers to the type of it's member functions xobj parameter + +#define MAKE_STRUCT_OPS(TYPE) \ + TYPE operator+=(this TYPE self, int) { return self; } \ + TYPE operator-=(this TYPE self, int) { return self; } \ + TYPE operator*=(this TYPE self, int) { return self; } \ + TYPE operator/=(this TYPE self, int) { return self; } \ + TYPE operator%=(this TYPE self, int) { return self; } \ + TYPE operator&=(this TYPE self, int) { return self; } \ + TYPE operator|=(this TYPE self, int) { return self; } \ + TYPE operator^=(this TYPE self, int) { return self; } \ + TYPE operator<<=(this TYPE self, int) { return self; } \ + TYPE operator>>=(this TYPE self, int) { return self; } \ + TYPE operator++(this TYPE self) { return self; } \ + TYPE operator--(this TYPE self) { return self; } \ + TYPE operator++(this TYPE self, int) { return self; } \ + TYPE operator--(this TYPE self, int) { return self; } \ + TYPE operator+(this TYPE self) { return self; } \ + TYPE operator-(this TYPE self) { return self; } \ + TYPE operator+(this TYPE self, int) { return self; } \ + TYPE operator-(this TYPE self, int) { return self; } \ + TYPE operator*(this TYPE self, int) { return self; } \ + TYPE operator/(this TYPE self, int) { return self; } \ + TYPE operator%(this TYPE self, int) { return self; } \ + TYPE operator&(this TYPE self, int) { return self; } \ + TYPE operator|(this TYPE self, int) { return self; } \ + TYPE operator^(this TYPE self, int) { return self; } \ + TYPE operator<<(this TYPE self, int) { return self; } \ + TYPE operator>>(this TYPE self, int) { return self; } \ + TYPE operator!(this TYPE self) { return self; } \ + TYPE operator&&(this TYPE self, int const&) { return self; } \ + TYPE operator||(this TYPE self, int const&) { return self; } \ + TYPE operator==(this TYPE self, int) { return self; } \ + TYPE operator!=(this TYPE self, int) { return self; } \ + TYPE operator<(this TYPE self, int) { return self; } \ + TYPE operator>(this TYPE self, int) { return self; } \ + TYPE operator<=(this TYPE self, int) { return self; } \ + TYPE operator>=(this TYPE self, int) { return self; } \ + TYPE operator<=>(this TYPE self, int) { return self; } \ + TYPE operator*(this TYPE self) { return self; } \ + TYPE operator->*(this TYPE self, int) { return self; } \ + TYPE operator&(this TYPE self) { return self; } \ + TYPE operator,(this TYPE self, int) { return self; } + +struct Value { + MAKE_STRUCT_OPS (Value) +}; + +struct LRef { + MAKE_STRUCT_OPS (LRef&) +}; + +struct RRef { + MAKE_STRUCT_OPS (RRef&&) +}; + +struct ConstLRef { + MAKE_STRUCT_OPS (ConstLRef const&) +}; + +struct ConstRRef { + MAKE_STRUCT_OPS (ConstRRef const&&) +}; + +#undef MAKE_STRUCT_OPS + +struct Deduced { + template Self&& operator+=(this Self&& self, int) { return static_cast(self); } + template Self&& operator-=(this Self&& self, int) { return static_cast(self); } + template Self&& operator*=(this Self&& self, int) { return static_cast(self); } + template Self&& operator/=(this Self&& self, int) { return static_cast(self); } + template Self&& operator%=(this Self&& self, int) { return static_cast(self); } + template Self&& operator&=(this Self&& self, int) { return static_cast(self); } + template Self&& operator|=(this Self&& self, int) { return static_cast(self); } + template Self&& operator^=(this Self&& self, int) { return static_cast(self); } + template Self&& operator<<=(this Self&& self, int) { return static_cast(self); } + template Self&& operator>>=(this Self&& self, int) { return static_cast(self); } + + template Self&& operator++(this Self&& self) { return static_cast(self); } + template Self&& operator--(this Self&& self) { return static_cast(self); } + template Self&& operator++(this Self&& self, int) { return static_cast(self); } + template Self&& operator--(this Self&& self, int) { return static_cast(self); } + + template Self&& operator+(this Self&& self) { return static_cast(self); } + template Self&& operator-(this Self&& self) { return static_cast(self); } + template Self&& operator+(this Self&& self, int) { return static_cast(self); } + template Self&& operator-(this Self&& self, int) { return static_cast(self); } + template Self&& operator*(this Self&& self, int) { return static_cast(self); } + template Self&& operator/(this Self&& self, int) { return static_cast(self); } + template Self&& operator%(this Self&& self, int) { return static_cast(self); } + template Self&& operator&(this Self&& self, int) { return static_cast(self); } + template Self&& operator|(this Self&& self, int) { return static_cast(self); } + template Self&& operator^(this Self&& self, int) { return static_cast(self); } + template Self&& operator<<(this Self&& self, int) { return static_cast(self); } + template Self&& operator>>(this Self&& self, int) { return static_cast(self); } + + template Self&& operator!(this Self&& self) { return static_cast(self); } + template Self&& operator&&(this Self&& self, int const&) { return static_cast(self); } + template Self&& operator||(this Self&& self, int const&) { return static_cast(self); } + + template Self&& operator==(this Self&& self, int) { return static_cast(self); } + template Self&& operator!=(this Self&& self, int) { return static_cast(self); } + template Self&& operator<(this Self&& self, int) { return static_cast(self); } + template Self&& operator>(this Self&& self, int) { return static_cast(self); } + template Self&& operator<=(this Self&& self, int) { return static_cast(self); } + template Self&& operator>=(this Self&& self, int) { return static_cast(self); } + template Self&& operator<=>(this Self&& self, int) { return static_cast(self); } + + template Self&& operator*(this Self&& self) { return static_cast(self); } + template Self&& operator->*(this Self&& self, int) { return static_cast(self); } + template Self&& operator&(this Self&& self) { return static_cast(self); } + template Self&& operator,(this Self&& self, int) { return static_cast(self); } +}; + +#define TEST_OPS(OPERAND) \ + (OPERAND) += 0; \ + (OPERAND) -= 0; \ + (OPERAND) *= 0; \ + (OPERAND) /= 0; \ + (OPERAND) %= 0; \ + (OPERAND) &= 0; \ + (OPERAND) |= 0; \ + (OPERAND) ^= 0; \ + (OPERAND) <<= 0; \ + (OPERAND) >>= 0; \ + \ + ++(OPERAND); \ + --(OPERAND); \ + (OPERAND)++; \ + (OPERAND)--; \ + \ + +(OPERAND); \ + -(OPERAND); \ + (OPERAND) + 0; \ + (OPERAND) - 0; \ + (OPERAND) * 0; \ + (OPERAND) / 0; \ + (OPERAND) % 0; \ + (OPERAND) & 0; \ + (OPERAND) | 0; \ + (OPERAND) ^ 0; \ + (OPERAND) << 0; \ + (OPERAND) >> 0; \ + \ + !(OPERAND); \ + (OPERAND) && 0; \ + (OPERAND) || 0; \ + \ + (OPERAND) == 0; \ + (OPERAND) != 0; \ + (OPERAND) < 0; \ + (OPERAND) > 0; \ + (OPERAND) <= 0; \ + (OPERAND) >= 0; \ + (OPERAND) <=> 0; \ + \ + *(OPERAND); \ + (OPERAND) ->* 0; \ + &(OPERAND); \ + (OPERAND), 0; + +#define VALIDATE_RETURN_TYPES(OPERAND, CORRECT_TYPE) \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) += 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) -= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) *= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) /= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) %= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) &= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) |= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) ^= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) <<= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) >>= 0))); \ + \ + static_assert(__is_same(CORRECT_TYPE, decltype(++(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype(--(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND)++))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND)--))); \ + \ + static_assert(__is_same(CORRECT_TYPE, decltype(+(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype(-(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) + 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) - 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) * 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) / 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) % 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) & 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) | 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) ^ 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) << 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) >> 0))); \ + \ + static_assert(__is_same(CORRECT_TYPE, decltype(!(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) && 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) || 0))); \ + \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) == 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) != 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) < 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) > 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) <= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) >= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) <=> 0))); \ + \ + static_assert(__is_same(CORRECT_TYPE, decltype(*(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) ->* 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype(&(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND), 0))); + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-mem.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-mem.C new file mode 100644 index 0000000..d08e938 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-mem.C @@ -0,0 +1,171 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// well-formed and ill-formed uses of member only operators in a requires expression + +// suppress the warning for Value's arrow operator +// { dg-options "-Wno-return-local-addr" } + +// It's very hard to test for incorrect successes without requires, and by extension a non dependent variable +// so for the time being, there are no non dependent tests invalid calls. + +struct Value { + int _v; + Value operator=(this Value self, int) { return self; } + Value operator()(this Value self) { return self; } + Value operator[](this Value self) { return self; } + Value* operator->(this Value self) { return &self; } +}; + +struct LRef { + int _v; + LRef& operator=(this LRef& self, int) { return self; } + LRef& operator()(this LRef& self) { return self; } + LRef& operator[](this LRef& self) { return self; } + LRef* operator->(this LRef& self) { return &self; } +}; + +struct RRef { + int _v; + RRef&& operator=(this RRef&& self, int) { return static_cast(self); } + RRef&& operator()(this RRef&& self) { return static_cast(self); } + RRef&& operator[](this RRef&& self) { return static_cast(self); } + RRef* operator->(this RRef&& self) { return &self; } +}; + +struct ConstLRef { + int _v; + ConstLRef const& operator=(this ConstLRef const& self, int) { return self; } + ConstLRef const& operator()(this ConstLRef const& self) { return self; } + ConstLRef const& operator[](this ConstLRef const& self) { return self; } + ConstLRef const* operator->(this ConstLRef const& self) { return &self; } +}; + +struct ConstRRef { + int _v; + ConstRRef const&& operator=(this ConstRRef const&& self, int) { return static_cast(self); } + ConstRRef const&& operator()(this ConstRRef const&& self) { return static_cast(self); } + ConstRRef const&& operator[](this ConstRRef const&& self) { return static_cast(self); } + ConstRRef const* operator->(this ConstRRef const&& self) { return &self; } +}; + +// needed to implement deduced operator-> +template struct remove_ref { using type = T; }; +template struct remove_ref { using type = T; }; +template struct remove_ref { using type = T; }; +template using remove_ref_t = typename remove_ref::type; + +struct Deduced { + int _v; + template + Self&& operator=(this Self&& self, int) { return static_cast(self); } + template + Self&& operator()(this Self&& self) { return static_cast(self); } + template + Self&& operator[](this Self&& self) { return static_cast(self); } + template + remove_ref_t* operator->(this Self&& self) { return &self; } +}; + +#define TEST_INVALID(OPERAND) \ + static_assert(!requires{ (OPERAND) = 0; }, "Unexpected success calling operator = with " #OPERAND); \ + static_assert(!requires{ (OPERAND)(); }, "Unexpected success calling operator () with " #OPERAND); \ + static_assert(!requires{ (OPERAND)[]; }, "Unexpected success calling operator [] with " #OPERAND); \ + static_assert(!requires{ (OPERAND)->_v; }, "Unexpected success calling operator -> with " #OPERAND); + +#define TEST_VALID(OPERAND) \ + static_assert(requires{ (OPERAND) = 0; }, "Unexpected failure calling operator = with " #OPERAND); \ + static_assert(requires{ (OPERAND)(); }, "Unexpected failure calling operator () with " #OPERAND); \ + static_assert(requires{ (OPERAND)[]; }, "Unexpected failure calling operator [] with " #OPERAND); \ + static_assert(requires{ (OPERAND)->_v; }, "Unexpected failure calling operator -> with " #OPERAND); + +template +concept same_as = __is_same(T, U); + +#define TEST_VALID_WITH_RETURN_TYPES(OPERAND, CORRECT_TYPE) \ + static_assert(requires{ {(OPERAND) = 0} -> same_as; },"Unexpected failure with return type check calling operator = with " #OPERAND " -> expected return type: " #CORRECT_TYPE); \ + static_assert(requires{ {(OPERAND)()} -> same_as; }, "Unexpected failure with return type check calling operator () with " #OPERAND " -> expected return type: " #CORRECT_TYPE); \ + static_assert(requires{ {(OPERAND)[]} -> same_as; }, "Unexpected failure with return type check calling operator [] with " #OPERAND " -> expected return type: " #CORRECT_TYPE); + + +template +void test_value() +{ + DepValue value{}; + TEST_VALID(value) + TEST_VALID(static_cast(value)) + TEST_VALID(static_cast(value)) + TEST_VALID(static_cast(value)) +} + +template +void test_l_ref() +{ + DepLRef l_ref{}; + TEST_VALID(l_ref) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) +} + +template +void test_r_ref() +{ + DepRRef r_ref{}; + TEST_INVALID(r_ref) + TEST_VALID(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) +} + +template +void test_const_l_ref() +{ + DepConstLRef const_l_ref{}; + TEST_VALID(const_l_ref) + TEST_VALID(static_cast(const_l_ref)) + TEST_VALID(static_cast(const_l_ref)) + TEST_VALID(static_cast(const_l_ref)) +} + +template +void test_const_r_ref() +{ + DepConstRRef const_r_ref{}; + TEST_INVALID(const_r_ref) + TEST_VALID(static_cast(const_r_ref)) + TEST_INVALID(static_cast(const_r_ref)) + TEST_VALID(static_cast(const_r_ref)) +} + +template +void test_deduced() +{ + DepDeduced deduced{}; + + TEST_VALID(deduced) + TEST_VALID(static_cast(deduced)) + TEST_VALID(static_cast(deduced)) + TEST_VALID(static_cast(deduced)) + + TEST_VALID_WITH_RETURN_TYPES(deduced, DepDeduced&) + TEST_VALID_WITH_RETURN_TYPES(static_cast(deduced), DepDeduced&&) + TEST_VALID_WITH_RETURN_TYPES(static_cast(deduced), DepDeduced const&) + TEST_VALID_WITH_RETURN_TYPES(static_cast(deduced), DepDeduced const&&) + // arrow operator needs to be seperate to check the type of _v + static_assert(requires{ {(deduced->_v)} -> same_as; }, "Unexpected failure with return type check calling operator -> with deduced->_v"); + static_assert(requires{ {(static_cast(deduced)->_v)} -> same_as; }, "Unexpected failure with return type check calling operator -> with static_cast(deduced)->_v"); + static_assert(requires{ {(static_cast(deduced)->_v)} -> same_as; }, "Unexpected failure with return type check calling operator -> with static_cast(deduced)->_v"); + static_assert(requires{ {(static_cast(deduced)->_v)} -> same_as; }, "Unexpected failure with return type check calling operator -> with static_cast(deduced)->_v"); +} + +void test() +{ + test_value(); + test_l_ref(); + test_r_ref(); + test_const_l_ref(); + test_const_r_ref(); + test_deduced(); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C new file mode 100644 index 0000000..865b1f5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C @@ -0,0 +1,237 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// well-formed and ill-formed uses of non-member capable operators in a requires expression + +#include "explicit-obj-ops-non-mem.h" + +// we only need the structs from the header +#undef TEST_OPS +#undef VALIDATE_RETURN_TYPES + +// It's very hard to test for incorrect successes without requires, and by extension a non dependent variable +// so for the time being, there are no non dependent tests invalid calls. + +template +concept same_as = __is_same(T, U); + +#define TEST_INVALID(OPERAND, CORRECT_TYPE) \ + static_assert(!requires{ (OPERAND) += 0; }, "Unexpected success calling operator += with " #OPERAND); \ + static_assert(!requires{ (OPERAND) -= 0; }, "Unexpected success calling operator -= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) *= 0; }, "Unexpected success calling operator *= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) /= 0; }, "Unexpected success calling operator /= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) %= 0; }, "Unexpected success calling operator %= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) &= 0; }, "Unexpected success calling operator &= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) |= 0; }, "Unexpected success calling operator |= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) ^= 0; }, "Unexpected success calling operator ^= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) <<= 0; }, "Unexpected success calling operator <<= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) >>= 0; }, "Unexpected success calling operator >>= with " #OPERAND); \ + \ + static_assert(!requires{ ++(OPERAND); }, "Unexpected success calling operator pre++ with " #OPERAND); \ + static_assert(!requires{ --(OPERAND); }, "Unexpected success calling operator pre-- with " #OPERAND); \ + static_assert(!requires{ (OPERAND)++; }, "Unexpected success calling operator post++ with " #OPERAND); \ + static_assert(!requires{ (OPERAND)--; }, "Unexpected success calling operator post-- with " #OPERAND); \ + \ + static_assert(!requires{ +(OPERAND); }, "Unexpected success calling operator unary+ with " #OPERAND); \ + static_assert(!requires{ -(OPERAND); }, "Unexpected success calling operator unary- with " #OPERAND); \ + static_assert(!requires{ (OPERAND) + 0; }, "Unexpected success calling operator binary+ with " #OPERAND); \ + static_assert(!requires{ (OPERAND) - 0; }, "Unexpected success calling operator binary- with " #OPERAND); \ + static_assert(!requires{ (OPERAND) * 0; }, "Unexpected success calling operator binary* with " #OPERAND); \ + static_assert(!requires{ (OPERAND) / 0; }, "Unexpected success calling operator / with " #OPERAND); \ + static_assert(!requires{ (OPERAND) % 0; }, "Unexpected success calling operator % with " #OPERAND); \ + static_assert(!requires{ (OPERAND) & 0; }, "Unexpected success calling operator binary& with " #OPERAND); \ + static_assert(!requires{ (OPERAND) | 0; }, "Unexpected success calling operator | with " #OPERAND); \ + static_assert(!requires{ (OPERAND) ^ 0; }, "Unexpected success calling operator ^ with " #OPERAND); \ + static_assert(!requires{ (OPERAND) << 0; }, "Unexpected success calling operator << with " #OPERAND); \ + static_assert(!requires{ (OPERAND) >> 0; }, "Unexpected success calling operator >> with " #OPERAND); \ + \ + static_assert(!requires{ !(OPERAND); }, "Unexpected success calling operator ! with " #OPERAND); \ + static_assert(!requires{ (OPERAND) && 0; }, "Unexpected success calling operator && with " #OPERAND); \ + static_assert(!requires{ (OPERAND) || 0; }, "Unexpected success calling operator || with " #OPERAND); \ + \ + static_assert(!requires{ (OPERAND) == 0; }, "Unexpected success calling operator == with " #OPERAND); \ + static_assert(!requires{ (OPERAND) != 0; }, "Unexpected success calling operator != with " #OPERAND); \ + static_assert(!requires{ (OPERAND) < 0; }, "Unexpected success calling operator < with " #OPERAND); \ + static_assert(!requires{ (OPERAND) > 0; }, "Unexpected success calling operator > with " #OPERAND); \ + static_assert(!requires{ (OPERAND) <= 0; }, "Unexpected success calling operator <= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) >= 0; }, "Unexpected success calling operator >= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) <=> 0; }, "Unexpected success calling operator <=> with " #OPERAND); \ + \ + static_assert(!requires{ *(OPERAND); }, "Unexpected success calling operator unary* with " #OPERAND); \ + static_assert(!requires{ (OPERAND) ->* 0; }, "Unexpected success calling operator ->* with " #OPERAND); \ + /* We need to check the return type to confirm the built-in operator was not selected. */ \ + static_assert(!requires{ {&(OPERAND)} -> same_as; }, \ + "Unexpected success calling operator unary& with " #OPERAND); \ + static_assert(!requires{ {(OPERAND), 0} -> same_as; }, \ + "Unexpected success calling operator , with " #OPERAND); + +#define TEST_VALID(OPERAND, CORRECT_TYPE) \ + static_assert(requires{ (OPERAND) += 0; }, "Unexpected failure calling operator += with " #OPERAND); \ + static_assert(requires{ (OPERAND) -= 0; }, "Unexpected failure calling operator -= with " #OPERAND); \ + static_assert(requires{ (OPERAND) *= 0; }, "Unexpected failure calling operator *= with " #OPERAND); \ + static_assert(requires{ (OPERAND) /= 0; }, "Unexpected failure calling operator /= with " #OPERAND); \ + static_assert(requires{ (OPERAND) %= 0; }, "Unexpected failure calling operator %= with " #OPERAND); \ + static_assert(requires{ (OPERAND) &= 0; }, "Unexpected failure calling operator &= with " #OPERAND); \ + static_assert(requires{ (OPERAND) |= 0; }, "Unexpected failure calling operator |= with " #OPERAND); \ + static_assert(requires{ (OPERAND) ^= 0; }, "Unexpected failure calling operator ^= with " #OPERAND); \ + static_assert(requires{ (OPERAND) <<= 0; }, "Unexpected failure calling operator <<= with " #OPERAND); \ + static_assert(requires{ (OPERAND) >>= 0; }, "Unexpected failure calling operator >>= with " #OPERAND); \ + \ + static_assert(requires{ ++(OPERAND); }, "Unexpected failure calling operator pre++ with " #OPERAND); \ + static_assert(requires{ --(OPERAND); }, "Unexpected failure calling operator pre-- with " #OPERAND); \ + static_assert(requires{ (OPERAND)++; }, "Unexpected failure calling operator post++ with " #OPERAND); \ + static_assert(requires{ (OPERAND)--; }, "Unexpected failure calling operator post-- with " #OPERAND); \ + \ + static_assert(requires{ +(OPERAND); }, "Unexpected failure calling operator unary+ with " #OPERAND); \ + static_assert(requires{ -(OPERAND); }, "Unexpected failure calling operator unary- with " #OPERAND); \ + static_assert(requires{ (OPERAND) + 0; }, "Unexpected failure calling operator binary+ with " #OPERAND); \ + static_assert(requires{ (OPERAND) - 0; }, "Unexpected failure calling operator binary- with " #OPERAND); \ + static_assert(requires{ (OPERAND) * 0; }, "Unexpected failure calling operator binary* with " #OPERAND); \ + static_assert(requires{ (OPERAND) / 0; }, "Unexpected failure calling operator / with " #OPERAND); \ + static_assert(requires{ (OPERAND) % 0; }, "Unexpected failure calling operator % with " #OPERAND); \ + static_assert(requires{ (OPERAND) & 0; }, "Unexpected failure calling operator binary& with " #OPERAND); \ + static_assert(requires{ (OPERAND) | 0; }, "Unexpected failure calling operator | with " #OPERAND); \ + static_assert(requires{ (OPERAND) ^ 0; }, "Unexpected failure calling operator ^ with " #OPERAND); \ + static_assert(requires{ (OPERAND) << 0; }, "Unexpected failure calling operator << with " #OPERAND); \ + static_assert(requires{ (OPERAND) >> 0; }, "Unexpected failure calling operator >> with " #OPERAND); \ + \ + static_assert(requires{ !(OPERAND); }, "Unexpected failure calling operator ! with " #OPERAND); \ + static_assert(requires{ (OPERAND) && 0; }, "Unexpected failure calling operator && with " #OPERAND); \ + static_assert(requires{ (OPERAND) || 0; }, "Unexpected failure calling operator || with " #OPERAND); \ + \ + static_assert(requires{ (OPERAND) == 0; }, "Unexpected failure calling operator == with " #OPERAND); \ + static_assert(requires{ (OPERAND) != 0; }, "Unexpected failure calling operator != with " #OPERAND); \ + static_assert(requires{ (OPERAND) < 0; }, "Unexpected failure calling operator < with " #OPERAND); \ + static_assert(requires{ (OPERAND) > 0; }, "Unexpected failure calling operator > with " #OPERAND); \ + static_assert(requires{ (OPERAND) <= 0; }, "Unexpected failure calling operator <= with " #OPERAND); \ + static_assert(requires{ (OPERAND) >= 0; }, "Unexpected failure calling operator >= with " #OPERAND); \ + static_assert(requires{ (OPERAND) <=> 0; }, "Unexpected failure calling operator <=> with " #OPERAND); \ + \ + static_assert(requires{ *(OPERAND); }, "Unexpected failure calling operator unary* with " #OPERAND); \ + static_assert(requires{ (OPERAND) ->* 0; }, "Unexpected failure calling operator ->* with " #OPERAND); \ + /* We need to check the return type to confirm we selected our overload, not the built-in operator. */ \ + static_assert(requires{ {&(OPERAND)} -> same_as; }, \ + "Unexpected failure calling operator unary& with " #OPERAND); \ + static_assert(requires{ {(OPERAND), 0} -> same_as; }, \ + "Unexpected failure calling operator , with " #OPERAND); + +// Return types need to be tested for the deduced case + +#define TEST_VALID_WITH_RETURN_TYPES(OPERAND, CORRECT_TYPE) \ + static_assert(requires{ {(OPERAND) += 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) -= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) *= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) /= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) %= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) &= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) |= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) ^= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) <<= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) >>= 0} -> same_as; }); \ + \ + static_assert(requires{ {++(OPERAND)} -> same_as; }); \ + static_assert(requires{ {--(OPERAND)} -> same_as; }); \ + static_assert(requires{ {(OPERAND)++} -> same_as; }); \ + static_assert(requires{ {(OPERAND)--} -> same_as; }); \ + \ + static_assert(requires{ {+(OPERAND)} -> same_as; }); \ + static_assert(requires{ {-(OPERAND)} -> same_as; }); \ + static_assert(requires{ {(OPERAND) + 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) - 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) * 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) / 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) % 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) & 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) | 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) ^ 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) << 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) >> 0} -> same_as; }); \ + \ + static_assert(requires{ {!(OPERAND)} -> same_as; }); \ + static_assert(requires{ {(OPERAND) && 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) || 0} -> same_as; }); \ + \ + static_assert(requires{ {(OPERAND) == 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) != 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) < 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) > 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) <= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) >= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) <=> 0} -> same_as; }); \ + \ + static_assert(requires{ {*(OPERAND)} -> same_as; }); \ + static_assert(requires{ {(OPERAND) ->* 0} -> same_as; }); \ + static_assert(requires{ {&(OPERAND)} -> same_as; }); \ + static_assert(requires{ {(OPERAND), 0} -> same_as; }); + +template +void test_value() +{ + DepValue value{}; + TEST_VALID(value, DepValue) + TEST_VALID(static_cast(value), DepValue) + TEST_VALID(static_cast(value), DepValue) + TEST_VALID(static_cast(value), DepValue) +} + +template +void test_l_ref() +{ + DepLRef l_ref{}; + TEST_VALID(l_ref, DepLRef&) + TEST_INVALID(static_cast(l_ref), DepLRef&) + TEST_INVALID(static_cast(l_ref), DepLRef&) + TEST_INVALID(static_cast(l_ref), DepLRef&) +} + +template +void test_r_ref() +{ + DepRRef r_ref{}; + TEST_INVALID(r_ref, DepRRef&&) + TEST_VALID(static_cast(r_ref), DepRRef&&) + TEST_INVALID(static_cast(r_ref), DepRRef&&) + TEST_INVALID(static_cast(r_ref), DepRRef&&) +} + +template +void test_const_l_ref() +{ + DepConstLRef const_l_ref{}; + TEST_VALID(const_l_ref, DepConstLRef const&) + TEST_VALID(static_cast(const_l_ref), DepConstLRef const&) + TEST_VALID(static_cast(const_l_ref), DepConstLRef const&) + TEST_VALID(static_cast(const_l_ref), DepConstLRef const&) +} + +template +void test_const_r_ref() +{ + DepConstRRef const_r_ref{}; + TEST_INVALID(const_r_ref, DepConstRRef const&&) + TEST_VALID(static_cast(const_r_ref), DepConstRRef const&&) + TEST_INVALID(static_cast(const_r_ref), DepConstRRef const&&) + TEST_VALID(static_cast(const_r_ref), DepConstRRef const&&) +} + +template +void test_deduced() +{ + DepDeduced deduced{}; + + TEST_VALID_WITH_RETURN_TYPES(deduced, DepDeduced&) + TEST_VALID_WITH_RETURN_TYPES(static_cast(deduced), DepDeduced&&) + TEST_VALID_WITH_RETURN_TYPES(static_cast(deduced), DepDeduced const&) + TEST_VALID_WITH_RETURN_TYPES(static_cast(deduced), DepDeduced const&&) +} + +void test() +{ + test_value(); + test_l_ref(); + test_r_ref(); + test_const_l_ref(); + test_const_r_ref(); + test_deduced(); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl.C new file mode 100644 index 0000000..7fcfcc3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl.C @@ -0,0 +1,246 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// redeclarations of xobj/iobj member functions where the iobj member function +// is not ref qualified + +// it does not make sense to check for the inverse in this test (7 iobj, 1 xobj) +// because you are not allowed to overload iobj member functions without ref qualifiers +// with iobj member functions that do (and vice versa) + +// iobj first + +struct S0 { + void f0(); // { dg-note "previous declaration" } + void f0(this S0 &&); // { dg-error "cannot be overloaded with" } + void f0(this S0 const&); // { dg-bogus "" } + void f0(this S0 const&&); // { dg-bogus "" } + void f0(this S0 volatile&); // { dg-bogus "" } + void f0(this S0 volatile&&); // { dg-bogus "" } + void f0(this S0 const volatile&); // { dg-bogus "" } + void f0(this S0 const volatile&&); // { dg-bogus "" } + + void f1(); // { dg-note "previous declaration" } + void f1(this S0 &); // { dg-error "cannot be overloaded with" } + void f1(this S0 const&); // { dg-bogus "" } + void f1(this S0 const&&); // { dg-bogus "" } + void f1(this S0 volatile&); // { dg-bogus "" } + void f1(this S0 volatile&&); // { dg-bogus "" } + void f1(this S0 const volatile&); // { dg-bogus "" } + void f1(this S0 const volatile&&); // { dg-bogus "" } + + void fc0() const; // { dg-note "previous declaration" } + void fc0(this S0 &); // { dg-bogus "" } + void fc0(this S0 &&); // { dg-bogus "" } + void fc0(this S0 const&&); // { dg-error "cannot be overloaded with" } + void fc0(this S0 volatile&); // { dg-bogus "" } + void fc0(this S0 volatile&&); // { dg-bogus "" } + void fc0(this S0 const volatile&); // { dg-bogus "" } + void fc0(this S0 const volatile&&); // { dg-bogus "" } + + void fc1() const; // { dg-note "previous declaration" } + void fc1(this S0 &); // { dg-bogus "" } + void fc1(this S0 &&); // { dg-bogus "" } + void fc1(this S0 const&); // { dg-error "cannot be overloaded with" } + void fc1(this S0 volatile&); // { dg-bogus "" } + void fc1(this S0 volatile&&); // { dg-bogus "" } + void fc1(this S0 const volatile&); // { dg-bogus "" } + void fc1(this S0 const volatile&&); // { dg-bogus "" } + + void fv0() volatile; // { dg-note "previous declaration" } + void fv0(this S0 &); // { dg-bogus "" } + void fv0(this S0 &&); // { dg-bogus "" } + void fv0(this S0 const&); // { dg-bogus "" } + void fv0(this S0 const&&); // { dg-bogus "" } + void fv0(this S0 volatile&&); // { dg-error "cannot be overloaded with" } + void fv0(this S0 const volatile&); // { dg-bogus "" } + void fv0(this S0 const volatile&&); // { dg-bogus "" } + + void fv1() volatile; // { dg-note "previous declaration" } + void fv1(this S0 &); // { dg-bogus "" } + void fv1(this S0 &&); // { dg-bogus "" } + void fv1(this S0 const&); // { dg-bogus "" } + void fv1(this S0 const&&); // { dg-bogus "" } + void fv1(this S0 volatile&); // { dg-error "cannot be overloaded with" } + void fv1(this S0 const volatile&); // { dg-bogus "" } + void fv1(this S0 const volatile&&); // { dg-bogus "" } + + void fcv0() const volatile; // { dg-note "previous declaration" } + void fcv0(this S0 &); // { dg-bogus "" } + void fcv0(this S0 &&); // { dg-bogus "" } + void fcv0(this S0 const&); // { dg-bogus "" } + void fcv0(this S0 const&&); // { dg-bogus "" } + void fcv0(this S0 volatile&); // { dg-bogus "" } + void fcv0(this S0 volatile&&); // { dg-bogus "" } + void fcv0(this S0 const volatile&&); // { dg-error "cannot be overloaded with" } + + void fcv1() const volatile; // { dg-note "previous declaration" } + void fcv1(this S0 &); // { dg-bogus "" } + void fcv1(this S0 &&); // { dg-bogus "" } + void fcv1(this S0 const&); // { dg-bogus "" } + void fcv1(this S0 const&&); // { dg-bogus "" } + void fcv1(this S0 volatile&); // { dg-bogus "" } + void fcv1(this S0 volatile&&); // { dg-bogus "" } + void fcv1(this S0 const volatile&); // { dg-error "cannot be overloaded with" } +}; + +// iobj last + +struct S1 { + void f0(this S1 &&); // { dg-note "previous declaration" } + void f0(this S1 const&); // { dg-bogus "" } + void f0(this S1 const&&); // { dg-bogus "" } + void f0(this S1 volatile&); // { dg-bogus "" } + void f0(this S1 volatile&&); // { dg-bogus "" } + void f0(this S1 const volatile&); // { dg-bogus "" } + void f0(this S1 const volatile&&); // { dg-bogus "" } + void f0(); // { dg-error "cannot be overloaded with" } + + void f1(this S1 &); // { dg-note "previous declaration" } + void f1(this S1 const&); // { dg-bogus "" } + void f1(this S1 const&&); // { dg-bogus "" } + void f1(this S1 volatile&); // { dg-bogus "" } + void f1(this S1 volatile&&); // { dg-bogus "" } + void f1(this S1 const volatile&); // { dg-bogus "" } + void f1(this S1 const volatile&&); // { dg-bogus "" } + void f1(); // { dg-error "cannot be overloaded with" } + + void fc0(this S1 &); // { dg-bogus "" } + void fc0(this S1 &&); // { dg-bogus "" } + void fc0(this S1 const&&); // { dg-note "previous declaration" } + void fc0(this S1 volatile&); // { dg-bogus "" } + void fc0(this S1 volatile&&); // { dg-bogus "" } + void fc0(this S1 const volatile&); // { dg-bogus "" } + void fc0(this S1 const volatile&&); // { dg-bogus "" } + void fc0() const; // { dg-error "cannot be overloaded with" } + + void fc1(this S1 &); // { dg-bogus "" } + void fc1(this S1 &&); // { dg-bogus "" } + void fc1(this S1 const&); // { dg-note "previous declaration" } + void fc1(this S1 volatile&); // { dg-bogus "" } + void fc1(this S1 volatile&&); // { dg-bogus "" } + void fc1(this S1 const volatile&); // { dg-bogus "" } + void fc1(this S1 const volatile&&); // { dg-bogus "" } + void fc1() const; // { dg-error "cannot be overloaded with" } + + void fv0(this S1 &); // { dg-bogus "" } + void fv0(this S1 &&); // { dg-bogus "" } + void fv0(this S1 const&); // { dg-bogus "" } + void fv0(this S1 const&&); // { dg-bogus "" } + void fv0(this S1 volatile&&); // { dg-note "previous declaration" } + void fv0(this S1 const volatile&); // { dg-bogus "" } + void fv0(this S1 const volatile&&); // { dg-bogus "" } + void fv0() volatile; // { dg-error "cannot be overloaded with" } + + void fv1(this S1 &); // { dg-bogus "" } + void fv1(this S1 &&); // { dg-bogus "" } + void fv1(this S1 const&); // { dg-bogus "" } + void fv1(this S1 const&&); // { dg-bogus "" } + void fv1(this S1 volatile&); // { dg-note "previous declaration" } + void fv1(this S1 const volatile&); // { dg-bogus "" } + void fv1(this S1 const volatile&&); // { dg-bogus "" } + void fv1() volatile; // { dg-error "cannot be overloaded with" } + + void fcv0(this S1 &); // { dg-bogus "" } + void fcv0(this S1 &&); // { dg-bogus "" } + void fcv0(this S1 const&); // { dg-bogus "" } + void fcv0(this S1 const&&); // { dg-bogus "" } + void fcv0(this S1 volatile&); // { dg-bogus "" } + void fcv0(this S1 volatile&&); // { dg-bogus "" } + void fcv0(this S1 const volatile&&); // { dg-note "previous declaration" } + void fcv0() const volatile; // { dg-error "cannot be overloaded with" } + + void fcv1(this S1 &); // { dg-bogus "" } + void fcv1(this S1 &&); // { dg-bogus "" } + void fcv1(this S1 const&); // { dg-bogus "" } + void fcv1(this S1 const&&); // { dg-bogus "" } + void fcv1(this S1 volatile&); // { dg-bogus "" } + void fcv1(this S1 volatile&&); // { dg-bogus "" } + void fcv1(this S1 const volatile&); // { dg-note "previous declaration" } + void fcv1() const volatile; // { dg-error "cannot be overloaded with" } +}; + +// in order (iobj replacing one of the following in each group) +// lvalue ref to S +// rvalue ref to S +// lvalue c ref to S +// rvalue c ref to S +// lvalue v ref to S +// rvalue v ref to S +// lvalue cv ref to S +// rvalue cv ref to S + +struct S2 { + void f0(); // { dg-note "previous declaration" } + void f0(this S2 &&); // { dg-error "cannot be overloaded with" } + void f0(this S2 const&); // { dg-bogus "" } + void f0(this S2 const&&); // { dg-bogus "" } + void f0(this S2 volatile&); // { dg-bogus "" } + void f0(this S2 volatile&&); // { dg-bogus "" } + void f0(this S2 const volatile&); // { dg-bogus "" } + void f0(this S2 const volatile&&); // { dg-bogus "" } + + void f1(this S2 &); // { dg-note "previous declaration" } + void f1(); // { dg-error "cannot be overloaded with" } + void f1(this S2 const&); // { dg-bogus "" } + void f1(this S2 const&&); // { dg-bogus "" } + void f1(this S2 volatile&); // { dg-bogus "" } + void f1(this S2 volatile&&); // { dg-bogus "" } + void f1(this S2 const volatile&); // { dg-bogus "" } + void f1(this S2 const volatile&&); // { dg-bogus "" } + + void fc0(this S2 &); // { dg-bogus "" } + void fc0(this S2 &&); // { dg-bogus "" } + void fc0() const; // { dg-note "previous declaration" } + void fc0(this S2 const&&); // { dg-error "cannot be overloaded with" } + void fc0(this S2 volatile&); // { dg-bogus "" } + void fc0(this S2 volatile&&); // { dg-bogus "" } + void fc0(this S2 const volatile&); // { dg-bogus "" } + void fc0(this S2 const volatile&&); // { dg-bogus "" } + + void fc1(this S2 &); // { dg-bogus "" } + void fc1(this S2 &&); // { dg-bogus "" } + void fc1(this S2 const&); // { dg-note "previous declaration" } + void fc1() const; // { dg-error "cannot be overloaded with" } + void fc1(this S2 volatile&); // { dg-bogus "" } + void fc1(this S2 volatile&&); // { dg-bogus "" } + void fc1(this S2 const volatile&); // { dg-bogus "" } + void fc1(this S2 const volatile&&); // { dg-bogus "" } + + void fv0(this S2 &); // { dg-bogus "" } + void fv0(this S2 &&); // { dg-bogus "" } + void fv0(this S2 const&); // { dg-bogus "" } + void fv0(this S2 const&&); // { dg-bogus "" } + void fv0() volatile; // { dg-note "previous declaration" } + void fv0(this S2 volatile&&); // { dg-error "cannot be overloaded with" } + void fv0(this S2 const volatile&); // { dg-bogus "" } + void fv0(this S2 const volatile&&); // { dg-bogus "" } + + void fv1(this S2 &); // { dg-bogus "" } + void fv1(this S2 &&); // { dg-bogus "" } + void fv1(this S2 const&); // { dg-bogus "" } + void fv1(this S2 const&&); // { dg-bogus "" } + void fv1(this S2 volatile&); // { dg-note "previous declaration" } + void fv1() volatile; // { dg-error "cannot be overloaded with" } + void fv1(this S2 const volatile&); // { dg-bogus "" } + void fv1(this S2 const volatile&&); // { dg-bogus "" } + + void fcv0(this S2 &); // { dg-bogus "" } + void fcv0(this S2 &&); // { dg-bogus "" } + void fcv0(this S2 const&); // { dg-bogus "" } + void fcv0(this S2 const&&); // { dg-bogus "" } + void fcv0(this S2 volatile&); // { dg-bogus "" } + void fcv0(this S2 volatile&&); // { dg-bogus "" } + void fcv0() const volatile; // { dg-note "previous declaration" } + void fcv0(this S2 const volatile&&); // { dg-error "cannot be overloaded with" } + + void fcv1(this S2 &); // { dg-bogus "" } + void fcv1(this S2 &&); // { dg-bogus "" } + void fcv1(this S2 const&); // { dg-bogus "" } + void fcv1(this S2 const&&); // { dg-bogus "" } + void fcv1(this S2 volatile&); // { dg-bogus "" } + void fcv1(this S2 volatile&&); // { dg-bogus "" } + void fcv1(this S2 const volatile&); // { dg-note "previous declaration" } + void fcv1() const volatile; // { dg-error "cannot be overloaded with" } +}; + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl2.C new file mode 100644 index 0000000..adb6ae5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl2.C @@ -0,0 +1,161 @@ +// P0847R7 +// { dg-do compile { target c++23 } } +// { dg-options "-pedantic-errors -Wno-volatile" } + +// rejecting redeclarations of by-value xobj member functions +// as iobj member functions that are not ref qualified (and vice-versa) +// also check that valid overloads are accepted without diagnostic + +// iobj | xobj | MSVC | clang | ISOC++ +// none | none | X | X | X +// none | c | X | O | X +// none | v | X | O | X +// none | cv | X | O | X +// c | none | O | O | O +// c | c | O | X | O +// c | v | O | O | O +// c | cv | O | O | O +// v | none | O | O | O +// v | c | O | O | O +// v | v | O | X | O +// v | cv | O | O | O +// cv | none | O | O | O +// cv | c | O | O | O +// cv | v | O | O | O +// cv | cv | O | X | O + +/* Top-level cv qualifiers are supposed to be discarded from + the parameters of a function declaration. + + [dcl.fct.5] + After producing the list of parameter types, any top-level + cv-qualifiers modifying a parameter type are deleted when forming + the function type. + + According to the standard, the type of an implicit object parameter + is always a reference. This isn't reflected in GCC but we still need + to take this rule into account here. + + [over.match.funcs.general.4] + For implicit object member functions, the type of the implicit + object parameter is + -- “lvalue reference to cv X” for functions declared + without a ref-qualifier or with the & ref-qualifier + -- “rvalue reference to cv X” for functions declared with + the && ref-qualifier + + When comparing an iobj and xobj member function to see if they are + redeclarations we treat them differently depending on if the iobj + member function has a ref qualifier or not. + If the iobj member function does not have a ref qualifier, we need to + strip the top-level references before comparing them. + + [basic.scope.scope.3] + Two non-static member functions have corresponding object + parameters if: + -- exactly one is an implicit object member function with no + ref-qualifier and the types of their object parameters + ([dcl.fct]), after removing top-level references, are the + same, or + -- their object parameters have the same type. */ + +struct S { + void f0(); // { dg-note "previous declaration" } + void f0(this S); // { dg-error "cannot be overloaded with" } + + void f1(); // { dg-note "previous declaration" } + void f1(this S const); // { dg-error "cannot be overloaded with" } + + void f2(); // { dg-note "previous declaration" } + void f2(this S volatile); // { dg-error "cannot be overloaded with" } + + void f3(); // { dg-note "previous declaration" } + void f3(this S const volatile); // { dg-error "cannot be overloaded with" } + + void fc0() const; // { dg-bogus "" } + void fc0(this S); // { dg-bogus "" } + + void fc1() const; // { dg-bogus "" } + void fc1(this S const); // { dg-bogus "" } + + void fc2() const; // { dg-bogus "" } + void fc2(this S volatile); // { dg-bogus "" } + + void fc3() const; // { dg-bogus "" } + void fc3(this S const volatile); // { dg-bogus "" } + + void fv0() volatile; // { dg-bogus "" } + void fv0(this S); // { dg-bogus "" } + + void fv1() volatile; // { dg-bogus "" } + void fv1(this S const); // { dg-bogus "" } + + void fv2() volatile; // { dg-bogus "" } + void fv2(this S volatile); // { dg-bogus "" } + + void fv3() volatile; // { dg-bogus "" } + void fv3(this S const volatile); // { dg-bogus "" } + + void fcv0() const volatile; // { dg-bogus "" } + void fcv0(this S); // { dg-bogus "" } + + void fcv1() const volatile; // { dg-bogus "" } + void fcv1(this S const); // { dg-bogus "" } + + void fcv2() const volatile; // { dg-bogus "" } + void fcv2(this S volatile); // { dg-bogus "" } + + void fcv3() const volatile; // { dg-bogus "" } + void fcv3(this S const volatile); // { dg-bogus "" } + + // same as the above f cases except reversed + + void g0(this S); // { dg-note "previous declaration" } + void g0(); // { dg-error "cannot be overloaded with" } + + void g1(this S const); // { dg-note "previous declaration" } + void g1(); // { dg-error "cannot be overloaded with" } + + void g2(this S volatile); // { dg-note "previous declaration" } + void g2(); // { dg-error "cannot be overloaded with" } + + void g3(this S const volatile); // { dg-note "previous declaration" } + void g3(); // { dg-error "cannot be overloaded with" } + + void gc0(this S); // { dg-bogus "" } + void gc0() const; // { dg-bogus "" } + + void gc1(this S const); // { dg-bogus "" } + void gc1() const; // { dg-bogus "" } + + void gc2(this S volatile); // { dg-bogus "" } + void gc2() const; // { dg-bogus "" } + + void gc3(this S const volatile); // { dg-bogus "" } + void gc3() const; // { dg-bogus "" } + + void gv0(this S); // { dg-bogus "" } + void gv0() volatile; // { dg-bogus "" } + + void gv1(this S const); // { dg-bogus "" } + void gv1() volatile; // { dg-bogus "" } + + void gv2(this S volatile); // { dg-bogus "" } + void gv2() volatile; // { dg-bogus "" } + + void gv3(this S const volatile); // { dg-bogus "" } + void gv3() volatile; // { dg-bogus "" } + + void gcv0(this S); // { dg-bogus "" } + void gcv0() const volatile; // { dg-bogus "" } + + void gcv1(this S const); // { dg-bogus "" } + void gcv1() const volatile; // { dg-bogus "" } + + void gcv2(this S volatile); // { dg-bogus "" } + void gcv2() const volatile; // { dg-bogus "" } + + void gcv3(this S const volatile); // { dg-bogus "" } + void gcv3() const volatile; // { dg-bogus "" } +}; + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl3.C new file mode 100644 index 0000000..10cf82d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl3.C @@ -0,0 +1,206 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// redeclarations of xobj member functions as static member functions and vice versa + +struct S { +// no additional params + void f_xs_v(this S) {}; // { dg-note {previous declaration} } + static void f_xs_v() {}; // { dg-error {cannot be overloaded with} } + + void f_xs_ref(this S&) {}; // { dg-note {previous declaration} } + static void f_xs_ref() {}; // { dg-error {cannot be overloaded with} } + + void f_xs_cref(this S const&) {}; // { dg-note {previous declaration} } + static void f_xs_cref() {}; // { dg-error {cannot be overloaded with} } + + void f_xs_rref(this S&&) {}; // { dg-note {previous declaration} } + static void f_xs_rref() {}; // { dg-error {cannot be overloaded with} } + + void f_xs_crref(this S const&&) {}; // { dg-note {previous declaration} } + static void f_xs_crref() {}; // { dg-error {cannot be overloaded with} } + +// reversed + static void f_sx_v() {}; // { dg-note {previous declaration} } + void f_sx_v(this S) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_ref() {}; // { dg-note {previous declaration} } + void f_sx_ref(this S&) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_cref() {}; // { dg-note {previous declaration} } + void f_sx_cref(this S const&) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_rref() {}; // { dg-note {previous declaration} } + void f_sx_rref(this S&&) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_crref() {}; // { dg-note {previous declaration} } + void f_sx_crref(this S const&&) {}; // { dg-error {cannot be overloaded with} } + +// one additional param + void f_xs_v_int(this S, int) {}; // { dg-note {previous declaration} } + static void f_xs_v_int(int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_ref_int(this S&, int) {}; // { dg-note {previous declaration} } + static void f_xs_ref_int(int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_cref_int(this S const&, int) {}; // { dg-note {previous declaration} } + static void f_xs_cref_int(int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_rref_int(this S&&, int) {}; // { dg-note {previous declaration} } + static void f_xs_rref_int(int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_crref_int(this S const&&, int) {}; // { dg-note {previous declaration} } + static void f_xs_crref_int(int) {}; // { dg-error {cannot be overloaded with} } + +// reversed + static void f_sx_v_int(int) {}; // { dg-note {previous declaration} } + void f_sx_v_int(this S, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_ref_int(int) {}; // { dg-note {previous declaration} } + void f_sx_ref_int(this S&, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_cref_int(int) {}; // { dg-note {previous declaration} } + void f_sx_cref_int(this S const&, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_rref_int(int) {}; // { dg-note {previous declaration} } + void f_sx_rref_int(this S&&, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_crref_int(int) {}; // { dg-note {previous declaration} } + void f_sx_crref_int(this S const&&, int) {}; // { dg-error {cannot be overloaded with} } + +// two additional params + void f_xs_v_int2(this S, int, int) {}; // { dg-note {previous declaration} } + static void f_xs_v_int2(int, int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_ref_int2(this S&, int, int) {}; // { dg-note {previous declaration} } + static void f_xs_ref_int2(int, int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_cref_int2(this S const&, int, int) {}; // { dg-note {previous declaration} } + static void f_xs_cref_int2(int, int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_rref_int2(this S&&, int, int) {}; // { dg-note {previous declaration} } + static void f_xs_rref_int2(int, int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_crref_int2(this S const&&, int, int) {}; // { dg-note {previous declaration} } + static void f_xs_crref_int2(int, int) {}; // { dg-error {cannot be overloaded with} } + +// reversed + static void f_sx_v_int2(int, int) {}; // { dg-note {previous declaration} } + void f_sx_v_int2(this S, int, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_ref_int2(int, int) {}; // { dg-note {previous declaration} } + void f_sx_ref_int2(this S&, int, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_cref_int2(int, int) {}; // { dg-note {previous declaration} } + void f_sx_cref_int2(this S const&, int, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_rref_int2(int, int) {}; // { dg-note {previous declaration} } + void f_sx_rref_int2(this S&&, int, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_crref_int2(int, int) {}; // { dg-note {previous declaration} } + void f_sx_crref_int2(this S const&&, int, int) {}; // { dg-error {cannot be overloaded with} } +}; + +// unrelated explicit object parameter type + +struct A {}; + +struct S1 +{ +// no additional params + void f_xs_v(this A) {}; // { dg-note {previous declaration} } + static void f_xs_v() {}; // { dg-error {cannot be overloaded with} } + + void f_xs_ref(this A&) {}; // { dg-note {previous declaration} } + static void f_xs_ref() {}; // { dg-error {cannot be overloaded with} } + + void f_xs_cref(this A const&) {}; // { dg-note {previous declaration} } + static void f_xs_cref() {}; // { dg-error {cannot be overloaded with} } + + void f_xs_rref(this A&&) {}; // { dg-note {previous declaration} } + static void f_xs_rref() {}; // { dg-error {cannot be overloaded with} } + + void f_xs_crref(this A const&&) {}; // { dg-note {previous declaration} } + static void f_xs_crref() {}; // { dg-error {cannot be overloaded with} } + +// reversed + static void f_sx_v() {}; // { dg-note {previous declaration} } + void f_sx_v(this A) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_ref() {}; // { dg-note {previous declaration} } + void f_sx_ref(this A&) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_cref() {}; // { dg-note {previous declaration} } + void f_sx_cref(this A const&) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_rref() {}; // { dg-note {previous declaration} } + void f_sx_rref(this A&&) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_crref() {}; // { dg-note {previous declaration} } + void f_sx_crref(this A const&&) {}; // { dg-error {cannot be overloaded with} } + +// one additional param + void f_xs_v_int(this A, int) {}; // { dg-note {previous declaration} } + static void f_xs_v_int(int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_ref_int(this A&, int) {}; // { dg-note {previous declaration} } + static void f_xs_ref_int(int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_cref_int(this A const&, int) {}; // { dg-note {previous declaration} } + static void f_xs_cref_int(int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_rref_int(this A&&, int) {}; // { dg-note {previous declaration} } + static void f_xs_rref_int(int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_crref_int(this A const&&, int) {}; // { dg-note {previous declaration} } + static void f_xs_crref_int(int) {}; // { dg-error {cannot be overloaded with} } + +// reversed + static void f_sx_v_int(int) {}; // { dg-note {previous declaration} } + void f_sx_v_int(this A, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_ref_int(int) {}; // { dg-note {previous declaration} } + void f_sx_ref_int(this A&, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_cref_int(int) {}; // { dg-note {previous declaration} } + void f_sx_cref_int(this A const&, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_rref_int(int) {}; // { dg-note {previous declaration} } + void f_sx_rref_int(this A&&, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_crref_int(int) {}; // { dg-note {previous declaration} } + void f_sx_crref_int(this A const&&, int) {}; // { dg-error {cannot be overloaded with} } + +// two additional params + void f_xs_v_int2(this A, int, int) {}; // { dg-note {previous declaration} } + static void f_xs_v_int2(int, int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_ref_int2(this A&, int, int) {}; // { dg-note {previous declaration} } + static void f_xs_ref_int2(int, int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_cref_int2(this A const&, int, int) {}; // { dg-note {previous declaration} } + static void f_xs_cref_int2(int, int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_rref_int2(this A&&, int, int) {}; // { dg-note {previous declaration} } + static void f_xs_rref_int2(int, int) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_crref_int2(this A const&&, int, int) {}; // { dg-note {previous declaration} } + static void f_xs_crref_int2(int, int) {}; // { dg-error {cannot be overloaded with} } + +// reversed + static void f_sx_v_int2(int, int) {}; // { dg-note {previous declaration} } + void f_sx_v_int2(this A, int, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_ref_int2(int, int) {}; // { dg-note {previous declaration} } + void f_sx_ref_int2(this A&, int, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_cref_int2(int, int) {}; // { dg-note {previous declaration} } + void f_sx_cref_int2(this A const&, int, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_rref_int2(int, int) {}; // { dg-note {previous declaration} } + void f_sx_rref_int2(this A&&, int, int) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_crref_int2(int, int) {}; // { dg-note {previous declaration} } + void f_sx_crref_int2(this A const&&, int, int) {}; // { dg-error {cannot be overloaded with} } +}; + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl4.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl4.C new file mode 100644 index 0000000..2e1b12b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl4.C @@ -0,0 +1,97 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// redeclarations with equivalent constraints + +template +concept Constrain = true; + + +struct S { +// xobj/static + void f_xs_v(this S, Constrain auto) {}; // { dg-note {previous declaration} } + static void f_xs_v(Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_ref(this S&, Constrain auto) {}; // { dg-note {previous declaration} } + static void f_xs_ref(Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_cref(this S const&, Constrain auto) {}; // { dg-note {previous declaration} } + static void f_xs_cref(Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_rref(this S&&, Constrain auto) {}; // { dg-note {previous declaration} } + static void f_xs_rref(Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + void f_xs_crref(this S const&&, Constrain auto) {}; // { dg-note {previous declaration} } + static void f_xs_crref(Constrain auto) {}; // { dg-error {cannot be overloaded with} } + +// static/xobj + static void f_sx_v(Constrain auto) {}; // { dg-note {previous declaration} } + void f_sx_v(this S, Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_ref(Constrain auto) {}; // { dg-note {previous declaration} } + void f_sx_ref(this S&, Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_cref(Constrain auto) {}; // { dg-note {previous declaration} } + void f_sx_cref(this S const&, Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_rref(Constrain auto) {}; // { dg-note {previous declaration} } + void f_sx_rref(this S&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + static void f_sx_crref(Constrain auto) {}; // { dg-note {previous declaration} } + void f_sx_crref(this S const&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + // iobj/xobj + void f_ix_lref(Constrain auto) {}; // { dg-note {previous declaration} } + void f_ix_lref(this S&, Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + void f_ix_rref(Constrain auto) {}; // { dg-note {previous declaration} } + void f_ix_rref(this S&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + void f_ix_const_lref(Constrain auto) const {}; // { dg-note {previous declaration} } + void f_ix_const_lref(this S const&, Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + void f_ix_const_rref(Constrain auto) const {}; // { dg-note {previous declaration} } + void f_ix_const_rref(this S const&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + // xobj/iobj + void f_xi_lref(this S&, Constrain auto) {}; // { dg-note {previous declaration} } + void f_xi_lref(Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + void f_xi_rref(this S&&, Constrain auto) {}; // { dg-note {previous declaration} } + void f_xi_rref(Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + void f_xi_const_lref(this S const&, Constrain auto) {}; // { dg-note {previous declaration} } + void f_xi_const_lref(Constrain auto) const {}; // { dg-error {cannot be overloaded with} } + + void f_xi_const_rref(this S const&&, Constrain auto) {}; // { dg-note {previous declaration} } + void f_xi_const_rref(Constrain auto) const {}; // { dg-error {cannot be overloaded with} } + + // with ref qualifier + + // iobj/xobj + void f_ix_lref_refqual(Constrain auto) & {}; // { dg-note {previous declaration} } + void f_ix_lref_refqual(this S&, Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + void f_ix_rref_refqual(Constrain auto) && {}; // { dg-note {previous declaration} } + void f_ix_rref_refqual(this S&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + void f_ix_const_lref_refqual(Constrain auto) const& {}; // { dg-note {previous declaration} } + void f_ix_const_lref_refqual(this S const&, Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + void f_ix_const_rref_refqual(Constrain auto) const&& {}; // { dg-note {previous declaration} } + void f_ix_const_rref_refqual(this S const&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} } + + // xobj/iobj + void f_xi_lref_refqual(this S&, Constrain auto) {}; // { dg-note {previous declaration} } + void f_xi_lref_refqual(Constrain auto) & {}; // { dg-error {cannot be overloaded with} } + + void f_xi_rref_refqual(this S&&, Constrain auto) {}; // { dg-note {previous declaration} } + void f_xi_rref_refqual(Constrain auto) && {}; // { dg-error {cannot be overloaded with} } + + void f_xi_const_lref_refqual(this S const&, Constrain auto) {}; // { dg-note {previous declaration} } + void f_xi_const_lref_refqual(Constrain auto) const& {}; // { dg-error {cannot be overloaded with} } + + void f_xi_const_rref_refqual(this S const&&, Constrain auto) {}; // { dg-note {previous declaration} } + void f_xi_const_rref_refqual(Constrain auto) const&& {}; // { dg-error {cannot be overloaded with} } +}; + -- cgit v1.1 From f8bf6a69e260a5f1aa0dbf89a6e4bcdf1a24af5d Mon Sep 17 00:00:00 2001 From: waffl3x Date: Sun, 7 Jan 2024 00:03:19 +0000 Subject: c++: P0847R7 (deducing this) - diagnostics. [PR102609] Diagnostics for xobj member functions. Also includes some diagnostics for xobj lambdas which are not implemented here. CWG2554 is also implemented here, we explicitly error when an xobj member function overrides a virtual function. PR c++/102609 gcc/c-family/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - diagnostics. * c-cppbuiltin.cc (c_cpp_builtins): Define __cpp_explicit_this_parameter=202110L feature test macro. gcc/cp/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - diagnostics. * class.cc (resolve_address_of_overloaded_function): Diagnostics. * cp-tree.h (TFF_XOBJ_FUNC): Define. * decl.cc (grokfndecl): Diagnostics. (grokdeclarator): Diagnostics. * error.cc (dump_aggr_type): Pass TFF_XOBJ_FUNC. (dump_lambda_function): Formatting for xobj lambda. (dump_function_decl): Pass TFF_XOBJ_FUNC. (dump_parameters): Formatting for xobj member functions. (function_category): Formatting for xobj member functions. * parser.cc (cp_parser_decl_specifier_seq): Diagnostics. (cp_parser_parameter_declaration): Diagnostics. * search.cc (look_for_overrides_here): Make xobj member functions override. (look_for_overrides_r): Reject an overriding xobj member function and diagnose it. * semantics.cc (finish_this_expr): Diagnostics. * typeck.cc (cp_build_addr_expr_1): Diagnostics. gcc/testsuite/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - diagnostics. * g++.dg/cpp23/feat-cxx2b.C: Test existance and value of __cpp_explicit_this_parameter feature test macro. * g++.dg/cpp26/feat-cxx26.C: Likewise. * g++.dg/cpp23/explicit-obj-cxx-dialect-A.C: New test. * g++.dg/cpp23/explicit-obj-cxx-dialect-B.C: New test. * g++.dg/cpp23/explicit-obj-cxx-dialect-C.C: New test. * g++.dg/cpp23/explicit-obj-cxx-dialect-D.C: New test. * g++.dg/cpp23/explicit-obj-cxx-dialect-E.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics1.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics2.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics3.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics4.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics5.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics6.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics7.C: New test. Signed-off-by: Waffl3x --- gcc/c-family/c-cppbuiltin.cc | 1 + gcc/cp/class.cc | 55 ++++-- gcc/cp/cp-tree.h | 5 +- gcc/cp/decl.cc | 138 ++++++++++++-- gcc/cp/error.cc | 24 ++- gcc/cp/parser.cc | 38 +++- gcc/cp/search.cc | 14 +- gcc/cp/semantics.cc | 25 ++- gcc/cp/typeck.cc | 45 +++-- .../g++.dg/cpp23/explicit-obj-cxx-dialect-A.C | 7 + .../g++.dg/cpp23/explicit-obj-cxx-dialect-B.C | 7 + .../g++.dg/cpp23/explicit-obj-cxx-dialect-C.C | 9 + .../g++.dg/cpp23/explicit-obj-cxx-dialect-D.C | 8 + .../g++.dg/cpp23/explicit-obj-cxx-dialect-E.C | 8 + .../g++.dg/cpp23/explicit-obj-diagnostics1.C | 139 ++++++++++++++ .../g++.dg/cpp23/explicit-obj-diagnostics2.C | 26 +++ .../g++.dg/cpp23/explicit-obj-diagnostics3.C | 20 ++ .../g++.dg/cpp23/explicit-obj-diagnostics4.C | 16 ++ .../g++.dg/cpp23/explicit-obj-diagnostics5.C | 23 +++ .../g++.dg/cpp23/explicit-obj-diagnostics6.C | 206 +++++++++++++++++++++ .../g++.dg/cpp23/explicit-obj-diagnostics7.C | 95 ++++++++++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 6 + gcc/testsuite/g++.dg/cpp26/feat-cxx26.C | 6 + 23 files changed, 871 insertions(+), 50 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc index 9553b13..38f3203 100644 --- a/gcc/c-family/c-cppbuiltin.cc +++ b/gcc/c-family/c-cppbuiltin.cc @@ -1082,6 +1082,7 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_named_character_escapes=202207L"); cpp_define (pfile, "__cpp_static_call_operator=202207L"); cpp_define (pfile, "__cpp_implicit_move=202207L"); + cpp_define (pfile, "__cpp_explicit_this_parameter=202110L"); } if (cxx_dialect > cxx23) { diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index 6f924d5..f3cfa9f 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -8968,20 +8968,51 @@ resolve_address_of_overloaded_function (tree target_type, fn = TREE_PURPOSE (matches); if (DECL_OBJECT_MEMBER_FUNCTION_P (fn) - && !(complain & tf_ptrmem_ok) && !flag_ms_extensions) - { - static int explained; - - if (!(complain & tf_error)) + && !(complain & tf_ptrmem_ok)) + { + /* Previously we allowed this behavior for iobj member functions when the + -fms-extensions flag is passed as MSVC allows this as a language + extension. MSVC also allows this for xobj member functions, but the + documentation for -fms-extensions states it's purpose is to support + the use of microsoft headers. Until otherwise demonstrated, we should + assume xobj member functions are not used in this manner in microsoft + headers and indiscriminately forbid the incorrect syntax instead of + supporting it for non-legacy uses. This should hopefully encourage + conformance going forward. + This comment is referred to in typeck.cc:cp_build_addr_expr_1. */ + if (DECL_IOBJ_MEMBER_FUNCTION_P (fn) && flag_ms_extensions) + /* Early escape. */; + else if (!(complain & tf_error)) return error_mark_node; - - auto_diagnostic_group d; - if (permerror (input_location, "assuming pointer to member %qD", fn) - && !explained) + else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)) + { + auto_diagnostic_group d; + /* Should match the error in typeck.cc:cp_build_addr_expr_1. + We seem to lack the details here to match that diagnostic exactly, + perhaps this could be fixed in the future? See PR113075 bug 2. */ + error_at (input_location, + "ISO C++ forbids taking the address of an unqualified" + " or parenthesized non-static member function to form" + " a pointer to explicit object member function."); + /* This is incorrect, see PR113075 bug 3. */ + inform (input_location, + "a pointer to explicit object member function can only be " + "formed with %<&%E%>", fn); + } + else { - inform (input_location, "(a pointer to member can only be " - "formed with %<&%E%>)", fn); - explained = 1; + static int explained; + gcc_assert (DECL_IOBJ_MEMBER_FUNCTION_P (fn) && !flag_ms_extensions); + /* Is there a reason this error message doesn't match the one in + typeck.cc:cp_build_addr_expr_1? */ + auto_diagnostic_group d; + if (permerror (input_location, "assuming pointer to member %qD", fn) + && !explained) + { + inform (input_location, "(a pointer to member can only be " + "formed with %<&%E%>)", fn); + explained = 1; + } } } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index ce57716..dbc7177 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6181,7 +6181,9 @@ enum auto_deduction_context identical to their defaults. TFF_NO_TEMPLATE_BINDINGS: do not print information about the template arguments for a function template specialization. - TFF_POINTER: we are printing a pointer type. */ + TFF_POINTER: we are printing a pointer type. + TFF_XOBJ_FUNC: we are printing an explicit object member function's + parameters. */ #define TFF_PLAIN_IDENTIFIER (0) #define TFF_SCOPE (1) @@ -6199,6 +6201,7 @@ enum auto_deduction_context #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS (1 << 12) #define TFF_NO_TEMPLATE_BINDINGS (1 << 13) #define TFF_POINTER (1 << 14) +#define TFF_XOBJ_FUNC (1 << 15) /* These constants can be used as bit flags to control strip_typedefs. diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 16ccdb3..7f26705 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -10805,24 +10805,30 @@ grokfndecl (tree ctype, TREE_TYPE (decl) = apply_memfn_quals (TREE_TYPE (decl), TYPE_UNQUALIFIED, REF_QUAL_NONE); - + auto_diagnostic_group d; if (quals) - { - error (ctype + error (!ctype + ? G_("non-member function %qD cannot have cv-qualifier") + : !xobj_func_p ? G_("static member function %qD cannot have cv-qualifier") - : G_("non-member function %qD cannot have cv-qualifier"), - decl); - quals = TYPE_UNQUALIFIED; - } - + : G_("explicit object member function " + "%qD cannot have cv-qualifier"), + decl); if (rqual) - { - error (ctype + error (!ctype + ? G_("non-member function %qD cannot have ref-qualifier") + : !xobj_func_p ? G_("static member function %qD cannot have ref-qualifier") - : G_("non-member function %qD cannot have ref-qualifier"), + : G_("explicit object member function " + "%qD cannot have ref-qualifier"), decl); - rqual = REF_QUAL_NONE; - } + + if (xobj_func_p && (quals || rqual)) + inform (DECL_SOURCE_LOCATION (DECL_ARGUMENTS (decl)), + "explicit object parameter declared here"); + quals = TYPE_UNQUALIFIED; + rqual = REF_QUAL_NONE; + } if (deduction_guide_p (decl)) @@ -13317,17 +13323,85 @@ grokdeclarator (const cp_declarator *declarator, /* There is no need to iterate over the list, only the first parm can be a valid xobj parm. */ if (!parm_list || TREE_PURPOSE (parm_list) != this_identifier) - return false; + return NULL_TREE; /* If we make it here, we are looking at an xobj parm. Non-null 'purpose' usually means the parm has a default argument, we don't want to violate this assumption. */ TREE_PURPOSE (parm_list) = NULL_TREE; - return true; + return TREE_VALUE (parm_list); }; - is_xobj_member_function + tree xobj_parm = find_xobj_parm (declarator->u.function.parameters); + is_xobj_member_function = xobj_parm; + + if (xobj_parm && cxx_dialect < cxx23) + pedwarn (DECL_SOURCE_LOCATION (xobj_parm), OPT_Wc__23_extensions, + "explicit object member function only available " + "with %<-std=c++23%> or %<-std=gnu++23%>"); + + if (xobj_parm && decl_context == TYPENAME) + { + /* We inform in every case, just differently depending on what + case it is. */ + auto_diagnostic_group d; + bool ptr_type = true; + /* If declarator->kind is cdk_function and we are at the end of + the declarator chain, we are looking at a function type. */ + if (!declarator->declarator) + { + error_at (DECL_SOURCE_LOCATION (xobj_parm), + "a function type cannot " + "have an explicit object parameter"); + ptr_type = false; + } + else if (declarator->declarator->kind == cdk_pointer) + error_at (DECL_SOURCE_LOCATION (xobj_parm), + "a pointer to function type cannot " + "have an explicit object parameter"); + else if (declarator->declarator->kind == cdk_ptrmem) + error_at (DECL_SOURCE_LOCATION (xobj_parm), + "a pointer to member function type " + "cannot have an explicit object parameter"); + else + gcc_unreachable (); + + /* The locations being used here are probably not correct. */ + if (ptr_type) + inform (DECL_SOURCE_LOCATION (xobj_parm), + "the type of a pointer to explicit object member " + "function is a regular pointer to function type"); + else + inform (DECL_SOURCE_LOCATION (xobj_parm), + "the type of an explicit object " + "member function is a regular function type"); + /* Ideally we should synthesize the correct syntax + for the user, perhaps this could be added later. */ + } + /* Since a valid xobj parm has its purpose cleared in find_xobj_parm + the first parm node will never erroneously be detected here. */ + { + auto_diagnostic_group d; + bool bad_xobj_parm_encountered = false; + for (tree parm = declarator->u.function.parameters; + parm && parm != void_list_node; + parm = TREE_CHAIN (parm)) + { + if (TREE_PURPOSE (parm) != this_identifier) + continue; + bad_xobj_parm_encountered = true; + gcc_rich_location bad_xobj_parm + (DECL_SOURCE_LOCATION (TREE_VALUE (parm))); + error_at (&bad_xobj_parm, + "Only the first parameter of a member function " + "can be declared as an explicit object parameter"); + } + if (bad_xobj_parm_encountered && xobj_parm) + inform (DECL_SOURCE_LOCATION (xobj_parm), + "Valid explicit object parameter declared here"); + } + if (reqs) error_at (location_of (reqs), "requires-clause on return type"); reqs = declarator->u.function.requires_clause; @@ -13615,6 +13689,38 @@ grokdeclarator (const cp_declarator *declarator, explicitp = 2; } + if (xobj_parm) + { + if (!ctype + && decl_context == NORMAL + && (in_namespace + || !declarator->declarator->u.id.qualifying_scope)) + error_at (DECL_SOURCE_LOCATION (xobj_parm), + "a non-member function cannot have " + "an explicit object parameter"); + else + { + if (virtualp) + { + auto_diagnostic_group d; + error_at (declspecs->locations[ds_virtual], + "an explicit object member function cannot " + "be %"); + inform (DECL_SOURCE_LOCATION (xobj_parm), + "explicit object parameter declared here"); + virtualp = false; + } + if (staticp >= 2) + { + auto_diagnostic_group d; + error_at (declspecs->locations[ds_storage_class], + "an explicit object member function cannot " + "be %"); + inform (DECL_SOURCE_LOCATION (xobj_parm), + "explicit object parameter declared here"); + } + } + } tree pushed_scope = NULL_TREE; if (funcdecl_p && decl_context != FIELD diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index a384f62..52e24fb 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -840,10 +840,14 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags) { /* A lambda's "type" is essentially its signature. */ pp_string (pp, M_("padding = pp_before; pp_c_ws_string (pp, "static"); @@ -1833,7 +1839,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags) if (!(flags & TFF_NO_FUNCTION_ARGUMENTS)) { - dump_parameters (pp, parmtypes, flags); + int const parm_flags + = DECL_XOBJ_MEMBER_FUNCTION_P (t) ? TFF_XOBJ_FUNC | flags : flags; + dump_parameters (pp, parmtypes, parm_flags); if (TREE_CODE (fntype) == METHOD_TYPE) { @@ -1912,6 +1920,8 @@ dump_parameters (cxx_pretty_printer *pp, tree parmtypes, int flags) for (first = 1; parmtypes != void_list_node; parmtypes = TREE_CHAIN (parmtypes)) { + if (first && flags & TFF_XOBJ_FUNC) + pp_string (pp, "this "); if (!first) pp_separate_with_comma (pp); first = 0; @@ -3696,6 +3706,8 @@ function_category (tree fn) return _("In destructor %qD"); else if (LAMBDA_FUNCTION_P (fn)) return _("In lambda function"); + else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)) + return _("In explicit object member function %qD"); else return _("In member function %qD"); } diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 046c48d..b0c08cf 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -16188,6 +16188,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser, /* Assume no class or enumeration type is declared. */ *declares_class_or_enum = 0; + /* Keep a token that additionally will be used for diagnostics. */ + cp_token *first_specifier = NULL; /* Keep reading specifiers until there are no more to read. */ while (true) { @@ -16260,12 +16262,32 @@ cp_parser_decl_specifier_seq (cp_parser* parser, decl_specs->locations[ds_attribute] = token->location; continue; } + /* We know by this point that the token is not part of an attribute. */ + if (!first_specifier) + first_specifier = token; /* Special case for "this" specifier, indicating a parm is an xobj parm. The "this" specifier must be the first specifier in the declaration, after any attributes. */ if (token->keyword == RID_THIS) { cp_lexer_consume_token (parser->lexer); + if (token != first_specifier) + { + /* Don't emit diagnostics if we have already seen "this", + leave it for set_and_check_decl_spec_loc. */ + if (decl_specs->locations[ds_this] == 0) + { + auto_diagnostic_group d; + gcc_rich_location richloc (token->location); + /* Works, need to add tests for it though. */ + richloc.add_fixit_remove (); + richloc.add_fixit_insert_before (first_specifier->location, + "this "); + error_at (&richloc, + "% must be the first specifier " + "in a parameter declaration"); + } + } set_and_check_decl_spec_loc (decl_specs, ds_this, token); continue; } @@ -25657,12 +25679,14 @@ cp_parser_parameter_declaration (cp_parser *parser, /* The restriction on defining new types applies only to the type of the parameter, not to the default argument. */ parser->type_definition_forbidden_message = saved_message; - + cp_token *eq_token = NULL; /* If the next token is `=', then process a default argument. */ if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) { tree type = decl_specifiers.type; token = cp_lexer_peek_token (parser->lexer); + /* Used for diagnostics with an xobj parameter. */ + eq_token = token; if (declarator) declarator->init_loc = token->location; /* If we are defining a class, then the tokens that make up the @@ -25733,6 +25757,18 @@ cp_parser_parameter_declaration (cp_parser *parser, if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_this)) { + if (default_argument) + { + /* If there is a default_argument, eq_token should always be set. */ + gcc_assert (eq_token); + location_t param_with_init_loc + = make_location (eq_token->location, + decl_spec_token_start->location, + input_location); + error_at (param_with_init_loc, + "an explicit object parameter " + "may not have a default argument"); + } /* Xobj parameters can not have default arguments, thus we can reuse the default argument field to flag the param as such. */ default_argument = this_identifier; diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc index f7be4a1..dc76714 100644 --- a/gcc/cp/search.cc +++ b/gcc/cp/search.cc @@ -2224,10 +2224,13 @@ look_for_overrides_here (tree type, tree fndecl) /* Not a virtual. */; else if (DECL_CONTEXT (fn) != type) /* Introduced with a using declaration. */; - else if (DECL_STATIC_FUNCTION_P (fndecl)) + else if (DECL_STATIC_FUNCTION_P (fndecl) + || DECL_XOBJ_MEMBER_FUNCTION_P (fndecl)) { tree btypes = TYPE_ARG_TYPES (TREE_TYPE (fn)); tree dtypes = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); + dtypes = DECL_XOBJ_MEMBER_FUNCTION_P (fndecl) ? TREE_CHAIN (dtypes) + : dtypes; if (compparms (TREE_CHAIN (btypes), dtypes)) return fn; } @@ -2255,6 +2258,15 @@ look_for_overrides_r (tree type, tree fndecl) error ("%q+#D cannot be declared", fndecl); error (" since %q+#D declared in base class", fn); } + else if (DECL_XOBJ_MEMBER_FUNCTION_P (fndecl)) + { + auto_diagnostic_group d; + error_at (DECL_SOURCE_LOCATION (fndecl), + "explicit object member function " + "overrides virtual function"); + inform (DECL_SOURCE_LOCATION (fn), + "virtual function declared here"); + } else { /* It's definitely virtual, even if not explicitly set. */ diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 8ff6962..3253cee 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -3169,7 +3169,30 @@ finish_this_expr (void) return rvalue (result); tree fn = current_nonlambda_function (); - if (fn && DECL_STATIC_FUNCTION_P (fn)) + if (fn && DECL_XOBJ_MEMBER_FUNCTION_P (fn)) + { + auto_diagnostic_group d; + error ("% is unavailable for explicit object member " + "functions"); + tree xobj_parm = DECL_ARGUMENTS (fn); + gcc_assert (xobj_parm); + tree parm_name = DECL_NAME (xobj_parm); + + static tree remembered_fn = NULL_TREE; + /* Only output this diagnostic once per function. */ + if (remembered_fn == fn) + /* Early escape. */; + else if (parm_name) + inform (DECL_SOURCE_LOCATION (xobj_parm), + "use explicit object parameter %qs instead", + IDENTIFIER_POINTER (parm_name)); + else + inform (DECL_SOURCE_LOCATION (xobj_parm), + "name the explicit object parameter"); + + remembered_fn = fn; + } + else if (fn && DECL_STATIC_FUNCTION_P (fn)) error ("% is unavailable for static member functions"); else if (fn && processing_contract_condition && DECL_CONSTRUCTOR_P (fn)) error ("invalid use of % before it is valid"); diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index 7e66913..a15eda3 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -7129,26 +7129,47 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain) tree fn = get_first_fn (TREE_OPERAND (arg, 1)); if (!mark_used (fn, complain) && !(complain & tf_error)) return error_mark_node; - - if (! flag_ms_extensions) + /* Until microsoft headers are known to incorrectly take the address of + unqualified xobj member functions we should not support this + extension. + See comment in class.cc:resolve_address_of_overloaded_function for + the extended reasoning. */ + if (!flag_ms_extensions || DECL_XOBJ_MEMBER_FUNCTION_P (fn)) { + auto_diagnostic_group d; tree name = DECL_NAME (fn); if (!(complain & tf_error)) return error_mark_node; else if (current_class_type && TREE_OPERAND (arg, 0) == current_class_ref) /* An expression like &memfn. */ - permerror (loc, - "ISO C++ forbids taking the address of an unqualified" - " or parenthesized non-static member function to form" - " a pointer to member function. Say %<&%T::%D%>", - base, name); + if (!DECL_XOBJ_MEMBER_FUNCTION_P (fn)) + permerror (loc, + "ISO C++ forbids taking the address of an unqualified" + " or parenthesized non-static member function to form" + " a pointer to member function. Say %<&%T::%D%>", + base, name); + else + error_at (loc, + "ISO C++ forbids taking the address of an unqualified" + " or parenthesized non-static member function to form" + " a pointer to explicit object member function"); else - permerror (loc, - "ISO C++ forbids taking the address of a bound member" - " function to form a pointer to member function." - " Say %<&%T::%D%>", - base, name); + if (!DECL_XOBJ_MEMBER_FUNCTION_P (fn)) + permerror (loc, + "ISO C++ forbids taking the address of a bound member" + " function to form a pointer to member function." + " Say %<&%T::%D%>", + base, name); + else + error_at (loc, + "ISO C++ forbids taking the address of a bound member" + " function to form a pointer to explicit object member" + " function"); + if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)) + inform (loc, + "a pointer to explicit object member function can only be " + "formed with %<&%T::%D%>", base, name); } arg = build_offset_ref (base, fn, /*address_p=*/true, complain); } diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C new file mode 100644 index 0000000..5043e91 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C @@ -0,0 +1,7 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +struct S { + void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} } +}; + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C new file mode 100644 index 0000000..fb2a6a0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C @@ -0,0 +1,7 @@ +// P0847R7 +// { dg-do compile { target c++20_down } } + +struct S { + void f(this S); // { dg-error {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} } +}; + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C new file mode 100644 index 0000000..182e294 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C @@ -0,0 +1,9 @@ +// P0847R7 +// { dg-do compile { target c++20_down } } +// don't pass in -pedantic-errors +// { dg-options "" } + +struct S { + void f(this S); // { dg-warning {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} } +}; + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C new file mode 100644 index 0000000..49b7ea0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C @@ -0,0 +1,8 @@ +// P0847R7 +// { dg-do compile { target c++20_down } } +// { dg-options "-Wno-c++23-extensions" } + +struct S { + void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} } +}; + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C new file mode 100644 index 0000000..411b70c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C @@ -0,0 +1,8 @@ +// P0847R7 +// { dg-do compile { target c++20_down } } +// { dg-options "-Wno-c++23-extensions -pedantic-errors" } + +struct S { + void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} } +}; + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C new file mode 100644 index 0000000..dfac118 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C @@ -0,0 +1,139 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// rejection and diagnosis of xobj member functions that have member function qualifiers. + +struct S { + void f_value_0(this S) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_value_1(this S) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_value_2(this S) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_value_3(this S) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_value_4(this S) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_value_5(this S) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_value_6(this S) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_value_7(this S) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_value_8(this S) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_value_9(this S) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_value_A(this S) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_ref_0(this S&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_ref_1(this S&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_ref_2(this S&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_ref_3(this S&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_ref_4(this S&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_ref_5(this S&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_ref_6(this S&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_ref_7(this S&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_ref_8(this S&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_ref_9(this S&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_ref_A(this S&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_refref_0(this S&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_refref_1(this S&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_refref_2(this S&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_refref_3(this S&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_refref_4(this S&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_refref_5(this S&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_refref_6(this S&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_refref_7(this S&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_refref_8(this S&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_refref_9(this S&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_refref_A(this S&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_cref_0(this S const&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cref_1(this S const&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cref_2(this S const&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cref_3(this S const&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_cref_4(this S const&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_cref_5(this S const&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cref_6(this S const&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cref_7(this S const&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cref_8(this S const&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cref_9(this S const&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cref_A(this S const&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_crefref_0(this S const&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_crefref_1(this S const&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_crefref_2(this S const&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_crefref_3(this S const&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_crefref_4(this S const&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_crefref_5(this S const&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_crefref_6(this S const&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_crefref_7(this S const&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_crefref_8(this S const&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_crefref_9(this S const&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_crefref_A(this S const&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_vref_0(this S volatile&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_vref_1(this S volatile&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_vref_2(this S volatile&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_vref_3(this S volatile&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_vref_4(this S volatile&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_vref_5(this S volatile&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vref_6(this S volatile&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vref_7(this S volatile&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vref_8(this S volatile&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vref_9(this S volatile&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vref_A(this S volatile&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_vrefref_0(this S volatile&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_vrefref_1(this S volatile&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_vrefref_2(this S volatile&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_vrefref_3(this S volatile&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_vrefref_4(this S volatile&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_vrefref_5(this S volatile&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vrefref_6(this S volatile&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vrefref_7(this S volatile&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vrefref_8(this S volatile&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vrefref_9(this S volatile&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vrefref_A(this S volatile&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_cvref_0(this S const volatile&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cvref_1(this S const volatile&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cvref_2(this S const volatile&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cvref_3(this S const volatile&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_cvref_4(this S const volatile&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_cvref_5(this S const volatile&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvref_6(this S const volatile&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvref_7(this S const volatile&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvref_8(this S const volatile&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvref_9(this S const volatile&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvref_A(this S const volatile&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_cvrefref_0(this S const volatile&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cvrefref_1(this S const volatile&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cvrefref_2(this S const volatile&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cvrefref_3(this S const volatile&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_cvrefref_4(this S const volatile&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_cvrefref_5(this S const volatile&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvrefref_6(this S const volatile&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvrefref_7(this S const volatile&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvrefref_8(this S const volatile&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvrefref_9(this S const volatile&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvrefref_A(this S const volatile&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + template void d_templ_0(this Self&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + template void d_templ_1(this Self&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + template void d_templ_2(this Self&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + template void d_templ_3(this Self&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + template void d_templ_4(this Self&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + template void d_templ_5(this Self&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + template void d_templ_6(this Self&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + template void d_templ_7(this Self&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + template void d_templ_8(this Self&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + template void d_templ_9(this Self&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + template void d_templ_A(this Self&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void d_auto_0(this auto&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void d_auto_1(this auto&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void d_auto_2(this auto&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void d_auto_3(this auto&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void d_auto_4(this auto&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void d_auto_5(this auto&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void d_auto_6(this auto&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void d_auto_7(this auto&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void d_auto_8(this auto&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void d_auto_9(this auto&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void d_auto_A(this auto&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } +}; + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C new file mode 100644 index 0000000..771200b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C @@ -0,0 +1,26 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// rejection and diagnosis of incorrect uses of 'this' in declarations and definitions + +using func_type = void(this int); // { dg-line func_type_line } +// { dg-error "a function type cannot have an explicit object parameter" "" { target *-*-* } func_type_line } +// { dg-note "the type of an explicit object member function is a regular function type" "" { target *-*-* } func_type_line } + +using func_ptr_type = void(*)(this int); // { dg-line func_ptr_type_line } +// { dg-error "a pointer to function type cannot have an explicit object parameter" "" { target *-*-* } func_ptr_type_line } +// { dg-note "the type of a pointer to explicit object member function is a regular pointer to function type" "" { target *-*-* } func_ptr_type_line } + +struct S { + static void f(this S) {} // { dg-line static_member_func_line } +}; +// { dg-error "an explicit object member function cannot be 'static'" "" { target *-*-* } static_member_func_line } +// { dg-note "explicit object parameter declared here" "" { target *-*-* } static_member_func_line } + +using mem_func_type = void (S::*)(this S&); // { dg-line mem_func_type_line } +// { dg-error "a pointer to member function type cannot have an explicit object parameter" "" { target *-*-* } mem_func_type_line } +// { dg-note "the type of a pointer to explicit object member function is a regular pointer to function type" "" { target *-*-* } mem_func_type_line } + +void f(this int); // { dg-error "a non-member function cannot have an explicit object parameter" } +void f(this int) {} // { dg-error "a non-member function cannot have an explicit object parameter" } + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C new file mode 100644 index 0000000..ec091d6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C @@ -0,0 +1,20 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// rejection and diagnosis of an xobj parameter declared with a default argument + +struct S { + void f0(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" } + void f1(this S = {}); // { dg-error "an explicit object parameter may not have a default argument" } + void f2(this S); + void f10(this S s = {}) {} // { dg-error "an explicit object parameter may not have a default argument" } + void f11(this S s = {}); // { dg-error "an explicit object parameter may not have a default argument" } + void f12(this S s); +}; + +void S::f1(this S) {} +void S::f2(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" } + +void S::f11(this S s) {} +void S::f12(this S s = {}) {} // { dg-error "an explicit object parameter may not have a default argument" } + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C new file mode 100644 index 0000000..1744b3f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C @@ -0,0 +1,16 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// location diagnostic text when an error is emitted from an xobj member function +// this does not test for specific ill-formed code, just the additional diagnostic message + +// { dg-message "In explicit object member function" "" { target *-*-* } 0 } + +struct S { + void f(this S s) { + // The specific diagnosis issued here does not matter + // we just need to force an error to be emitted + +s; // { dg-error "" } + } +}; + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C new file mode 100644 index 0000000..7ec43f6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C @@ -0,0 +1,23 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// rejection and diagnosis of invalid uses of 'this' in body of xobj member functions + +// { dg-message "In explicit object member function" "" { target *-*-* } 0 } + +struct S0 { + int _n; + void f(this S0& s) { // { dg-note {use explicit object parameter 's' instead} } + this->_n = 10; // { dg-error "'this' is unavailable for explicit object member functions" } + // suppress unused variable warning + static_cast(s); + } +}; + +struct S1 { + int _n; + void f(this S1&) { // { dg-note "name the explicit object parameter" } + this->_n = 10; // { dg-error "'this' is unavailable for explicit object member functions" } + } +}; + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C new file mode 100644 index 0000000..77ace49 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C @@ -0,0 +1,206 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// rejection and diagnosis when taking address of an unqualified xobj member function + +// { dg-message "In explicit object member function" "" { target *-*-* } 0 } + +struct S { + void f(this S&) {} + + void g(this S&) {} + void g(this S&, int) {} + + void test0() { + void (*fp)(S&) = &f; // { dg-line line_sf } + // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } line_sf } + // { dg-note {a pointer to explicit object member function can only be formed with '&S::f'} "" { target *-*-* } line_sf } + void (*gp)(S&) = &g; // { dg-line line_sg } + // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } line_sg } + // { dg-note {a pointer to explicit object member function can only be formed with '&S::g'} "" { target *-*-* } line_sg } + } + + void test1(this S& self) { + void (*fp)(S&) = &self.f; // { dg-line s_test1_f } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } s_test1_f } + // { dg-note {a pointer to explicit object member function can only be formed with '&S::f'} "" { target *-*-* } s_test1_f } + void (*gp)(S&) = &self.g; // { dg-line s_test1_g } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } s_test1_g } + // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } s_test1_g } + // { dg-note {a pointer to explicit object member function can only be formed with '&S::g'} "" { target *-*-* } s_test1_g } + } +}; + +void test0() +{ + S s{}; + + void (*fp)(S&) = &s.f; // { dg-line s_free_test0_f } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } s_free_test0_f } + // { dg-note {a pointer to explicit object member function can only be formed with '&S::f'} "" { target *-*-* } s_free_test0_f } + void (*gp)(S&) = &s.g; // { dg-line s_free_test0_g } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } s_free_test0_g } + // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } s_free_test0_g } + // { dg-note {a pointer to explicit object member function can only be formed with '&S::g'} "" { target *-*-* } s_free_test0_g } +} + +struct D; + +struct B { + void fb(this B&) {} + + void gb(this B&) {} + void gb(this B&, int) {} + + void fd(this D&) {} + + void gd(this D&) {} + void gd(this D&, int) {} +}; + +struct D : B { + void fb2(this B&) {} + + void gb2(this B&) {} + void gb2(this B&, int) {} + + void fd2(this D&) {} + + void gd2(this D&) {} + void gd2(this D&, int) {} + + void test0() { + void (*fbp)(B&) = &fb; // { dg-line d_test0_fb } + // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test0_fb } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::fb'} "" { target *-*-* } d_test0_fb } + void (*gbp)(B&) = &gb; // { dg-line d_test0_gb } + // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test0_gb } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::gb'} "PR113075" { xfail *-*-* } d_test0_gb } + // { dg-bogus {a pointer to explicit object member function can only be formed with '&B::gb'} "PR113075" { xfail *-*-* } d_test0_gb } + + void (*fdp)(D&) = &fd; // { dg-line d_test0_fd } + // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test0_fd } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::fd'} "" { target *-*-* } d_test0_fd } + void (*gdp)(D&) = &gd; // { dg-line d_test0_gd } + // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test0_gd } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::gd'} "PR113075" { xfail *-*-* } d_test0_gd } + // { dg-bogus {a pointer to explicit object member function can only be formed with '&B::gd'} "PR113075" { xfail *-*-* } d_test0_gd } + } + + void test1(this B& self) { + void (*fbp)(B&) = &self.fb; // { dg-line d_test1_fb } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_test1_fb } + // { dg-note {a pointer to explicit object member function can only be formed with '&B::fb'} "" { target *-*-* } d_test1_fb } + void (*gbp)(B&) = &self.gb; // { dg-line d_test1_gb } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test1_gb } + // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test1_gb } + // { dg-note {a pointer to explicit object member function can only be formed with '&B::gb'} "" { target *-*-* } d_test1_gb } + + void (*fdp)(D&) = &self.fd; // { dg-line d_test1_fd } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_test1_fd } + // { dg-note {a pointer to explicit object member function can only be formed with '&B::fd'} "" { target *-*-* } d_test1_fd } + void (*gdp)(D&) = &self.gd; // { dg-line d_test1_gd } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test1_gd } + // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test1_gd } + // { dg-note {a pointer to explicit object member function can only be formed with '&B::gd'} "" { target *-*-* } d_test1_gd } + } + + void test2(this D& self) { + void (*fbp)(B&) = &self.fb; // { dg-line d_test2_fb } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_test2_fb } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::fb'} "" { target *-*-* } d_test2_fb } + void (*gbp)(B&) = &self.gb; // { dg-line d_test2_gb } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test2_gb } + // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test2_gb } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::gb'} "PR113075" { xfail *-*-* } d_test2_gb } + // { dg-bogus {a pointer to explicit object member function can only be formed with '&B::gb'} "PR113075" { xfail *-*-* } d_test2_gb } + + void (*fdp)(D&) = &self.fd; // { dg-line d_test2_fd } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_test2_fd } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::fd'} "" { target *-*-* } d_test2_fd } + void (*gdp)(D&) = &self.gd; // { dg-line d_test2_gd } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test2_gd } + // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test2_gd } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::gd'} "PR113075" { xfail *-*-* } d_test2_gd } + // { dg-bogus {a pointer to explicit object member function can only be formed with '&B::gd'} "PR113075" { xfail *-*-* } d_test2_gd } + } + + void test3() { + void (*fbp)(B&) = &fb2; // { dg-line d_test3_fb2 } + // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test3_fb2 } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::fb2'} "" { target *-*-* } d_test3_fb2 } + void (*gbp)(B&) = &gb2; // { dg-line d_test3_gb2 } + // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test3_gb2 } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::gb2'} "" { target *-*-* } d_test3_gb2 } + + void (*fdp)(D&) = &fd2; // { dg-line d_test3_fd2 } + // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test3_fd2 } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::fd2'} "" { target *-*-* } d_test3_fd2 } + void (*gdp)(D&) = &gd2; // { dg-line d_test3_gd2 } + // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test3_gd2 } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::gd2'} "" { target *-*-* } d_test3_gd2 } + } + + void test4(this D& self) { + void (*fbp)(B&) = &self.fb2; // { dg-line d_test4_fb2 } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_test4_fb2 } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::fb2'} "" { target *-*-* } d_test4_fb2 } + void (*gbp)(B&) = &self.gb2; // { dg-line d_test4_gb2 } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test4_gb2 } + // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test4_gb2 } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::gb2'} "" { target *-*-* } d_test4_gb2 } + + void (*fdp)(D&) = &self.fd2; // { dg-line d_test4_fd2 } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_test4_fd2 } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::fd2'} "" { target *-*-* } d_test4_fd2 } + void (*gdp)(D&) = &self.gd2; // { dg-line d_test4_gd2 } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test4_gd2 } + // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test4_gd2 } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::gd2'} "" { target *-*-* } d_test4_gd2 } + } +}; + +void test1() +{ + D d{}; + + void (*fbp)(B&) = &d.fb; // { dg-line d_free_test1_fb } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_free_test1_fb } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::fb'} "" { target *-*-* } d_free_test1_fb } + void (*gbp)(B&) = &d.gb; // { dg-line d_free_test1_gb } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test1_gb } + // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test1_gb } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::gb'} "PR113075" { xfail *-*-* } d_free_test1_gb } + // { dg-bogus {a pointer to explicit object member function can only be formed with '&B::gb'} "PR113075" { xfail *-*-* } d_free_test1_gb } + + void (*fdp)(D&) = &d.fd; // { dg-line d_free_test1_fd } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_free_test1_fd } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::fd'} "" { target *-*-* } d_free_test1_fd } + void (*gdp)(D&) = &d.gd; // { dg-line d_free_test1_gd } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test1_gd } + // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test1_gd } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::gd'} "PR113075" { xfail *-*-* } d_free_test1_gd } + // { dg-bogus {a pointer to explicit object member function can only be formed with '&B::gd'} "PR113075" { xfail *-*-* } d_free_test1_gd } +} + +void test2() +{ + D d{}; + + void (*fbp)(B&) = &d.fb2; // { dg-line d_free_test2_fb2 } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_free_test2_fb2 } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::fb2'} "" { target *-*-* } d_free_test2_fb2 } + void (*gbp)(B&) = &d.gb2; // { dg-line d_free_test2_gb2 } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test2_gb2 } + // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test2_gb2 } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::gb2'} "" { target *-*-* } d_free_test2_gb2 } + + void (*fdp)(D&) = &d.fd2; // { dg-line d_free_test2_fd2 } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_free_test2_fd2 } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::fd2'} "" { target *-*-* } d_free_test2_fd2 } + void (*gdp)(D&) = &d.gd2; // { dg-line d_free_test2_gd2 } + // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test2_gd2 } + // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test2_gd2 } + // { dg-note {a pointer to explicit object member function can only be formed with '&D::gd2'} "" { target *-*-* } d_free_test2_gd2 } +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C new file mode 100644 index 0000000..023cdc2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C @@ -0,0 +1,95 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// diagnose xobj member functions that override +// or are declared as virtual, override, or final + +struct B { + virtual void f0() {} // { dg-note {virtual function declared here} } + virtual void f1() {} // { dg-note {virtual function declared here} } + virtual void f2() {} // { dg-note {virtual function declared here} } + virtual void f3() {} // { dg-note {virtual function declared here} } + virtual void f4() {} // { dg-note {virtual function declared here} } + virtual void f5() {} // { dg-note {virtual function declared here} } + virtual void f6() {} // { dg-note {virtual function declared here} } + virtual void f7() {} // { dg-note {virtual function declared here} } + virtual ~B() {} +}; + +struct S : B { + virtual void f0(this S&) {} // { dg-line line_f0 } + virtual void f1(this S&) override {} // { dg-line line_f1 } + virtual void f2(this S&) final {} // { dg-line line_f2 } + virtual void f3(this S&) override final {} // { dg-line line_f3 } + void f4(this S&) {} // { dg-line line_f4 } + void f5(this S&) override {} // { dg-line line_f5 } + void f6(this S&) final {} // { dg-line line_f6 } + void f7(this S&) override final {} // { dg-line line_f7 } +}; + +// { dg-error {an explicit object member function cannot be 'virtual'} "" { target *-*-* } line_f0 } +// { dg-error {an explicit object member function cannot be 'virtual'} "" { target *-*-* } line_f1 } +// { dg-error {an explicit object member function cannot be 'virtual'} "" { target *-*-* } line_f2 } +// { dg-error {an explicit object member function cannot be 'virtual'} "" { target *-*-* } line_f3 } + +// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f0 } +// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f1 } +// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f2 } +// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f3 } +// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f4 } +// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f5 } +// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f6 } +// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f7 } + +// these should be suppressed, the wording conflicts with the error +// the issue is not that they don't override, it's that they do override, and that isn't allowed +// { dg-bogus "marked 'override', but does not override" "" { xfail *-*-* } line_f1 } +// { dg-bogus "marked 'final', but is not virtual" "" { xfail *-*-* } line_f2 } +// { dg-bogus "marked '(override|final)'" "" { xfail *-*-* } line_f3 } + +// { dg-bogus "marked 'override', but does not override" "" { xfail *-*-* } line_f5 } +// { dg-bogus "marked 'final', but is not virtual" "" { xfail *-*-* } line_f6 } +// { dg-bogus "marked '(override|final)'" "" { xfail *-*-* } line_f7 } + +// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_f0 } +// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_f1 } +// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_f2 } +// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_f3 } +// { dg-note "explicit object parameter declared here" "" { xfail *-*-* } line_f4 } +// { dg-note "explicit object parameter declared here" "" { xfail *-*-* } line_f5 } +// { dg-note "explicit object parameter declared here" "" { xfail *-*-* } line_f6 } +// { dg-note "explicit object parameter declared here" "" { xfail *-*-* } line_f7 } + +struct S1 { + virtual void f0(this S&) {} // { dg-line line_S1_f0 } + virtual void f1(this S&) override {} // { dg-line line_S1_f1 } + virtual void f2(this S&) final {} // { dg-line line_S1_f2 } + virtual void f3(this S&) override final {} // { dg-line line_S1_f3 } + void f4(this S&) {} + void f5(this S&) override {} // { dg-line line_S1_f5 } + void f6(this S&) final {} // { dg-line line_S1_f6 } + void f7(this S&) override final {} // { dg-line line_S1_f7 } +}; + +// { dg-error "an explicit object member function cannot be 'virtual'" "" { target *-*-* } line_S1_f0 } +// { dg-error "an explicit object member function cannot be 'virtual'" "" { target *-*-* } line_S1_f1 } +// { dg-error "an explicit object member function cannot be 'virtual'" "" { target *-*-* } line_S1_f2 } +// { dg-error "an explicit object member function cannot be 'virtual'" "" { target *-*-* } line_S1_f3 } + +// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_S1_f0 } +// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_S1_f1 } +// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_S1_f2 } +// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_S1_f3 } + +// I think I want these suppressed, but theres a decent argument that they should stay +// theres arguably no reason the error about virtual should suppress these +// { dg-bogus "marked 'override', but does not override" "" { xfail *-*-* } line_S1_f1 } +// { dg-bogus "marked 'final', but is not virtual" "" { xfail *-*-* } line_S1_f2 } +// { dg-bogus "marked '(override|final)'" "" { xfail *-*-* } line_S1_f3 } + +// I don't want to suppress these, there is nothing that could possibly be overridden +// even if the xobj param was removed +// { dg-error "marked 'override', but does not override" "" { target *-*-* } line_S1_f5 } +// { dg-error "marked 'final', but is not virtual" "" { target *-*-* } line_S1_f6 } +// { dg-error "marked '(override|final)'" "" { target *-*-* } line_S1_f7 } + diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C index 2b21bd1..d81aab7 100644 --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C @@ -578,6 +578,12 @@ # error "__cpp_implicit_move != 202207" #endif +#ifndef __cpp_explicit_this_parameter +# error "__cpp_explicit_this_parameter" +#elif __cpp_explicit_this_parameter != 202110 +# error "__cpp_explicit_this_parameter != 202110" +#endif + #ifndef __cpp_auto_cast # error "__cpp_auto_cast" #elif __cpp_auto_cast != 202110 diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C index 4507ea0..d19fca4 100644 --- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C +++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C @@ -578,6 +578,12 @@ # error "__cpp_implicit_move != 202207" #endif +#ifndef __cpp_explicit_this_parameter +# error "__cpp_explicit_this_parameter" +#elif __cpp_explicit_this_parameter != 202110 +# error "__cpp_explicit_this_parameter != 202110" +#endif + #ifndef __cpp_auto_cast # error "__cpp_auto_cast" #elif __cpp_auto_cast != 202110 -- cgit v1.1 From 07d09f0af100a9873982fba663800d87bfd73585 Mon Sep 17 00:00:00 2001 From: waffl3x Date: Sun, 7 Jan 2024 00:53:32 +0000 Subject: c++: P0847R7 (deducing this) - xobj lambdas. [PR102609] This implements support for xobj lambdas. There are extensive tests included, but not exhaustive. Dependent lambdas should work and have been tested lightly, but we need more exhaustive tests for them. PR c++/102609 gcc/cp/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - xobj lambdas. * lambda.cc (build_capture_proxy): Don't fold direct object types. * parser.cc (cp_parser_lambda_declarator_opt): Handle xobj lambdas, diagnostics. Comments also updated. * pt.cc (tsubst_function_decl): Handle xobj lambdas. Check object type of xobj lambda call operator, diagnose incorrect types. (tsubst_lambda_expr): Update comment. * semantics.cc (finish_decltype_type): Also consider by-value object parameter qualifications. gcc/testsuite/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - xobj lambdas. * g++.dg/cpp23/explicit-obj-diagnostics8.C: New test. * g++.dg/cpp23/explicit-obj-lambda1.C: New test. * g++.dg/cpp23/explicit-obj-lambda10.C: New test. * g++.dg/cpp23/explicit-obj-lambda11.C: New test. * g++.dg/cpp23/explicit-obj-lambda12.C: New test. * g++.dg/cpp23/explicit-obj-lambda13.C: New test. * g++.dg/cpp23/explicit-obj-lambda2.C: New test. * g++.dg/cpp23/explicit-obj-lambda3.C: New test. * g++.dg/cpp23/explicit-obj-lambda4.C: New test. * g++.dg/cpp23/explicit-obj-lambda5.C: New test. * g++.dg/cpp23/explicit-obj-lambda6.C: New test. * g++.dg/cpp23/explicit-obj-lambda7.C: New test. * g++.dg/cpp23/explicit-obj-lambda8.C: New test. * g++.dg/cpp23/explicit-obj-lambda9.C: New test. Signed-off-by: Waffl3x --- gcc/cp/lambda.cc | 4 +- gcc/cp/parser.cc | 84 +- gcc/cp/pt.cc | 74 +- gcc/cp/semantics.cc | 10 +- .../g++.dg/cpp23/explicit-obj-diagnostics8.C | 68 ++ gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C | 25 + gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda10.C | 39 + gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda11.C | 46 ++ gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda12.C | 103 +++ gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda13.C | 103 +++ gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C | 23 + gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda3.C | 64 ++ gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda4.C | 23 + gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda5.C | 21 + gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda6.C | 873 +++++++++++++++++++++ gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda7.C | 20 + gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda8.C | 87 ++ gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda9.C | 46 ++ 18 files changed, 1699 insertions(+), 14 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics8.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda10.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda11.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda12.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda13.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda3.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda4.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda5.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda6.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda7.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda8.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda9.C diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc index 04f159fe8..1d37e5a 100644 --- a/gcc/cp/lambda.cc +++ b/gcc/cp/lambda.cc @@ -404,8 +404,10 @@ build_capture_proxy (tree member, tree init) fn = lambda_function (closure); lam = CLASSTYPE_LAMBDA_EXPR (closure); + object = DECL_ARGUMENTS (fn); /* The proxy variable forwards to the capture field. */ - object = build_fold_indirect_ref (DECL_ARGUMENTS (fn)); + if (INDIRECT_TYPE_P (TREE_TYPE (object))) + object = build_fold_indirect_ref (object); object = finish_non_static_data_member (member, object, NULL_TREE); if (REFERENCE_REF_P (object)) object = TREE_OPERAND (object, 0); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index b0c08cf..d71522d 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -11874,8 +11874,12 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) else if (cxx_dialect < cxx23) omitted_parms_loc = cp_lexer_peek_token (parser->lexer)->location; - /* In the decl-specifier-seq of the lambda-declarator, each - decl-specifier shall either be mutable or constexpr. */ + /* [expr.prim.lambda.general] + lambda-specifier: + consteval, constexpr, mutable, static + [4] A lambda-specifier-seq shall contain at most one of each + lambda-specifier and shall not contain both constexpr and consteval. + The lambda-specifier-seq shall not contain both mutable and static. */ int declares_class_or_enum; if (cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer)) cp_parser_decl_specifier_seq (parser, @@ -11890,13 +11894,83 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) "%<-std=gnu++2b%>"); omitted_parms_loc = UNKNOWN_LOCATION; } - - if (lambda_specs.storage_class == sc_mutable) + /* Peek at the params, see if we have an xobj parameter. */ + if (param_list && TREE_PURPOSE (param_list) == this_identifier) + { + quals = TYPE_UNQUALIFIED; + /* We still need grokdeclarator to see that this is an xobj function + and finish the rest of the work, don't mutate it. */ + tree const xobj_param = TREE_VALUE (param_list); + tree const param_type = TREE_TYPE (xobj_param); + /* [expr.prim.lambda.closure-5] + Given a lambda with a lambda-capture, the type of the explicit object + parameter, if any, of the lambda's function call operator (possibly + instantiated from a function call operator template) shall be either: + -- the closure type, + -- a class type derived from the closure type, or + -- a reference to a possibly cv-qualified such type. */ + bool const unrelated_with_captures + = (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE + || LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)) + /* Since a lambda's type is anonymous, we can assume an xobj + parameter is unrelated to the closure if it is non-dependent. + If it is dependent we handle it at instantiation time. */ + && !WILDCARD_TYPE_P (non_reference (param_type)); + if (unrelated_with_captures) + { + error_at (DECL_SOURCE_LOCATION (xobj_param), + "a lambda with captures may not have an explicit object " + "parameter of an unrelated type"); + LAMBDA_EXPR_CAPTURE_LIST (lambda_expr) = NULL_TREE; + } + + /* [expr.prim.lambda.general-4] + If the lambda-declarator contains an explicit object parameter + ([dcl.fct]), then no lambda-specifier in the lambda-specifier-seq + shall be mutable or static. */ + if (lambda_specs.storage_class == sc_mutable) + { + auto_diagnostic_group d; + error_at (lambda_specs.locations[ds_storage_class], + "% lambda specifier " + "with explicit object parameter"); + /* Tell the user how to do what they probably meant, maybe fixits + would be appropriate later? */ + if (unrelated_with_captures) + /* The following hints don't make sense when we already have an + unrelated type with captures, don't emit them. */; + else if (!TYPE_REF_P (param_type)) + inform (DECL_SOURCE_LOCATION (xobj_param), + "the passed in closure object will not be mutated because " + "it is taken by value"); + else if (TYPE_READONLY (TREE_TYPE (param_type))) + inform (DECL_SOURCE_LOCATION (xobj_param), + "declare the explicit object parameter as non-const " + "reference instead"); + else + inform (DECL_SOURCE_LOCATION (xobj_param), + "explicit object parameter is already a mutable " + "reference"); + } + else if (lambda_specs.storage_class == sc_static) + { + auto_diagnostic_group d; + error_at (lambda_specs.locations[ds_storage_class], + "% lambda specifier " + "with explicit object parameter"); + inform (DECL_SOURCE_LOCATION (xobj_param), + "explicit object parameter declared here"); + } + } + else if (lambda_specs.storage_class == sc_mutable) { quals = TYPE_UNQUALIFIED; } else if (lambda_specs.storage_class == sc_static) { + /* [expr.prim.lambda.general-4] + If the lambda-specifier-seq contains static, there shall be no + lambda-capture. */ if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE || LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)) error_at (lambda_specs.locations[ds_storage_class], @@ -12021,7 +12095,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) { DECL_INITIALIZED_IN_CLASS_P (fco) = 1; DECL_ARTIFICIAL (fco) = 1; - if (!LAMBDA_EXPR_STATIC_P (lambda_expr)) + if (DECL_IOBJ_MEMBER_FUNCTION_P (fco)) /* Give the object parameter a different name. */ DECL_NAME (DECL_ARGUMENTS (fco)) = closure_identifier; DECL_SET_LAMBDA_FUNCTION (fco, true); diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index da4b7fa..6900d17 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -14490,9 +14490,9 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain, tree ctx = closure ? closure : DECL_CONTEXT (t); bool member = ctx && TYPE_P (ctx); - /* If this is a static lambda, remove the 'this' pointer added in + /* If this is a static or xobj lambda, remove the 'this' pointer added in tsubst_lambda_expr now that we know the closure type. */ - if (lambda_fntype && DECL_STATIC_FUNCTION_P (t)) + if (lambda_fntype && !DECL_IOBJ_MEMBER_FUNCTION_P (t)) lambda_fntype = static_fn_type (lambda_fntype); if (member && !closure) @@ -14567,12 +14567,12 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain, DECL_NAME (r) = make_conv_op_name (TREE_TYPE (type)); tree parms = DECL_ARGUMENTS (t); - if (closure && !DECL_STATIC_FUNCTION_P (t)) + if (closure && DECL_IOBJ_MEMBER_FUNCTION_P (t)) parms = DECL_CHAIN (parms); parms = tsubst (parms, args, complain, t); for (tree parm = parms; parm; parm = DECL_CHAIN (parm)) DECL_CONTEXT (parm) = r; - if (closure && !DECL_STATIC_FUNCTION_P (t)) + if (closure && DECL_IOBJ_MEMBER_FUNCTION_P (t)) { tree tparm = build_this_parm (r, closure, type_memfn_quals (type)); DECL_NAME (tparm) = closure_identifier; @@ -14608,6 +14608,66 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain, && !grok_op_properties (r, /*complain=*/false)) return error_mark_node; + /* If we are looking at an xobj lambda, we might need to check the type of + its xobj parameter. */ + if (LAMBDA_FUNCTION_P (r) && DECL_XOBJ_MEMBER_FUNCTION_P (r)) + { + tree closure_obj = DECL_CONTEXT (r); + tree lambda_expr = CLASSTYPE_LAMBDA_EXPR (closure_obj); + tree obj_param = TREE_TYPE (DECL_ARGUMENTS (r)); + + if (!(LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE + || LAMBDA_EXPR_CAPTURE_LIST (lambda_expr))) + /* If a lambda has an empty capture clause, an xobj parameter of + unrelated type is not an error. */; + else if (dependent_type_p (obj_param)) + /* If we are coming from tsubst_lambda_expr we might not have + substituted into our xobj parameter yet. We can't error out until + we know what the type really is so do nothing... + ...but if we are instantiating the call op for real and we don't + have a real type then something has gone incredibly wrong. */ + gcc_assert (lambda_fntype); + else + { + /* We have a lambda with captures, and know the type of the xobj + parameter, time to check it. */ + tree obj_param_type = TYPE_MAIN_VARIANT (non_reference (obj_param)); + if (!same_or_base_type_p (closure_obj, obj_param_type)) + { + /* This error does not emit when the lambda's call operator + template is instantiated by taking its address, such as in + the following case: + + auto f = [x = 0](this auto&&){}; + int (*fp)(int&) = &decltype(f)::operator(); + + It only emits when explicitly calling the call operator with + an explicit template parameter: + + template + struct S : T { + using T::operator(); + operator int() const {return {};} + }; + + auto s = S{[x = 0](this auto&&) {}}; + s.operator()(); + + This is due to resolve_address_of_overloaded_function being + deficient at reporting candidates when overload resolution + fails. + + This diagnostic will be active in the first case if/when + resolve_address_of_overloaded_function is fixed to properly + emit candidates upon failure to resolve to an overload. */ + if (complain & tf_error) + error ("a lambda with captures may not have an explicit " + "object parameter of an unrelated type"); + return error_mark_node; + } + } + } + /* Associate the constraints directly with the instantiation. We don't substitute through the constraints; that's only done when they are checked. */ @@ -19595,7 +19655,11 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) which would be skipped if cp_unevaluated_operand. */ cp_evaluated ev; - /* Fix the type of 'this'. */ + /* Fix the type of 'this'. + For static and xobj member functions we use this to transport the + lambda's closure type. It appears that in the regular case the + object parameter is still pulled off, and then re-added again anyway. + So perhaps we could do something better here? */ fntype = build_memfn_type (fntype, type, type_memfn_quals (fntype), type_memfn_rqual (fntype)); diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 3253cee..86e1adf 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -11933,9 +11933,13 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p, if (WILDCARD_TYPE_P (non_reference (obtype))) /* We don't know what the eventual obtype quals will be. */ goto dependent; - int quals = cp_type_quals (type); - if (INDIRECT_TYPE_P (obtype)) - quals |= cp_type_quals (TREE_TYPE (obtype)); + auto direct_type = [](tree t){ + if (INDIRECT_TYPE_P (t)) + return TREE_TYPE (t); + return t; + }; + int const quals = cp_type_quals (type) + | cp_type_quals (direct_type (obtype)); type = cp_build_qualified_type (type, quals); type = build_reference_type (type); } diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics8.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics8.C new file mode 100644 index 0000000..7b75dc9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics8.C @@ -0,0 +1,68 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// xobj lambda with invalid decl specs + +void test() +{ + auto f0 = [](this auto) mutable {}; // { dg-line line_f0 } + auto f1 = [](this auto&) mutable {}; // { dg-line line_f1 } + auto f2 = [](this auto const&) mutable {}; // { dg-line line_f2 } + auto f3 = [](this auto&&) mutable {}; // { dg-line line_f3 } + + auto g0 = [](this auto) static {}; // { dg-line line_g0 } + auto g1 = [](this auto&) static {}; // { dg-line line_g1 } + auto g2 = [](this auto const&) static {}; // { dg-line line_g2 } + auto g3 = [](this auto&&) static {}; // { dg-line line_g3 } + + auto fc0 = [n = 0](this auto) mutable {}; // { dg-line line_fc0 } + auto fc1 = [n = 0](this auto&) mutable {}; // { dg-line line_fc1 } + auto fc2 = [n = 0](this auto const&) mutable {}; // { dg-line line_fc2 } + auto fc3 = [n = 0](this auto&&) mutable {}; // { dg-line line_fc3 } + + auto gc0 = [n = 0](this auto) static {}; // { dg-line line_gc0 } + auto gc1 = [n = 0](this auto&) static {}; // { dg-line line_gc1 } + auto gc2 = [n = 0](this auto const&) static {}; // { dg-line line_gc2 } + auto gc3 = [n = 0](this auto&&) static {}; // { dg-line line_gc3 } +} + +// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_f0 } +// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_f1 } +// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_f2 } +// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_f3 } + +// { dg-note {the passed in closure object will not be mutated because it is taken by value} {} { target *-*-* } line_f0 } +// { dg-note {explicit object parameter is already a mutable reference} {} { target *-*-* } line_f1 } +// { dg-note {declare the explicit object parameter as non-const reference instead} {} { target *-*-* } line_f2 } +// { dg-note {explicit object parameter is already a mutable reference} {} { target *-*-* } line_f3 } + +// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_g0 } +// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_g1 } +// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_g2 } +// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_g3 } + +// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_g0 } +// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_g1 } +// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_g2 } +// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_g3 } + +// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_fc0 } +// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_fc1 } +// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_fc2 } +// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_fc3 } + +// { dg-note {the passed in closure object will not be mutated because it is taken by value} {} { target *-*-* } line_fc0 } +// { dg-note {explicit object parameter is already a mutable reference} {} { target *-*-* } line_fc1 } +// { dg-note {declare the explicit object parameter as non-const reference instead} {} { target *-*-* } line_fc2 } +// { dg-note {explicit object parameter is already a mutable reference} {} { target *-*-* } line_fc3 } + +// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_gc0 } +// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_gc1 } +// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_gc2 } +// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_gc3 } + +// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_gc0 } +// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_gc1 } +// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_gc2 } +// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_gc3 } + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C new file mode 100644 index 0000000..86e0471 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C @@ -0,0 +1,25 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// lambda declaration with xobj parameter + +struct S{}; + +void test() +{ + (void)[](this auto&& self){}; + (void)[](this auto& self){}; + (void)[](this auto const& self){}; + (void)[](this auto self){}; + + (void)[](this S&& self){}; + (void)[](this S& self){}; + (void)[](this S const& self){}; + (void)[](this S self){}; + + (void)[x = 0](this auto&& self){}; + (void)[x = 0](this auto& self){}; + (void)[x = 0](this auto const& self){}; + (void)[x = 0](this auto self){}; +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda10.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda10.C new file mode 100644 index 0000000..715a245 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda10.C @@ -0,0 +1,39 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// instantiating captureless lambda call operator with unrelated explicit object parameter + +void test0() +{ + auto f0 = [](this auto self) { return self; }; + auto fp0_value = static_cast(&decltype(f0)::operator()); + auto fp0_lref = static_cast(&decltype(f0)::operator()); + auto fp0_rref = static_cast(&decltype(f0)::operator()); + auto fp0_constlref = static_cast(&decltype(f0)::operator()); + auto fp0_constrref = static_cast(&decltype(f0)::operator()); + + auto f1 = [](this auto&& self) { return self; }; + auto fp1_value = static_cast(&decltype(f1)::operator()); // { dg-error {invalid 'static_cast' from type} } + auto fp1_lref = static_cast(&decltype(f1)::operator()); + auto fp1_rref = static_cast(&decltype(f1)::operator()); + auto fp1_constlref = static_cast(&decltype(f1)::operator()); + auto fp1_constrref = static_cast(&decltype(f1)::operator()); +} + +void test1() +{ + auto f0 = [](this auto self) { return self; }; + int (*fp0_value)(int) = &decltype(f0)::operator(); + int (*fp0_lref)(int&) = &decltype(f0)::operator(); + int (*fp0_rref)(int&&) = &decltype(f0)::operator(); + int (*fp0_constlref)(int const&) = &decltype(f0)::operator(); + int (*fp0_constrref)(int const&&) = &decltype(f0)::operator(); + + auto f1 = [](this auto&& self) { return self; }; + int (*fp1_value)(int) = &decltype(f1)::operator(); // { dg-error {no matches converting function} } + int (*fp1_lref)(int&) = &decltype(f1)::operator(); + int (*fp1_rref)(int&&) = &decltype(f1)::operator(); + int (*fp1_constlref)(int const&) = &decltype(f1)::operator(); + int (*fp1_constrref)(int const&&) = &decltype(f1)::operator(); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda11.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda11.C new file mode 100644 index 0000000..7f2bdb8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda11.C @@ -0,0 +1,46 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// unrelated xobj parameter type in captureless lambdas and lambdas with captures + +struct S0{}; + +void test0() +{ + auto f0 = [](this S0){ return 5; }; // { dg-bogus "a lambda with captures may not have an explicit object parameter of an unrelated type" } + auto f1 = [x = 42](this S0){ return 5; }; // { dg-error "a lambda with captures may not have an explicit object parameter of an unrelated type" } +} + +// instantiation by calling with explicit template arguments + +template +struct S1 : T { + using T::operator(); + operator int() const {return {};} +}; + +void test1() +{ + auto s0 = S1{[](this auto&& self) { return self; }}; // { dg-bogus {a lambda with captures may not have an explicit object parameter of an unrelated type} } + s0.operator()(); // { dg-bogus {no matching function for call to} } + + auto s1 = S1{[x = 0](this auto&& self) { return self; }}; // { dg-line t1_s1 } + s1.operator()(); // { dg-error {no matching function for call to} } +} +// { dg-note {candidate:} {} { target *-*-* } t1_s1 } +// { dg-note {template argument deduction/substitution failed} {} { target *-*-* } t1_s1 } +// { dg-error {a lambda with captures may not have an explicit object parameter of an unrelated type} {} { target *-*-* } t1_s1 } + +// instantiation from overload resolution when taking address of call operator + +void test2() +{ + auto f = [x = 42](this auto&&){ return x; }; // { dg-line t2_f } + + int (*fp0)(decltype(f)&) = &decltype(f)::operator(); + int (*fp1)(int&) = &decltype(f)::operator(); // { dg-error {no matches converting function} } +} + +// { dg-error "a lambda with captures may not have an explicit object parameter of an unrelated type" {depends on PR112874} { xfail *-*-* } t2_f } +// { dg-note "candidate is" "" { target *-*-* } t2_f } + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda12.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda12.C new file mode 100644 index 0000000..8b6c2ba --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda12.C @@ -0,0 +1,103 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// SFINAE when the call operator for a lambda with captures is instantiated +// with an unrelated type. +// diagnose ambiguous overloads when the call operator for a captureless lambda is instantiated +// with an unrelated type. + +// overload resolution when taking address of function + +/* [expr.prim.lambda.general-5] + + Given a lambda with a lambda-capture, the type of the explicit object + parameter, if any, of the lambda's function call operator (possibly + instantiated from a function call operator template) shall be either: + + --(5.1) the closure type, + --(5.2) a class type derived from the closure type, or + --(5.3) a reference to a possibly cv-qualified such type. */ + +// The above wording is similar to [dcl.fct-15] which is handled by SFINAE, +// thus we also handle the following cases the same way. + +// We need the 2 overloads to be ambiguous to observe substitution failure +// for the lambda's call operator when instantiated with an unrelated type. +// We accomplish this by introducing both overloads through using declarations. + +struct B0 { + void operator()(this auto) {} +}; +template +struct S0 : T, B0 { + using B0::operator(); + using T::operator(); +}; + +void test0() +{ + auto s0 = S0{[](this auto){}}; + void (*p0)(int) = &decltype(s0)::operator(); // { dg-error {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} } + + auto s1 = S0{[x = 42](this auto){}}; + void (*p1)(int) = &decltype(s1)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} {Substitution failure for a captureful lambda with an unrelated xobj parameter type failed!} } +} + +struct B1 { + void operator()(this auto&&) {} +}; +template +struct S1 : T, B1 { + using B1::operator(); + using T::operator(); +}; + +void test1() +{ + auto s0 = S1{[](this auto&&){}}; + void (*p0)(int&) = &decltype(s0)::operator(); // { dg-error {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} } + + auto s1 = S1{[x = 42](this auto&&){}}; + void (*p1)(int&) = &decltype(s1)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} {Substitution failure for a captureful lambda with an unrelated xobj parameter type failed!} } +} + + +struct B2 { + // not a template, should be taken over the lambda's call operator + void operator()(this int&) {} +}; +template +struct S2 : T, B2 { + using T::operator(); + using B2::operator(); +}; + +void test2() +{ + auto s0 = S2{[](this auto&&){}}; + void (*p0)(int&) = &decltype(s0)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} } + + auto s1 = S2{[x = 42](this auto&&){}}; + void (*p1)(int&) = &decltype(s1)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} } +} + +struct B3 { + // must be a template so it is not taken over the lambda's call operator + template + void operator()(this int&) {} +}; +template +struct S3 : T, B3 { + using B3::operator(); + using T::operator(); +}; + +void test3() +{ + auto s0 = S3{[](this auto&&){}}; + void (*p0)(int&) = &decltype(s0)::operator(); // { dg-error {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} } + + auto s1 = S3{[x = 42](this auto&&){}}; + void (*p1)(int&) = &decltype(s1)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} {Substitution failure for a captureful lambda with an unrelated xobj parameter type failed!} } +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda13.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda13.C new file mode 100644 index 0000000..c48de47 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda13.C @@ -0,0 +1,103 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// SFINAE when the call operator for a lambda with captures is instantiated +// with an unrelated type. +// diagnose ambiguous overloads when the call operator for a captureless lambda is instantiated +// with an unrelated type. + +// overload resolution from call expression + +/* [expr.prim.lambda.general-5] + + Given a lambda with a lambda-capture, the type of the explicit object + parameter, if any, of the lambda's function call operator (possibly + instantiated from a function call operator template) shall be either: + + --(5.1) the closure type, + --(5.2) a class type derived from the closure type, or + --(5.3) a reference to a possibly cv-qualified such type. */ + +// The above wording is similar to [dcl.fct-15] which is handled by SFINAE, +// thus we also handle the following cases the same way. + +// We need the 2 overloads to be ambiguous to observe substitution failure +// for the lambda's call operator when instantiated with an unrelated type. +// We accomplish this by introducing both overloads through using declarations. + +struct B0 { + void operator()(this auto) {} +}; + +template +struct S0 : T, B0 { + using T::operator(); + using B0::operator(); + operator int() const {return {};} +}; + +void test0() +{ + auto s0 = S0{[](this auto){}}; + s0.operator()(); // { dg-error {call of overloaded 'operator\(\)\(\)' is ambiguous} } + + auto s1 = S0{[x = 42](this auto){}}; + s1.operator()(); // { dg-bogus {call of overloaded 'operator\(\)\(\)' is ambiguous} } +} + + +struct B1 { + void operator()(this auto&&) {} +}; +template +struct S1 : T, B1 { + using T::operator(); + using B1::operator(); + operator int() const {return {};} +}; + +void test1() +{ + auto s0 = S1{[](this auto&&){}}; + s0.operator()(); // { dg-error {call of overloaded 'operator\(\)\(\)' is ambiguous} } + + auto s1 = S1{[x = 42](this auto&&){}}; + s1.operator()(); // { dg-bogus {call of overloaded 'operator\(\)\(\)' is ambiguous} } +} + + +struct B2 { + // needs to be a template, we are explicitly passing a template argument, + // without the parameter here this would not be a candidate + template + void operator()(this int) {} +}; + +template +struct S2 : T, B2 { + using T::operator(); + using B2::operator(); + operator int() const {return {};} +}; + +// I don't know why the calls to s0::operator() are not ambiguous, it might have to do with one taking less conversions, I'm not sure. +// Someone who knows better should remove those cases if they are sure they are actually correct. + +void test2() +{ + auto s0 = S2{[](this auto){}}; + s0.operator()(); // { dg-error {call of overloaded 'operator\(\)\(\)' is ambiguous} {Not sure if this is a bug, one might be a better conversion} { xfail *-*-* } } + + auto s1 = S2{[x = 42](this auto){}}; + s1.operator()(); // { dg-bogus {call of overloaded 'operator\(\)\(\)' is ambiguous} } +} + +void test3() +{ + auto s0 = S2{[](this auto&&){}}; + s0.operator()(); // { dg-error {call of overloaded 'operator\(\)\(\)' is ambiguous} {Not sure if this is a bug, one might be a better conversion} { xfail *-*-* } } + + auto s1 = S2{[x = 42](this auto&&){}}; + s1.operator()(); // { dg-bogus {call of overloaded 'operator\(\)\(\)' is ambiguous} } +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C new file mode 100644 index 0000000..827197a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C @@ -0,0 +1,23 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// recursive lambdas + +inline constexpr int correct_result = 5 + 4 + 3 + 2 + 1; + +int main() +{ + auto cl0 = [](this auto&& self, int n) -> int { return n ? self(n - 1) + n : 0; }; + auto cl1 = [](this auto const& self, int n) -> int { return n ? self(n - 1) + n : 0; }; + auto cl2 = [](this auto self, int n) -> int { return n ? self(n - 1) + n : 0; }; + auto cl3 = [](this auto&& self, int n) { if (!n) return 0; else return self(n - 1) + n; }; + auto cl4 = [](this auto const& self, int n){ if (!n) return 0; else return self(n - 1) + n; }; + auto cl5 = [](this auto self, int n) { if (!n) return 0; else return self(n - 1) + n; }; + if (cl0(5) != correct_result) __builtin_abort (); + if (cl1(5) != correct_result) __builtin_abort (); + if (cl2(5) != correct_result) __builtin_abort (); + if (cl3(5) != correct_result) __builtin_abort (); + if (cl4(5) != correct_result) __builtin_abort (); + if (cl5(5) != correct_result) __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda3.C new file mode 100644 index 0000000..9d222b0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda3.C @@ -0,0 +1,64 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// an adaptation of one of the examples in P0847R7 + +struct Leaf { }; +struct Node; + +struct Tree { + enum class stored {leaf, node}; + stored _discriminator; + union { + Leaf _leaf; + Node* _node; + }; + Tree(Leaf) : _discriminator(stored::leaf), _leaf() {} + Tree(Node& node) : _discriminator(stored::node), _node(&node) {} +}; + +struct Node { + Tree left; + Tree right; +}; + +template +auto visit_tree(Visitor&& visitor, Tree const& tree) +{ + switch (tree._discriminator) + { + case Tree::stored::leaf: + return visitor (tree._leaf); + case Tree::stored::node: + return visitor (tree._node); + default: + __builtin_abort (); + } +} + +template +struct overload : Ts... { using Ts::operator()...; }; + +int main() +{ + static constexpr int true_num_leaves = 8; + Node branch0{.left = Leaf{}, .right = Leaf{}}; + Node branch1{.left = Leaf{}, .right = branch0}; + Node branch2{.left = Leaf{}, .right = Leaf{}}; + Node branch3{.left = branch1, .right = branch2}; + Node branch4{.left = branch3, .right = Leaf{}}; + Node branch5{.left = Leaf{}, .right = Leaf{}}; + Node branch6{.left = branch4, .right = branch5}; + + Tree root (branch6); + + int num_leaves = visit_tree (overload{ + [](Leaf const&) { return 1; }, + [](this auto const& self, Node* n) -> int { + return visit_tree (self, n->left) + visit_tree (self, n->right); + }}, + root); + if (num_leaves != true_num_leaves) + __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda4.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda4.C new file mode 100644 index 0000000..6ce42eb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda4.C @@ -0,0 +1,23 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// calls to call operator of a lambda with captures with an implicit object argument +// that derives from the lambda closure object + +template +struct S : T { + using T::operator(); +}; + +template +S(T) -> S; + +int main() +{ + static constexpr int magic = 42; + int n = magic; + S s{[n](this auto&&){return n;}}; + if (s () != magic) + __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda5.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda5.C new file mode 100644 index 0000000..88d45d9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda5.C @@ -0,0 +1,21 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// calls to (captureless) lambda with explicit object parameter of unrelated type +// with an appropriate converting constructor + +inline constexpr int magic = 42; + +struct S { + int _v; + template + S(T) : _v(magic) {} +}; + +int main() +{ + auto f = [](this S self){ return self._v; }; + if (f () != magic) + __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda6.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda6.C new file mode 100644 index 0000000..aa56306 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda6.C @@ -0,0 +1,873 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// decltype((x)) and decltype(x) in explicit object lambda + +template inline constexpr bool is_const_v = false; +template inline constexpr bool is_const_v = true; + +template inline constexpr bool is_lvalue_ref = false; +template inline constexpr bool is_lvalue_ref = true; + +void non_dep() +{ + int n = 0; + int const c = 0; + // value + auto f0_value = [=](this Self){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f0_value(); + f0_value.operator()(); + + auto f1_value = [&](this Self){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f1_value(); + f1_value.operator()(); + + auto f2_value = [n, c](this Self){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f2_value(); + f2_value.operator()(); + + auto f3_value = [&n, &c](this Self){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f3_value(); + f3_value.operator()(); + + auto f4_value = [](this Self){ + static_assert(__is_same (decltype(n), int)); + static_assert(__is_same (decltype((n)), int&)); + + static_assert(__is_same (decltype(c), int const)); + static_assert(__is_same (decltype((c)), int const&)); + }; + f4_value(); + f4_value.operator()(); + + // ref + auto f0_ref = [=](this Self&){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f0_ref(); + f0_ref.operator()(); + static_cast(f0_ref)(); + + auto f1_ref = [&](this Self&){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f1_ref(); + f1_ref.operator()(); + static_cast(f1_ref)(); + + auto f2_ref = [n, c](this Self&){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f2_ref(); + f2_ref.operator()(); + static_cast(f2_ref)(); + + auto f3_ref = [&n, &c](this Self&){ + static_assert(__is_same(decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f3_ref(); + f3_ref.operator()(); + static_cast(f3_ref)(); + + auto f4_ref = [](this Self&){ + static_assert(__is_same (decltype(n), int)); + static_assert(__is_same (decltype((n)), int&)); + + static_assert(__is_same (decltype(c), int const)); + static_assert(__is_same (decltype((c)), int const&)); + }; + f4_ref(); + f4_ref.operator()(); + static_cast(f4_ref)(); + + // const value + auto f0_const_value = [=](this Self const){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f0_const_value(); + + auto f1_const_value = [&](this Self const){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f1_const_value(); + + auto f2_const_value = [n, c](this Self const){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f2_const_value(); + + auto f3_const_value = [&n, &c](this Self const){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f3_const_value(); + + auto f4_const_value = [](this Self const){ + static_assert(__is_same (decltype(n), int)); + static_assert(__is_same (decltype((n)), int&)); + + static_assert(__is_same (decltype(c), int const)); + static_assert(__is_same (decltype((c)), int const&)); + }; + f4_const_value(); + + // const ref + auto f0_const_ref = [=](this Self const&){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f0_const_ref(); + + auto f1_const_ref = [&](this Self const&){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f1_const_ref(); + + auto f2_const_ref = [n, c](this Self const&){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f2_const_ref(); + + auto f3_const_ref = [&n, &c](this Self const&){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f3_const_ref(); + + auto f4_const_ref = [](this Self const&){ + static_assert(__is_same (decltype(n), int)); + static_assert(__is_same (decltype((n)), int&)); + + static_assert(__is_same (decltype(c), int const)); + static_assert(__is_same (decltype((c)), int const&)); + }; + f4_const_ref(); +} + +template +void dep0() +{ + int n = 0; + int const c = 0; + // value + auto f0_value = [=](this Self){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f0_value(); + f0_value.template operator()(); + + auto f1_value = [&](this Self){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f1_value(); + f1_value.template operator()(); + + auto f2_value = [n, c](this Self){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f2_value(); + f2_value.template operator()(); + + auto f3_value = [&n, &c](this Self){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f3_value(); + f3_value.template operator()(); + + auto f4_value = [](this Self){ + static_assert(__is_same (decltype(n), int)); + static_assert(__is_same (decltype((n)), int&)); + + static_assert(__is_same (decltype(c), int const)); + static_assert(__is_same (decltype((c)), int const&)); + }; + f4_value(); + f4_value.template operator()(); + + // ref + auto f0_ref = [=](this Self&){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f0_ref(); + f0_ref.template operator()(); + static_cast(f0_ref)(); + + auto f1_ref = [&](this Self&){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f1_ref(); + f1_ref.template operator()(); + static_cast(f1_ref)(); + + auto f2_ref = [n, c](this Self&){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f2_ref(); + f2_ref.template operator()(); + static_cast(f2_ref)(); + + auto f3_ref = [&n, &c](this Self&){ + static_assert(__is_same(decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f3_ref(); + f3_ref.template operator()(); + static_cast(f3_ref)(); + + auto f4_ref = [](this Self&){ + static_assert(__is_same (decltype(n), int)); + static_assert(__is_same (decltype((n)), int&)); + + static_assert(__is_same (decltype(c), int const)); + static_assert(__is_same (decltype((c)), int const&)); + }; + f4_ref(); + f4_ref.template operator()(); + static_cast(f4_ref)(); + + // const value + auto f0_const_value = [=](this Self const){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f0_const_value(); + + auto f1_const_value = [&](this Self const){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f1_const_value(); + + auto f2_const_value = [n, c](this Self const){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f2_const_value(); + + auto f3_const_value = [&n, &c](this Self const){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f3_const_value(); + + auto f4_const_value = [](this Self const){ + static_assert(__is_same (decltype(n), int)); + static_assert(__is_same (decltype((n)), int&)); + + static_assert(__is_same (decltype(c), int const)); + static_assert(__is_same (decltype((c)), int const&)); + }; + f4_const_value(); + + // const ref + auto f0_const_ref = [=](this Self const&){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f0_const_ref(); + + auto f1_const_ref = [&](this Self const&){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f1_const_ref(); + + auto f2_const_ref = [n, c](this Self const&){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f2_const_ref(); + + auto f3_const_ref = [&n, &c](this Self const&){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f3_const_ref(); + + auto f4_const_ref = [](this Self const&){ + static_assert(__is_same (decltype(n), int)); + static_assert(__is_same (decltype((n)), int&)); + + static_assert(__is_same (decltype(c), int const)); + static_assert(__is_same (decltype((c)), int const&)); + }; + f4_const_ref(); +} + +// dep1 uses the template parameter + +template +void dep1() +{ + T n = 0; + T const c = 0; + // value + auto f0_value = [=](this Self){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f0_value(); + f0_value.template operator()(); + + auto f1_value = [&](this Self){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f1_value(); + f1_value.template operator()(); + + auto f2_value = [n, c](this Self){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f2_value(); + f2_value.template operator()(); + + auto f3_value = [&n, &c](this Self){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f3_value(); + f3_value.template operator()(); + + auto f4_value = [](this Self){ + static_assert(__is_same (decltype(n), int)); + static_assert(__is_same (decltype((n)), int&)); + + static_assert(__is_same (decltype(c), int const)); + static_assert(__is_same (decltype((c)), int const&)); + }; + f4_value(); + f4_value.template operator()(); + + // ref + auto f0_ref = [=](this Self&){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f0_ref(); + f0_ref.template operator()(); + static_cast(f0_ref)(); + + auto f1_ref = [&](this Self&){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f1_ref(); + f1_ref.template operator()(); + static_cast(f1_ref)(); + + auto f2_ref = [n, c](this Self&){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f2_ref(); + f2_ref.template operator()(); + static_cast(f2_ref)(); + + auto f3_ref = [&n, &c](this Self&){ + static_assert(__is_same(decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f3_ref(); + f3_ref.template operator()(); + static_cast(f3_ref)(); + + auto f4_ref = [](this Self&){ + static_assert(__is_same (decltype(n), int)); + static_assert(__is_same (decltype((n)), int&)); + + static_assert(__is_same (decltype(c), int const)); + static_assert(__is_same (decltype((c)), int const&)); + }; + f4_ref(); + f4_ref.template operator()(); + static_cast(f4_ref)(); + + // const value + auto f0_const_value = [=](this Self const){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f0_const_value(); + + auto f1_const_value = [&](this Self const){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f1_const_value(); + + auto f2_const_value = [n, c](this Self const){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f2_const_value(); + + auto f3_const_value = [&n, &c](this Self const){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f3_const_value(); + + auto f4_const_value = [](this Self const){ + static_assert(__is_same (decltype(n), int)); + static_assert(__is_same (decltype((n)), int&)); + + static_assert(__is_same (decltype(c), int const)); + static_assert(__is_same (decltype((c)), int const&)); + }; + f4_const_value(); + + // const ref + auto f0_const_ref = [=](this Self const&){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f0_const_ref(); + + auto f1_const_ref = [&](this Self const&){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f1_const_ref(); + + auto f2_const_ref = [n, c](this Self const&){ + static_assert(is_lvalue_ref, + "decltype((n)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} } + "qualification of decltype((n)) does not match qualification of Self"); + static_assert(__is_same (__remove_cvref (decltype((n))), int), + "decltype((n)) is not an int"); + static_assert(__is_same (decltype(n), int)); + + static_assert(is_lvalue_ref, + "decltype((c)) is not an lvalue ref"); + static_assert(is_const_v<__remove_reference (decltype((c)))>, + "qualification of decltype((c)) is not const"); + static_assert(__is_same (__remove_cvref (decltype((c))), int), + "decltype((c)) is not an int"); + static_assert(__is_same (decltype(c), int const)); + }; + f2_const_ref(); + + auto f3_const_ref = [&n, &c](this Self const&){ + static_assert(__is_same (decltype((n)), int&)); + static_assert(__is_same (decltype(n), int)); + + static_assert(__is_same (decltype((c)), int const&)); + static_assert(__is_same (decltype(c), int const)); + }; + f3_const_ref(); + + auto f4_const_ref = [](this Self const&){ + static_assert(__is_same (decltype(n), int)); + static_assert(__is_same (decltype((n)), int&)); + + static_assert(__is_same (decltype(c), int const)); + static_assert(__is_same (decltype((c)), int const&)); + }; + f4_const_ref(); +} + +void instantiate_dep() +{ + dep0(); + dep1(); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda7.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda7.C new file mode 100644 index 0000000..1e10fb2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda7.C @@ -0,0 +1,20 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// diagnose mutation of lambda capture when called with a deduced as const explicit object parameter + +void test() +{ + auto f0 = [n = 5](this auto){ n = 10; }; // { dg-bogus {assignment of read-only variable} } + auto f1 = [n = 5](this auto const){ n = 10; }; // { dg-error {assignment of read-only variable} } + auto f2 = [n = 5](this auto&){ n = 10; }; // { dg-error {assignment of read-only variable} } + auto f3 = [n = 5](this auto const&){ n = 10; }; // { dg-error {assignment of read-only variable} } + auto f4 = [n = 5](this auto&&){ n = 10; }; // { dg-error {assignment of read-only variable} } + + static_cast(f0)(); + f1(); + static_cast(f2)(); + f3(); + static_cast(f4)(); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda8.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda8.C new file mode 100644 index 0000000..a068941 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda8.C @@ -0,0 +1,87 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// lambda capture mutability with explicit object parameter + +void capture_by_value() +{ + static constexpr int magic = 42; + auto f0 = [n = 0](this auto self){ + n += magic; + return n; + }; + auto f1 = [n = 0](this auto& self){ + n += magic; + return n; + }; + auto f2 = [n = 0](this auto&& self){ + n += magic; + return n; + }; + + // passed by value, should still return a value equal to magic regardless + // of how many times it is called + if (f0 () != magic) + __builtin_abort (); + if (f0 () != magic) + __builtin_abort (); + // passed by reference, the returned value should increase by magic + // each time it is called + if (f1 () != magic) + __builtin_abort (); + if (f1 () != magic + magic) + __builtin_abort (); + if (f2 () != magic) + __builtin_abort (); + if (f2 () != magic + magic) + __builtin_abort (); +} + +void capture_by_ref() +{ + static constexpr int magic = 42; + int n0 = 0; + auto f0 = [&n0](this auto self){ + n0 += magic; + }; + int n1 = 0; + auto f1 = [&n1](this auto& self){ + n1 += magic; + }; + int n2 = 0; + auto f2 = [&n2](this auto&& self){ + n2 += magic; + }; + int n3 = 0; + auto f3 = [&n3](this auto const& self){ + n3 += magic; + }; + + // all calls should mutate their capture, the capture is by reference + if (f0 (); n0 != magic) + __builtin_abort (); + if (f0 (); n0 != magic + magic) + __builtin_abort (); + + if (f1 (); n1 != magic) + __builtin_abort (); + if (f1 (); n1 != magic + magic) + __builtin_abort (); + + if (f2 (); n2 != magic) + __builtin_abort (); + if (f2 (); n2 != magic + magic) + __builtin_abort (); + + if (f3 (); n3 != magic) + __builtin_abort (); + if (f3 (); n3 != magic + magic) + __builtin_abort (); +} + +int main() +{ + capture_by_value (); + capture_by_ref (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda9.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda9.C new file mode 100644 index 0000000..a808019 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda9.C @@ -0,0 +1,46 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// calling captureless lambda call operator with unrelated explicit object parameter +// through function pointer + +int main() +{ + auto f0 = [](this auto self) { return self; }; + auto fp0_value = static_cast(&decltype(f0)::operator()); + auto fp0_lref = static_cast(&decltype(f0)::operator()); + auto fp0_rref = static_cast(&decltype(f0)::operator()); + auto fp0_constlref = static_cast(&decltype(f0)::operator()); + auto fp0_constrref = static_cast(&decltype(f0)::operator()); + + auto f1 = [](this auto&& self) { return self; }; + auto fp1_lref = static_cast(&decltype(f1)::operator()); + auto fp1_rref = static_cast(&decltype(f1)::operator()); + auto fp1_constlref = static_cast(&decltype(f1)::operator()); + auto fp1_constrref = static_cast(&decltype(f1)::operator()); + + // both are needed for lvalue/rvalue overloads + #define MAGIC 42 + int magic = MAGIC; + + if (fp0_value (magic) != magic) + __builtin_abort (); + if (fp0_lref (magic) != magic) + __builtin_abort (); + if (fp0_rref (MAGIC) != magic) + __builtin_abort (); + if (fp0_constlref (magic) != magic) + __builtin_abort (); + if (fp0_constrref (MAGIC) != magic) + __builtin_abort (); + + if (fp1_lref (magic) != magic) + __builtin_abort (); + if (fp1_rref (MAGIC) != magic) + __builtin_abort (); + if (fp1_constlref (magic) != magic) + __builtin_abort (); + if (fp1_constrref (MAGIC) != magic) + __builtin_abort (); +} + -- cgit v1.1 From bfad006b88ec26e91b7edf9cf9ad4aaf9b8a9727 Mon Sep 17 00:00:00 2001 From: waffl3x Date: Sun, 7 Jan 2024 23:10:00 +0000 Subject: c++: P0847R7 (deducing this) - CWG2586 [PR102609] This adds support for defaulted comparison operators and copy/move assignment operators, as well as allowing user defined xobj copy/move assignment operators. It turns out defaulted comparison operators already worked though, so this just adds a test for them. Defaulted comparison operators were not so nice and required a bit of a hack. Should work fine though! The diagnostics leave something to be desired, and there are some things that could be improved with more extensive design changes. There are a few notes left indicating where I think we could make improvements. Aside from some small bugs, with this commit xobj member functions should be feature complete. PR c++/102609 gcc/cp/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - CWG2586. * decl.cc (copy_fn_p): Accept xobj copy assignment functions. (move_signature_fn_p): Accept xobj move assignment functions. * method.cc (do_build_copy_assign): Handle defaulted xobj member functions. (defaulted_late_check): Comment. (defaultable_fn_check): Comment. gcc/testsuite/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - CWG2586. * g++.dg/cpp23/explicit-obj-basic6.C: New test. * g++.dg/cpp23/explicit-obj-default1.C: New test. * g++.dg/cpp23/explicit-obj-default2.C: New test. Signed-off-by: Waffl3x --- gcc/cp/decl.cc | 28 +++++++++- gcc/cp/method.cc | 55 ++++++++++++++++-- gcc/testsuite/g++.dg/cpp23/explicit-obj-basic6.C | 51 +++++++++++++++++ gcc/testsuite/g++.dg/cpp23/explicit-obj-default1.C | 57 +++++++++++++++++++ gcc/testsuite/g++.dg/cpp23/explicit-obj-default2.C | 65 ++++++++++++++++++++++ 5 files changed, 248 insertions(+), 8 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-basic6.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-default1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-default2.C diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 7f26705..b10a72a 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -15663,7 +15663,19 @@ copy_fn_p (const_tree d) && DECL_NAME (d) != assign_op_identifier) return 0; - args = FUNCTION_FIRST_USER_PARMTYPE (d); + if (DECL_XOBJ_MEMBER_FUNCTION_P (d)) + { + tree object_param = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (d))); + if (!TYPE_REF_P (object_param) + || TYPE_REF_IS_RVALUE (object_param) + /* Reject unrelated object parameters. */ + || TYPE_MAIN_VARIANT (TREE_TYPE (object_param)) != DECL_CONTEXT (d) + || CP_TYPE_CONST_P (TREE_TYPE (object_param))) + return 0; + args = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (d))); + } + else + args = FUNCTION_FIRST_USER_PARMTYPE (d); if (!args) return 0; @@ -15738,7 +15750,19 @@ move_signature_fn_p (const_tree d) && DECL_NAME (d) != assign_op_identifier) return 0; - args = FUNCTION_FIRST_USER_PARMTYPE (d); + if (DECL_XOBJ_MEMBER_FUNCTION_P (d)) + { + tree object_param = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (d))); + if (!TYPE_REF_P (object_param) + || TYPE_REF_IS_RVALUE (object_param) + /* Reject unrelated object parameters. */ + || TYPE_MAIN_VARIANT (TREE_TYPE (object_param)) != DECL_CONTEXT (d) + || CP_TYPE_CONST_P (TREE_TYPE (object_param))) + return 0; + args = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (d))); + } + else + args = FUNCTION_FIRST_USER_PARMTYPE (d); if (!args) return 0; diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc index aa5a044..da6a08a 100644 --- a/gcc/cp/method.cc +++ b/gcc/cp/method.cc @@ -795,13 +795,19 @@ do_build_copy_assign (tree fndecl) compound_stmt = begin_compound_stmt (0); parm = convert_from_reference (parm); + /* If we are building a defaulted xobj copy/move assignment operator then + current_class_ref will not have been set up. + Kind of an icky hack, but what can ya do? */ + tree const class_ref = DECL_XOBJ_MEMBER_FUNCTION_P (fndecl) + ? cp_build_fold_indirect_ref (DECL_ARGUMENTS (fndecl)) : current_class_ref; + if (trivial && is_empty_class (current_class_type)) /* Don't copy the padding byte; it might not have been allocated if *this is a base subobject. */; else if (trivial) { - tree t = build2 (MODIFY_EXPR, void_type_node, current_class_ref, parm); + tree t = build2 (MODIFY_EXPR, void_type_node, class_ref, parm); finish_expr_stmt (t); } else @@ -826,7 +832,7 @@ do_build_copy_assign (tree fndecl) /* Call the base class assignment operator. */ releasing_vec parmvec (make_tree_vector_single (converted_parm)); finish_expr_stmt - (build_special_member_call (current_class_ref, + (build_special_member_call (class_ref, assign_op_identifier, &parmvec, base_binfo, @@ -839,7 +845,7 @@ do_build_copy_assign (tree fndecl) fields; fields = DECL_CHAIN (fields)) { - tree comp = current_class_ref; + tree comp = class_ref; tree init = parm; tree field = fields; tree expr_type; @@ -898,7 +904,7 @@ do_build_copy_assign (tree fndecl) finish_expr_stmt (init); } } - finish_return_stmt (current_class_ref); + finish_return_stmt (class_ref); finish_compound_stmt (compound_stmt); } @@ -3380,13 +3386,46 @@ defaulted_late_check (tree fn) NULL, NULL); tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn)); + /* Includes special handling for a default xobj operator. */ + auto compare_fn_params = [](tree fn, tree implicit_fn){ + tree fn_parms = TYPE_ARG_TYPES (TREE_TYPE (fn)); + tree implicit_fn_parms = TYPE_ARG_TYPES (TREE_TYPE (implicit_fn)); + + if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)) + { + tree fn_obj_ref_type = TREE_VALUE (fn_parms); + /* We can't default xobj operators with an xobj parameter that is not + an lvalue reference. */ + if (!TYPE_REF_P (fn_obj_ref_type) + || TYPE_REF_IS_RVALUE (fn_obj_ref_type)) + return false; + /* If implicit_fn's object parameter is not a pointer, something is not + right. (Or we have finally changed the type of the iobj parameter + in iobj member functions.) */ + gcc_assert (TYPE_PTR_P (TREE_VALUE (implicit_fn_parms))); + /* Strip the reference/pointer off each object parameter before + comparing them. */ + if (!same_type_p (TREE_TYPE (fn_obj_ref_type), + TREE_TYPE (TREE_VALUE (implicit_fn_parms)))) + return false; + /* We just compared the object parameters, skip over them before + passing to compparms. */ + fn_parms = TREE_CHAIN (fn_parms); + implicit_fn_parms = TREE_CHAIN (implicit_fn_parms); + } + return compparms(fn_parms, implicit_fn_parms); + }; + if (!same_type_p (TREE_TYPE (TREE_TYPE (fn)), TREE_TYPE (TREE_TYPE (implicit_fn))) - || !compparms (TYPE_ARG_TYPES (TREE_TYPE (fn)), - TYPE_ARG_TYPES (TREE_TYPE (implicit_fn)))) + || !compare_fn_params (fn, implicit_fn)) { error ("defaulted declaration %q+D does not match the " "expected signature", fn); + /* FIXME: If the user is defaulting an xobj member function we should + emit an xobj member function for a signature. When we do this, maybe + we can just synthesize implicit_fn as an xobj member function and + avoid the dance in compare_fn_parms. */ inform (DECL_SOURCE_LOCATION (fn), "expected signature: %qD", implicit_fn); } @@ -3473,6 +3512,10 @@ defaultable_fn_check (tree fn) return false; } + /* FIXME: We need to check for xobj member functions here to give better + diagnostics for weird cases where unrelated xobj parameters are given. + We just want to do better than 'cannot be defaulted'. */ + if (kind == sfk_none) { error ("%qD cannot be defaulted", fn); diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic6.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic6.C new file mode 100644 index 0000000..6b8881e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic6.C @@ -0,0 +1,51 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// user defined copy/move assignment operators + +inline constexpr int add_when_copy = 5; +inline constexpr int add_when_move = 10; +inline constexpr int poison = -1; + +struct S { + int _v; + S& operator=(this S& self, S const& rhs) { + self._v = rhs._v + add_when_copy; + return self; + }; + S& operator=(this S& self, S&& rhs) { + self._v = rhs._v + add_when_move; + rhs._v = poison; + return self; + } +}; + +inline constexpr int init_val = 5; + +int main() +{ + S s0{0}; + S s1{init_val}; + + // Sanity check. + if (s0._v != 0 + || s1._v != init_val) + __builtin_abort (); + + s0 = s1; + if (s0._v != init_val + add_when_copy) + __builtin_abort (); + if (s1._v != init_val) + __builtin_abort (); + + s0 = S{init_val}; + if (s0._v != init_val + add_when_move) + __builtin_abort (); + + S s2{init_val}; + s0 = static_cast(s2); + if (s0._v != init_val + add_when_move) + __builtin_abort (); + if (s2._v != poison) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-default1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-default1.C new file mode 100644 index 0000000..fac3c28 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-default1.C @@ -0,0 +1,57 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// defaulted comparison operators + +#include + +struct S { + int _v; + bool operator==(this S const&, S const&) = default; + auto operator<=>(this S const&, S const&) = default; +}; + +int main() +{ + S const a_10{10}; + S const b_10{10}; + S const c_20{20}; + S const d_5{5}; + + if (a_10 != b_10) + __builtin_abort (); + if (c_20 == a_10) + __builtin_abort (); + if (!(a_10 == b_10)) + __builtin_abort (); + if (!(c_20 != a_10)) + __builtin_abort (); + + if (a_10 < b_10) + __builtin_abort (); + if (a_10 > b_10) + __builtin_abort (); + if (!(a_10 <= b_10)) + __builtin_abort (); + if (!(a_10 >= b_10)) + __builtin_abort (); + + if (!(a_10 < c_20)) + __builtin_abort (); + if (a_10 > c_20) + __builtin_abort (); + if (!(a_10 <= c_20)) + __builtin_abort (); + if (a_10 >= c_20) + __builtin_abort (); + + if (a_10 < d_5) + __builtin_abort (); + if (!(a_10 > d_5)) + __builtin_abort (); + if (a_10 <= d_5) + __builtin_abort (); + if (!(a_10 >= d_5)) + __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-default2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-default2.C new file mode 100644 index 0000000..786f0c2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-default2.C @@ -0,0 +1,65 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// defaulted copy/move assignment operators + +inline constexpr int add_when_copy = 5; +inline constexpr int add_when_move = 10; +inline constexpr int poison = -1; + +struct A { + int _v; + A(int v) : _v(v) {} + A& operator=(A const& rhs) { + if (&rhs == this) + return *this; + _v = rhs._v + add_when_copy; + return *this; + } + A& operator=(A&& rhs) { + if (&rhs == this) + return *this; + _v = rhs._v + add_when_move; + rhs._v = poison; + return *this; + } +}; + +struct S { + A _a; + S& operator=(this S&, S const&) = default; + S& operator=(this S&, S&&) = default; + + int v() const { return _a._v; } +}; + +inline constexpr int init_val = 5; + +int main() +{ + S s0{0}; + S s1{init_val}; + + // Sanity check. + if (s0.v () != 0 + || s1.v () != init_val) + __builtin_abort (); + + s0 = s1; + if (s0.v () != init_val + add_when_copy) + __builtin_abort (); + if (s1.v () != init_val) + __builtin_abort (); + + s0 = S{init_val}; + if (s0.v () != init_val + add_when_move) + __builtin_abort (); + + S s2{init_val}; + s0 = static_cast(s2); + if (s0.v () != init_val + add_when_move) + __builtin_abort (); + if (s2.v () != poison) + __builtin_abort (); +} + -- cgit v1.1 From 5a6d3b1737843aa64d83ffc5d639fa0afa5d8318 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Tue, 9 Jan 2024 16:00:52 -0500 Subject: c++: explicit object cleanups The FIXME in xobj_iobj_parameters_correspond was due to expecting TYPE_MAIN_VARIANT to be the same for all equivalent types, which is not the case. And I adjusted some comments that I disagree with; the iobj parameter adjustment only applies to overload resolution, we can handle that in cand_parms_match (and I have WIP for that). gcc/cp/ChangeLog: * call.cc (build_over_call): Refactor handle_arg lambda. * class.cc (xobj_iobj_parameters_correspond): Fix FIXME. * method.cc (defaulted_late_check): Adjust comments. --- gcc/cp/call.cc | 24 ++++++++++++------------ gcc/cp/class.cc | 40 ++++++++++++---------------------------- gcc/cp/method.cc | 7 +------ 3 files changed, 25 insertions(+), 46 deletions(-) diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index dca8e50..7d3d676 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -10187,11 +10187,11 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) parm = TREE_CHAIN (parm); } - auto handle_arg = [fn, flags, complain](tree type, - tree arg, - int const param_index, - conversion *conv, - bool const conversion_warning) + auto handle_arg = [fn, flags](tree type, + tree arg, + int const param_index, + conversion *conv, + tsubst_flags_t const arg_complain) { /* Set user_conv_p on the argument conversions, so rvalue/base handling knows not to allow any more UDCs. This needs to happen after we @@ -10199,9 +10199,6 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (flags & LOOKUP_NO_CONVERSION) conv->user_conv_p = true; - tsubst_flags_t const arg_complain - = conversion_warning ? complain : complain & ~tf_warning; - if (arg_complain & tf_warning) maybe_warn_pessimizing_move (arg, type, /*return_p=*/false); @@ -10214,13 +10211,12 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)) { gcc_assert (cand->num_convs > 0); - static constexpr bool conversion_warning = true; tree object_arg = consume_object_arg (); val = handle_arg (TREE_VALUE (parm), object_arg, param_index++, convs[conv_index++], - conversion_warning); + complain); if (val == error_mark_node) return error_mark_node; @@ -10260,11 +10256,14 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) && cand->template_decl && !cand->explicit_targs); + tsubst_flags_t const arg_complain + = conversion_warning ? complain : complain & ~tf_warning; + val = handle_arg (TREE_VALUE (parm), current_arg, param_index, convs[conv_index], - conversion_warning); + arg_complain); if (val == error_mark_node) return error_mark_node; @@ -10273,7 +10272,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) } /* Default arguments */ - for (; parm && parm != void_list_node; parm = TREE_CHAIN (parm), param_index++) + for (; parm && parm != void_list_node; + parm = TREE_CHAIN (parm), param_index++) { if (TREE_VALUE (parm) == error_mark_node) return error_mark_node; diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index f3cfa9f..e5e609b 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -1020,9 +1020,12 @@ modify_vtable_entry (tree t, /* Check if the object parameters of an xobj and iobj member function - correspond. This function assumes that the iobj parameter has been correctly - adjusted when the function is introduced by a using declaration per - [over.match.funcs.general.4]. */ + correspond. This function assumes that the iobj parameter has been + correctly adjusted when the function is introduced by a using declaration + per [over.match.funcs.general.4]. + + ??? But it isn't, that's only considered at overload resolution time. + cand_parms_match will probably need to check cand->conversion_path. */ bool xobj_iobj_parameters_correspond (tree fn1, tree fn2) @@ -1112,29 +1115,10 @@ xobj_iobj_parameters_correspond (tree fn1, tree fn2) handles xobj parameters of pointer type, we don't have to explicitly check for that case. */ - /* FIXME: - - template - struct S; - - template - struct B { - int f(this S&) requires true { return 5; } - }; - - template - struct S : B { - using B::f; - int f() { return 10; } - }; - - This case is broken, the incomplete type seems to screw with things. - I'm not sure how to fix that so I'm just noting the issue here, I have a - feeling it's trivial to do if you know how. */ - - if (TYPE_MAIN_VARIANT (iobj_param_type) - != TYPE_MAIN_VARIANT (non_reference (xobj_param))) + if (!same_type_ignoring_top_level_qualifiers_p + (iobj_param_type, non_reference (xobj_param))) return false; + /* We don't get to bail yet even if we have a by-value xobj parameter, a by-value xobj parameter can correspond to an iobj parameter provided the iobj member function is not declared with a reference qualifier. @@ -8976,9 +8960,9 @@ resolve_address_of_overloaded_function (tree target_type, documentation for -fms-extensions states it's purpose is to support the use of microsoft headers. Until otherwise demonstrated, we should assume xobj member functions are not used in this manner in microsoft - headers and indiscriminately forbid the incorrect syntax instead of - supporting it for non-legacy uses. This should hopefully encourage - conformance going forward. + headers and forbid the incorrect syntax instead of supporting it for + non-legacy uses. This should hopefully encourage conformance going + forward. This comment is referred to in typeck.cc:cp_build_addr_expr_1. */ if (DECL_IOBJ_MEMBER_FUNCTION_P (fn) && flag_ms_extensions) /* Early escape. */; diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc index da6a08a..6a9f03e 100644 --- a/gcc/cp/method.cc +++ b/gcc/cp/method.cc @@ -3400,8 +3400,7 @@ defaulted_late_check (tree fn) || TYPE_REF_IS_RVALUE (fn_obj_ref_type)) return false; /* If implicit_fn's object parameter is not a pointer, something is not - right. (Or we have finally changed the type of the iobj parameter - in iobj member functions.) */ + right. */ gcc_assert (TYPE_PTR_P (TREE_VALUE (implicit_fn_parms))); /* Strip the reference/pointer off each object parameter before comparing them. */ @@ -3422,10 +3421,6 @@ defaulted_late_check (tree fn) { error ("defaulted declaration %q+D does not match the " "expected signature", fn); - /* FIXME: If the user is defaulting an xobj member function we should - emit an xobj member function for a signature. When we do this, maybe - we can just synthesize implicit_fn as an xobj member function and - avoid the dance in compare_fn_parms. */ inform (DECL_SOURCE_LOCATION (fn), "expected signature: %qD", implicit_fn); } -- cgit v1.1 From ae3003b20d3e3ab6e50a6d4f2173e10ad9025135 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 8 Jan 2024 17:09:54 -0500 Subject: c++: adjust accessor fixits for explicit object parm In a couple of places in the xobj patch I noticed that is_this_parameter probably wanted to change to is_object_parameter; this implements that and does the additional adjustments needed to make the accessor fixits handle xobj parms. gcc/cp/ChangeLog: * semantics.cc (is_object_parameter): New. * cp-tree.h (is_object_parameter): Declare. * call.cc (maybe_warn_class_memaccess): Use it. * search.cc (field_access_p): Use it. (class_of_object_parm): New. (field_accessor_p): Adjust for explicit object parms. gcc/testsuite/ChangeLog: * g++.dg/torture/accessor-fixits-9-xobj.C: New test. --- gcc/cp/call.cc | 3 +- gcc/cp/cp-tree.h | 1 + gcc/cp/search.cc | 19 +++- gcc/cp/semantics.cc | 14 +++ .../g++.dg/torture/accessor-fixits-9-xobj.C | 119 +++++++++++++++++++++ 5 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 gcc/testsuite/g++.dg/torture/accessor-fixits-9-xobj.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 7d3d676..191664e 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -10815,8 +10815,7 @@ maybe_warn_class_memaccess (location_t loc, tree fndecl, be more permissive. */ if (current_function_decl && DECL_OBJECT_MEMBER_FUNCTION_P (current_function_decl) - /* ??? is_object_parameter? */ - && is_this_parameter (tree_strip_nop_conversions (dest))) + && is_object_parameter (tree_strip_nop_conversions (dest))) { tree ctx = DECL_CONTEXT (current_function_decl); bool special = same_type_ignoring_top_level_qualifiers_p (ctx, desttype); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index dbc7177..f3f265a 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7784,6 +7784,7 @@ extern void finish_handler_parms (tree, tree); extern void finish_handler (tree); extern void finish_cleanup (tree, tree); extern bool is_this_parameter (tree); +extern bool is_object_parameter (tree); enum { BCS_NORMAL = 0, diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc index dc76714..2b4ed5d 100644 --- a/gcc/cp/search.cc +++ b/gcc/cp/search.cc @@ -1737,8 +1737,7 @@ field_access_p (tree component_ref, tree field_decl, tree field_type) return false; tree ptr = STRIP_NOPS (TREE_OPERAND (indirect_ref, 0)); - /* ??? is_object_parameter? */ - if (!is_this_parameter (ptr)) + if (!is_object_parameter (ptr)) return false; /* Must access the correct field. */ @@ -1818,6 +1817,17 @@ reference_accessor_p (tree init_expr, tree field_decl, tree field_type, return true; } +/* Return the class of the `this' or explicit object parameter of FN. */ + +static tree +class_of_object_parm (const_tree fn) +{ + tree fntype = TREE_TYPE (fn); + if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)) + return non_reference (TREE_VALUE (TYPE_ARG_TYPES (fntype))); + return class_of_this_parm (fntype); +} + /* Return true if FN is an accessor method for FIELD_DECL. i.e. a method of the form { return FIELD; }, with no conversions. @@ -1835,15 +1845,14 @@ field_accessor_p (tree fn, tree field_decl, bool const_p) if (TREE_CODE (field_decl) != FIELD_DECL) return false; - tree fntype = TREE_TYPE (fn); - if (TREE_CODE (fntype) != METHOD_TYPE) + if (!DECL_OBJECT_MEMBER_FUNCTION_P (fn)) return false; /* If the field is accessed via a const "this" argument, verify that the "this" parameter is const. */ if (const_p) { - tree this_class = class_of_this_parm (fntype); + tree this_class = class_of_object_parm (fn); if (!TYPE_READONLY (this_class)) return false; } diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 86e1adf..3299e27 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -12832,6 +12832,20 @@ is_this_parameter (tree t) return true; } +/* As above, or a C++23 explicit object parameter. */ + +bool +is_object_parameter (tree t) +{ + if (is_this_parameter (t)) + return true; + if (TREE_CODE (t) != PARM_DECL) + return false; + tree ctx = DECL_CONTEXT (t); + return (ctx && DECL_XOBJ_MEMBER_FUNCTION_P (ctx) + && t == DECL_ARGUMENTS (ctx)); +} + /* Insert the deduced return type for an auto function. */ void diff --git a/gcc/testsuite/g++.dg/torture/accessor-fixits-9-xobj.C b/gcc/testsuite/g++.dg/torture/accessor-fixits-9-xobj.C new file mode 100644 index 0000000..89be978 --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/accessor-fixits-9-xobj.C @@ -0,0 +1,119 @@ +// PR c++/84993 +// { dg-options "-fdiagnostics-show-caret -std=c++23" } + +/* Misspelling (by omitting a leading "m_") of a private member for which + there's a public accessor. + + We expect a fix-it hint suggesting the accessor. */ + +class t1 +{ +public: + int get_ratio (this const t1& x) { return x.m_ratio; } + +private: + int m_ratio; +}; + +int test (t1 *ptr_1) +{ + return ptr_1->ratio; // { dg-error "'class t1' has no member named 'ratio'; did you mean 'int t1::m_ratio'\\? \\(accessible via 'int t1::get_ratio\\(this const t1&\\)'\\)" } + /* { dg-begin-multiline-output "" } + return ptr_1->ratio; + ^~~~~ + get_ratio() + { dg-end-multiline-output "" } */ +} + + +/* Misspelling of a private member for which there's a public accessor. + + We expect a fix-it hint suggesting the accessor. */ + +class t2 +{ +public: + int get_color (this const t2& x) { return x.m_color; } + +private: + int m_color; +}; + +int test (t2 *ptr_2) +{ + return ptr_2->m_colour; // { dg-error "'class t2' has no member named 'm_colour'; did you mean 'int t2::m_color'\\? \\(accessible via 'int t2::get_color\\(this const t2&\\)'\\)" } + /* { dg-begin-multiline-output "" } + return ptr_2->m_colour; + ^~~~~~~~ + get_color() + { dg-end-multiline-output "" } */ +} + + +/* Misspelling of a private member via a subclass pointer, for which there's + a public accessor in the base class. + + We expect a fix-it hint suggesting the accessor. */ + +class t3 : public t2 {}; + +int test (t3 *ptr_3) +{ + return ptr_3->m_colour; // { dg-error "'class t3' has no member named 'm_colour'; did you mean 'int t2::m_color'\\? \\(accessible via 'int t2::get_color\\(this const t2&\\)'\\)" } + /* { dg-begin-multiline-output "" } + return ptr_3->m_colour; + ^~~~~~~~ + get_color() + { dg-end-multiline-output "" } */ +} + + +/* Misspelling of a protected member, for which there's isn't a public + accessor. + + We expect no fix-it hint; instead a message identifying where the + data member was declared. */ + +class t4 +{ +protected: + int m_color; // { dg-message "declared protected here" } +}; + +int test (t4 *ptr_4) +{ + return ptr_4->m_colour; // { dg-error "'class t4' has no member named 'm_colour'; did you mean 'int t4::m_color'\\? \\(not accessible from this context\\)" } + /* { dg-begin-multiline-output "" } + return ptr_4->m_colour; + ^~~~~~~~ + { dg-end-multiline-output "" } */ + /* { dg-begin-multiline-output "" } + int m_color; + ^~~~~~~ + { dg-end-multiline-output "" } */ +} + + +/* Misspelling of a private member, for which the accessor is also private. + + We expect no fix-it hint; instead a message identifying where the + data member was declared. */ + +class t5 +{ + int get_color (this const t5& x) { return x.m_color; } + int m_color; // { dg-message "declared private here" } +}; + +int test (t5 *ptr_5) +{ + return ptr_5->m_colour; // { dg-error "'class t5' has no member named 'm_colour'; did you mean 'int t5::m_color'\\? \\(not accessible from this context\\)" } + /* { dg-begin-multiline-output "" } + return ptr_5->m_colour; + ^~~~~~~~ + { dg-end-multiline-output "" } */ + /* { dg-begin-multiline-output "" } + int m_color; + ^~~~~~~ + { dg-end-multiline-output "" } */ +} -- cgit v1.1 From 6002a3cd39c984423c59255ac780efb8b668b73a Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Thu, 4 Jan 2024 10:53:04 +0100 Subject: Fix spurious match in extract_symvers Tighten the regex to find the start of the .dynsym symtab in the readelf output to avoid matching the section symbol in the normal symtab. libstdc++-v3: * scripts/extract_symvers.in: Require final colon to only match .dsynsym in the header of the dynamic symtab. --- libstdc++-v3/scripts/extract_symvers.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libstdc++-v3/scripts/extract_symvers.in b/libstdc++-v3/scripts/extract_symvers.in index 17f0d31..6bb951c 100755 --- a/libstdc++-v3/scripts/extract_symvers.in +++ b/libstdc++-v3/scripts/extract_symvers.in @@ -52,7 +52,7 @@ SunOS) # Omit _DYNAMIC etc. for consistency with extract_symvers.pl, only # present on Solaris. ${readelf} ${lib} |\ - sed -e 's/ \[: [A-Fa-f0-9]*\] //' -e '/\.dynsym/,/^$/p;d' |\ + sed -e 's/ \[: [A-Fa-f0-9]*\] //' -e '/\.dynsym.*:$/,/^$/p;d' |\ sed -e 's/ \[: [0-9]*\] //' |\ grep -E -v ' (LOCAL|UND) ' |\ grep -E -v ' (_DYNAMIC|_GLOBAL_OFFSET_TABLE_|_PROCEDURE_LINKAGE_TABLE_|_edata|_end|_etext)$' |\ -- cgit v1.1 From ea314ccd625aada7ed8a324ac07cfc3a8aa0f03f Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Tue, 9 Jan 2024 14:43:40 +0000 Subject: libstdc++: Fix Unicode property detection functions Fix some copy & pasted logic in __is_extended_pictographic. This function should yield false for the values before the first edge, not true. Also add a missing boundary condition check in __incb_property. Also Fix an off-by-one error in _Utf_iterator::operator++() that would make dereferencing a past-the-end iterator undefined (where the intended design is that the iterator is always incrementable and dereferenceable, for better memory safety). Also simplify the grapheme view iterator, which still contained some remnants of an earlier design I was experimenting with. Slightly tweak the gen_libstdcxx_unicode_data.py script so that the _Gcb_property enumerators are in the order we encounter them in the data file, instead of sorting them alphabetically. Start with the "Other" property at value 0, because that's the default property for anything not in the file. This makes no practical difference, but seems cleaner. It causes the values in the __gcb_edges table to change, so can only be done now before anybody is using this code yet. The enumerator values and table entries become ABI artefacts for the function using them. contrib/ChangeLog: * unicode/gen_libstdcxx_unicode_data.py: Print out Gcb_property enumerators in the order they're seen, not alphabetical order. libstdc++-v3/ChangeLog: * include/bits/unicode-data.h: Regenerate. * include/bits/unicode.h (_Utf_iterator::operator++()): Fix off by one error. (__incb_property): Add missing check for values before the first edge. (__is_extended_pictographic): Invert return values to fix copy&pasted logic. (_Grapheme_cluster_view::_Iterator): Remove second iterator member and find end of cluster lazily. * testsuite/ext/unicode/grapheme_view.cc: New test. * testsuite/ext/unicode/properties.cc: New test. * testsuite/ext/unicode/view.cc: New test. --- contrib/unicode/gen_libstdcxx_unicode_data.py | 5 +- libstdc++-v3/include/bits/unicode-data.h | 596 ++++++++++----------- libstdc++-v3/include/bits/unicode.h | 51 +- .../testsuite/ext/unicode/grapheme_view.cc | 95 ++++ libstdc++-v3/testsuite/ext/unicode/properties.cc | 128 +++++ libstdc++-v3/testsuite/ext/unicode/view.cc | 30 ++ 6 files changed, 581 insertions(+), 324 deletions(-) create mode 100644 libstdc++-v3/testsuite/ext/unicode/grapheme_view.cc create mode 100644 libstdc++-v3/testsuite/ext/unicode/properties.cc diff --git a/contrib/unicode/gen_libstdcxx_unicode_data.py b/contrib/unicode/gen_libstdcxx_unicode_data.py index 1449145..f2f2f8a 100755 --- a/contrib/unicode/gen_libstdcxx_unicode_data.py +++ b/contrib/unicode/gen_libstdcxx_unicode_data.py @@ -122,7 +122,10 @@ for line in open("GraphemeBreakProperty.txt", "r"): process_code_points(code_points, grapheme_property.strip()) edges = find_edges(all_code_points) -gcb_props = {p:i+1 for i,p in enumerate(sorted(set([x[1] for x in edges])))} +gcb_props = {"Other":0} +for c, p in edges: + if p not in gcb_props: + gcb_props[p] = len(gcb_props) shift_bits = int(math.ceil(math.log2(len(gcb_props)))) # Enum definition for std::__unicode::_Gcb_property diff --git a/libstdc++-v3/include/bits/unicode-data.h b/libstdc++-v3/include/bits/unicode-data.h index c0c7e7d..8396809 100644 --- a/libstdc++-v3/include/bits/unicode-data.h +++ b/libstdc++-v3/include/bits/unicode-data.h @@ -37,20 +37,20 @@ }; enum class _Gcb_property { - _Gcb_CR = 1, - _Gcb_Control = 2, - _Gcb_Extend = 3, - _Gcb_L = 4, - _Gcb_LF = 5, - _Gcb_LV = 6, - _Gcb_LVT = 7, - _Gcb_Other = 8, - _Gcb_Prepend = 9, - _Gcb_Regional_Indicator = 10, - _Gcb_SpacingMark = 11, - _Gcb_T = 12, - _Gcb_V = 13, - _Gcb_ZWJ = 14, + _Gcb_Other = 0, + _Gcb_Control = 1, + _Gcb_LF = 2, + _Gcb_CR = 3, + _Gcb_Extend = 4, + _Gcb_Prepend = 5, + _Gcb_SpacingMark = 6, + _Gcb_L = 7, + _Gcb_V = 8, + _Gcb_T = 9, + _Gcb_ZWJ = 10, + _Gcb_LV = 11, + _Gcb_LVT = 12, + _Gcb_Regional_Indicator = 13, }; // Values generated by contrib/unicode/gen_std_format_width.py, @@ -58,290 +58,290 @@ // Entries are (code_point << shift_bits) + property. inline constexpr int __gcb_shift_bits = 0x4; inline constexpr uint32_t __gcb_edges[] = { - 0x2, 0xa5, 0xb2, 0xd1, 0xe2, 0x208, - 0x7f2, 0xa08, 0xad2, 0xae8, 0x3003, 0x3708, - 0x4833, 0x48a8, 0x5913, 0x5be8, 0x5bf3, 0x5c08, - 0x5c13, 0x5c38, 0x5c43, 0x5c68, 0x5c73, 0x5c88, - 0x6009, 0x6068, 0x6103, 0x61b8, 0x61c2, 0x61d8, - 0x64b3, 0x6608, 0x6703, 0x6718, 0x6d63, 0x6dd9, - 0x6de8, 0x6df3, 0x6e58, 0x6e73, 0x6e98, 0x6ea3, - 0x6ee8, 0x70f9, 0x7108, 0x7113, 0x7128, 0x7303, - 0x74b8, 0x7a63, 0x7b18, 0x7eb3, 0x7f48, 0x7fd3, - 0x7fe8, 0x8163, 0x81a8, 0x81b3, 0x8248, 0x8253, - 0x8288, 0x8293, 0x82e8, 0x8593, 0x85c8, 0x8909, - 0x8928, 0x8983, 0x8a08, 0x8ca3, 0x8e29, 0x8e33, - 0x903b, 0x9048, 0x93a3, 0x93bb, 0x93c3, 0x93d8, - 0x93eb, 0x9413, 0x949b, 0x94d3, 0x94eb, 0x9508, - 0x9513, 0x9588, 0x9623, 0x9648, 0x9813, 0x982b, - 0x9848, 0x9bc3, 0x9bd8, 0x9be3, 0x9bfb, 0x9c13, - 0x9c58, 0x9c7b, 0x9c98, 0x9cbb, 0x9cd3, 0x9ce8, - 0x9d73, 0x9d88, 0x9e23, 0x9e48, 0x9fe3, 0x9ff8, - 0xa013, 0xa03b, 0xa048, 0xa3c3, 0xa3d8, 0xa3eb, - 0xa413, 0xa438, 0xa473, 0xa498, 0xa4b3, 0xa4e8, - 0xa513, 0xa528, 0xa703, 0xa728, 0xa753, 0xa768, - 0xa813, 0xa83b, 0xa848, 0xabc3, 0xabd8, 0xabeb, - 0xac13, 0xac68, 0xac73, 0xac9b, 0xaca8, 0xacbb, - 0xacd3, 0xace8, 0xae23, 0xae48, 0xafa3, 0xb008, - 0xb013, 0xb02b, 0xb048, 0xb3c3, 0xb3d8, 0xb3e3, - 0xb40b, 0xb413, 0xb458, 0xb47b, 0xb498, 0xb4bb, - 0xb4d3, 0xb4e8, 0xb553, 0xb588, 0xb623, 0xb648, - 0xb823, 0xb838, 0xbbe3, 0xbbfb, 0xbc03, 0xbc1b, - 0xbc38, 0xbc6b, 0xbc98, 0xbcab, 0xbcd3, 0xbce8, - 0xbd73, 0xbd88, 0xc003, 0xc01b, 0xc043, 0xc058, - 0xc3c3, 0xc3d8, 0xc3e3, 0xc41b, 0xc458, 0xc463, - 0xc498, 0xc4a3, 0xc4e8, 0xc553, 0xc578, 0xc623, - 0xc648, 0xc813, 0xc82b, 0xc848, 0xcbc3, 0xcbd8, - 0xcbeb, 0xcbf3, 0xcc0b, 0xcc23, 0xcc3b, 0xcc58, - 0xcc63, 0xcc7b, 0xcc98, 0xccab, 0xccc3, 0xcce8, - 0xcd53, 0xcd78, 0xce23, 0xce48, 0xcf3b, 0xcf48, - 0xd003, 0xd02b, 0xd048, 0xd3b3, 0xd3d8, 0xd3e3, - 0xd3fb, 0xd413, 0xd458, 0xd46b, 0xd498, 0xd4ab, - 0xd4d3, 0xd4e9, 0xd4f8, 0xd573, 0xd588, 0xd623, - 0xd648, 0xd813, 0xd82b, 0xd848, 0xdca3, 0xdcb8, - 0xdcf3, 0xdd0b, 0xdd23, 0xdd58, 0xdd63, 0xdd78, - 0xdd8b, 0xddf3, 0xde08, 0xdf2b, 0xdf48, 0xe313, - 0xe328, 0xe33b, 0xe343, 0xe3b8, 0xe473, 0xe4f8, - 0xeb13, 0xeb28, 0xeb3b, 0xeb43, 0xebd8, 0xec83, - 0xecf8, 0xf183, 0xf1a8, 0xf353, 0xf368, 0xf373, - 0xf388, 0xf393, 0xf3a8, 0xf3eb, 0xf408, 0xf713, - 0xf7fb, 0xf803, 0xf858, 0xf863, 0xf888, 0xf8d3, - 0xf988, 0xf993, 0xfbd8, 0xfc63, 0xfc78, 0x102d3, - 0x1031b, 0x10323, 0x10388, 0x10393, 0x103bb, 0x103d3, - 0x103f8, 0x1056b, 0x10583, 0x105a8, 0x105e3, 0x10618, - 0x10713, 0x10758, 0x10823, 0x10838, 0x1084b, 0x10853, - 0x10878, 0x108d3, 0x108e8, 0x109d3, 0x109e8, 0x11004, - 0x1160d, 0x11a8c, 0x12008, 0x135d3, 0x13608, 0x17123, - 0x1715b, 0x17168, 0x17323, 0x1734b, 0x17358, 0x17523, - 0x17548, 0x17723, 0x17748, 0x17b43, 0x17b6b, 0x17b73, - 0x17beb, 0x17c63, 0x17c7b, 0x17c93, 0x17d48, 0x17dd3, - 0x17de8, 0x180b3, 0x180e2, 0x180f3, 0x18108, 0x18853, - 0x18878, 0x18a93, 0x18aa8, 0x19203, 0x1923b, 0x19273, - 0x1929b, 0x192c8, 0x1930b, 0x19323, 0x1933b, 0x19393, - 0x193c8, 0x1a173, 0x1a19b, 0x1a1b3, 0x1a1c8, 0x1a55b, - 0x1a563, 0x1a57b, 0x1a583, 0x1a5f8, 0x1a603, 0x1a618, - 0x1a623, 0x1a638, 0x1a653, 0x1a6db, 0x1a733, 0x1a7d8, - 0x1a7f3, 0x1a808, 0x1ab03, 0x1acf8, 0x1b003, 0x1b04b, - 0x1b058, 0x1b343, 0x1b3bb, 0x1b3c3, 0x1b3db, 0x1b423, - 0x1b43b, 0x1b458, 0x1b6b3, 0x1b748, 0x1b803, 0x1b82b, - 0x1b838, 0x1ba1b, 0x1ba23, 0x1ba6b, 0x1ba83, 0x1baab, - 0x1bab3, 0x1bae8, 0x1be63, 0x1be7b, 0x1be83, 0x1beab, - 0x1bed3, 0x1beeb, 0x1bef3, 0x1bf2b, 0x1bf48, 0x1c24b, - 0x1c2c3, 0x1c34b, 0x1c363, 0x1c388, 0x1cd03, 0x1cd38, - 0x1cd43, 0x1ce1b, 0x1ce23, 0x1ce98, 0x1ced3, 0x1cee8, - 0x1cf43, 0x1cf58, 0x1cf7b, 0x1cf83, 0x1cfa8, 0x1dc03, - 0x1e008, 0x200b2, 0x200c3, 0x200de, 0x200e2, 0x20108, - 0x20282, 0x202f8, 0x20602, 0x20708, 0x20d03, 0x20f18, - 0x2cef3, 0x2cf28, 0x2d7f3, 0x2d808, 0x2de03, 0x2e008, - 0x302a3, 0x30308, 0x30993, 0x309b8, 0xa66f3, 0xa6738, - 0xa6743, 0xa67e8, 0xa69e3, 0xa6a08, 0xa6f03, 0xa6f28, - 0xa8023, 0xa8038, 0xa8063, 0xa8078, 0xa80b3, 0xa80c8, - 0xa823b, 0xa8253, 0xa827b, 0xa8288, 0xa82c3, 0xa82d8, - 0xa880b, 0xa8828, 0xa8b4b, 0xa8c43, 0xa8c68, 0xa8e03, - 0xa8f28, 0xa8ff3, 0xa9008, 0xa9263, 0xa92e8, 0xa9473, - 0xa952b, 0xa9548, 0xa9604, 0xa97d8, 0xa9803, 0xa983b, - 0xa9848, 0xa9b33, 0xa9b4b, 0xa9b63, 0xa9bab, 0xa9bc3, - 0xa9beb, 0xa9c18, 0xa9e53, 0xa9e68, 0xaa293, 0xaa2fb, - 0xaa313, 0xaa33b, 0xaa353, 0xaa378, 0xaa433, 0xaa448, - 0xaa4c3, 0xaa4db, 0xaa4e8, 0xaa7c3, 0xaa7d8, 0xaab03, - 0xaab18, 0xaab23, 0xaab58, 0xaab73, 0xaab98, 0xaabe3, - 0xaac08, 0xaac13, 0xaac28, 0xaaebb, 0xaaec3, 0xaaeeb, - 0xaaf08, 0xaaf5b, 0xaaf63, 0xaaf78, 0xabe3b, 0xabe53, - 0xabe6b, 0xabe83, 0xabe9b, 0xabeb8, 0xabecb, 0xabed3, - 0xabee8, 0xac006, 0xac017, 0xac1c6, 0xac1d7, 0xac386, - 0xac397, 0xac546, 0xac557, 0xac706, 0xac717, 0xac8c6, - 0xac8d7, 0xaca86, 0xaca97, 0xacc46, 0xacc57, 0xace06, - 0xace17, 0xacfc6, 0xacfd7, 0xad186, 0xad197, 0xad346, - 0xad357, 0xad506, 0xad517, 0xad6c6, 0xad6d7, 0xad886, - 0xad897, 0xada46, 0xada57, 0xadc06, 0xadc17, 0xaddc6, - 0xaddd7, 0xadf86, 0xadf97, 0xae146, 0xae157, 0xae306, - 0xae317, 0xae4c6, 0xae4d7, 0xae686, 0xae697, 0xae846, - 0xae857, 0xaea06, 0xaea17, 0xaebc6, 0xaebd7, 0xaed86, - 0xaed97, 0xaef46, 0xaef57, 0xaf106, 0xaf117, 0xaf2c6, - 0xaf2d7, 0xaf486, 0xaf497, 0xaf646, 0xaf657, 0xaf806, - 0xaf817, 0xaf9c6, 0xaf9d7, 0xafb86, 0xafb97, 0xafd46, - 0xafd57, 0xaff06, 0xaff17, 0xb00c6, 0xb00d7, 0xb0286, - 0xb0297, 0xb0446, 0xb0457, 0xb0606, 0xb0617, 0xb07c6, - 0xb07d7, 0xb0986, 0xb0997, 0xb0b46, 0xb0b57, 0xb0d06, - 0xb0d17, 0xb0ec6, 0xb0ed7, 0xb1086, 0xb1097, 0xb1246, - 0xb1257, 0xb1406, 0xb1417, 0xb15c6, 0xb15d7, 0xb1786, - 0xb1797, 0xb1946, 0xb1957, 0xb1b06, 0xb1b17, 0xb1cc6, - 0xb1cd7, 0xb1e86, 0xb1e97, 0xb2046, 0xb2057, 0xb2206, - 0xb2217, 0xb23c6, 0xb23d7, 0xb2586, 0xb2597, 0xb2746, - 0xb2757, 0xb2906, 0xb2917, 0xb2ac6, 0xb2ad7, 0xb2c86, - 0xb2c97, 0xb2e46, 0xb2e57, 0xb3006, 0xb3017, 0xb31c6, - 0xb31d7, 0xb3386, 0xb3397, 0xb3546, 0xb3557, 0xb3706, - 0xb3717, 0xb38c6, 0xb38d7, 0xb3a86, 0xb3a97, 0xb3c46, - 0xb3c57, 0xb3e06, 0xb3e17, 0xb3fc6, 0xb3fd7, 0xb4186, - 0xb4197, 0xb4346, 0xb4357, 0xb4506, 0xb4517, 0xb46c6, - 0xb46d7, 0xb4886, 0xb4897, 0xb4a46, 0xb4a57, 0xb4c06, - 0xb4c17, 0xb4dc6, 0xb4dd7, 0xb4f86, 0xb4f97, 0xb5146, - 0xb5157, 0xb5306, 0xb5317, 0xb54c6, 0xb54d7, 0xb5686, - 0xb5697, 0xb5846, 0xb5857, 0xb5a06, 0xb5a17, 0xb5bc6, - 0xb5bd7, 0xb5d86, 0xb5d97, 0xb5f46, 0xb5f57, 0xb6106, - 0xb6117, 0xb62c6, 0xb62d7, 0xb6486, 0xb6497, 0xb6646, - 0xb6657, 0xb6806, 0xb6817, 0xb69c6, 0xb69d7, 0xb6b86, - 0xb6b97, 0xb6d46, 0xb6d57, 0xb6f06, 0xb6f17, 0xb70c6, - 0xb70d7, 0xb7286, 0xb7297, 0xb7446, 0xb7457, 0xb7606, - 0xb7617, 0xb77c6, 0xb77d7, 0xb7986, 0xb7997, 0xb7b46, - 0xb7b57, 0xb7d06, 0xb7d17, 0xb7ec6, 0xb7ed7, 0xb8086, - 0xb8097, 0xb8246, 0xb8257, 0xb8406, 0xb8417, 0xb85c6, - 0xb85d7, 0xb8786, 0xb8797, 0xb8946, 0xb8957, 0xb8b06, - 0xb8b17, 0xb8cc6, 0xb8cd7, 0xb8e86, 0xb8e97, 0xb9046, - 0xb9057, 0xb9206, 0xb9217, 0xb93c6, 0xb93d7, 0xb9586, - 0xb9597, 0xb9746, 0xb9757, 0xb9906, 0xb9917, 0xb9ac6, - 0xb9ad7, 0xb9c86, 0xb9c97, 0xb9e46, 0xb9e57, 0xba006, - 0xba017, 0xba1c6, 0xba1d7, 0xba386, 0xba397, 0xba546, - 0xba557, 0xba706, 0xba717, 0xba8c6, 0xba8d7, 0xbaa86, - 0xbaa97, 0xbac46, 0xbac57, 0xbae06, 0xbae17, 0xbafc6, - 0xbafd7, 0xbb186, 0xbb197, 0xbb346, 0xbb357, 0xbb506, - 0xbb517, 0xbb6c6, 0xbb6d7, 0xbb886, 0xbb897, 0xbba46, - 0xbba57, 0xbbc06, 0xbbc17, 0xbbdc6, 0xbbdd7, 0xbbf86, - 0xbbf97, 0xbc146, 0xbc157, 0xbc306, 0xbc317, 0xbc4c6, - 0xbc4d7, 0xbc686, 0xbc697, 0xbc846, 0xbc857, 0xbca06, - 0xbca17, 0xbcbc6, 0xbcbd7, 0xbcd86, 0xbcd97, 0xbcf46, - 0xbcf57, 0xbd106, 0xbd117, 0xbd2c6, 0xbd2d7, 0xbd486, - 0xbd497, 0xbd646, 0xbd657, 0xbd806, 0xbd817, 0xbd9c6, - 0xbd9d7, 0xbdb86, 0xbdb97, 0xbdd46, 0xbdd57, 0xbdf06, - 0xbdf17, 0xbe0c6, 0xbe0d7, 0xbe286, 0xbe297, 0xbe446, - 0xbe457, 0xbe606, 0xbe617, 0xbe7c6, 0xbe7d7, 0xbe986, - 0xbe997, 0xbeb46, 0xbeb57, 0xbed06, 0xbed17, 0xbeec6, - 0xbeed7, 0xbf086, 0xbf097, 0xbf246, 0xbf257, 0xbf406, - 0xbf417, 0xbf5c6, 0xbf5d7, 0xbf786, 0xbf797, 0xbf946, - 0xbf957, 0xbfb06, 0xbfb17, 0xbfcc6, 0xbfcd7, 0xbfe86, - 0xbfe97, 0xc0046, 0xc0057, 0xc0206, 0xc0217, 0xc03c6, - 0xc03d7, 0xc0586, 0xc0597, 0xc0746, 0xc0757, 0xc0906, - 0xc0917, 0xc0ac6, 0xc0ad7, 0xc0c86, 0xc0c97, 0xc0e46, - 0xc0e57, 0xc1006, 0xc1017, 0xc11c6, 0xc11d7, 0xc1386, - 0xc1397, 0xc1546, 0xc1557, 0xc1706, 0xc1717, 0xc18c6, - 0xc18d7, 0xc1a86, 0xc1a97, 0xc1c46, 0xc1c57, 0xc1e06, - 0xc1e17, 0xc1fc6, 0xc1fd7, 0xc2186, 0xc2197, 0xc2346, - 0xc2357, 0xc2506, 0xc2517, 0xc26c6, 0xc26d7, 0xc2886, - 0xc2897, 0xc2a46, 0xc2a57, 0xc2c06, 0xc2c17, 0xc2dc6, - 0xc2dd7, 0xc2f86, 0xc2f97, 0xc3146, 0xc3157, 0xc3306, - 0xc3317, 0xc34c6, 0xc34d7, 0xc3686, 0xc3697, 0xc3846, - 0xc3857, 0xc3a06, 0xc3a17, 0xc3bc6, 0xc3bd7, 0xc3d86, - 0xc3d97, 0xc3f46, 0xc3f57, 0xc4106, 0xc4117, 0xc42c6, - 0xc42d7, 0xc4486, 0xc4497, 0xc4646, 0xc4657, 0xc4806, - 0xc4817, 0xc49c6, 0xc49d7, 0xc4b86, 0xc4b97, 0xc4d46, - 0xc4d57, 0xc4f06, 0xc4f17, 0xc50c6, 0xc50d7, 0xc5286, - 0xc5297, 0xc5446, 0xc5457, 0xc5606, 0xc5617, 0xc57c6, - 0xc57d7, 0xc5986, 0xc5997, 0xc5b46, 0xc5b57, 0xc5d06, - 0xc5d17, 0xc5ec6, 0xc5ed7, 0xc6086, 0xc6097, 0xc6246, - 0xc6257, 0xc6406, 0xc6417, 0xc65c6, 0xc65d7, 0xc6786, - 0xc6797, 0xc6946, 0xc6957, 0xc6b06, 0xc6b17, 0xc6cc6, - 0xc6cd7, 0xc6e86, 0xc6e97, 0xc7046, 0xc7057, 0xc7206, - 0xc7217, 0xc73c6, 0xc73d7, 0xc7586, 0xc7597, 0xc7746, - 0xc7757, 0xc7906, 0xc7917, 0xc7ac6, 0xc7ad7, 0xc7c86, - 0xc7c97, 0xc7e46, 0xc7e57, 0xc8006, 0xc8017, 0xc81c6, - 0xc81d7, 0xc8386, 0xc8397, 0xc8546, 0xc8557, 0xc8706, - 0xc8717, 0xc88c6, 0xc88d7, 0xc8a86, 0xc8a97, 0xc8c46, - 0xc8c57, 0xc8e06, 0xc8e17, 0xc8fc6, 0xc8fd7, 0xc9186, - 0xc9197, 0xc9346, 0xc9357, 0xc9506, 0xc9517, 0xc96c6, - 0xc96d7, 0xc9886, 0xc9897, 0xc9a46, 0xc9a57, 0xc9c06, - 0xc9c17, 0xc9dc6, 0xc9dd7, 0xc9f86, 0xc9f97, 0xca146, - 0xca157, 0xca306, 0xca317, 0xca4c6, 0xca4d7, 0xca686, - 0xca697, 0xca846, 0xca857, 0xcaa06, 0xcaa17, 0xcabc6, - 0xcabd7, 0xcad86, 0xcad97, 0xcaf46, 0xcaf57, 0xcb106, - 0xcb117, 0xcb2c6, 0xcb2d7, 0xcb486, 0xcb497, 0xcb646, - 0xcb657, 0xcb806, 0xcb817, 0xcb9c6, 0xcb9d7, 0xcbb86, - 0xcbb97, 0xcbd46, 0xcbd57, 0xcbf06, 0xcbf17, 0xcc0c6, - 0xcc0d7, 0xcc286, 0xcc297, 0xcc446, 0xcc457, 0xcc606, - 0xcc617, 0xcc7c6, 0xcc7d7, 0xcc986, 0xcc997, 0xccb46, - 0xccb57, 0xccd06, 0xccd17, 0xccec6, 0xcced7, 0xcd086, - 0xcd097, 0xcd246, 0xcd257, 0xcd406, 0xcd417, 0xcd5c6, - 0xcd5d7, 0xcd786, 0xcd797, 0xcd946, 0xcd957, 0xcdb06, - 0xcdb17, 0xcdcc6, 0xcdcd7, 0xcde86, 0xcde97, 0xce046, - 0xce057, 0xce206, 0xce217, 0xce3c6, 0xce3d7, 0xce586, - 0xce597, 0xce746, 0xce757, 0xce906, 0xce917, 0xceac6, - 0xcead7, 0xcec86, 0xcec97, 0xcee46, 0xcee57, 0xcf006, - 0xcf017, 0xcf1c6, 0xcf1d7, 0xcf386, 0xcf397, 0xcf546, - 0xcf557, 0xcf706, 0xcf717, 0xcf8c6, 0xcf8d7, 0xcfa86, - 0xcfa97, 0xcfc46, 0xcfc57, 0xcfe06, 0xcfe17, 0xcffc6, - 0xcffd7, 0xd0186, 0xd0197, 0xd0346, 0xd0357, 0xd0506, - 0xd0517, 0xd06c6, 0xd06d7, 0xd0886, 0xd0897, 0xd0a46, - 0xd0a57, 0xd0c06, 0xd0c17, 0xd0dc6, 0xd0dd7, 0xd0f86, - 0xd0f97, 0xd1146, 0xd1157, 0xd1306, 0xd1317, 0xd14c6, - 0xd14d7, 0xd1686, 0xd1697, 0xd1846, 0xd1857, 0xd1a06, - 0xd1a17, 0xd1bc6, 0xd1bd7, 0xd1d86, 0xd1d97, 0xd1f46, - 0xd1f57, 0xd2106, 0xd2117, 0xd22c6, 0xd22d7, 0xd2486, - 0xd2497, 0xd2646, 0xd2657, 0xd2806, 0xd2817, 0xd29c6, - 0xd29d7, 0xd2b86, 0xd2b97, 0xd2d46, 0xd2d57, 0xd2f06, - 0xd2f17, 0xd30c6, 0xd30d7, 0xd3286, 0xd3297, 0xd3446, - 0xd3457, 0xd3606, 0xd3617, 0xd37c6, 0xd37d7, 0xd3986, - 0xd3997, 0xd3b46, 0xd3b57, 0xd3d06, 0xd3d17, 0xd3ec6, - 0xd3ed7, 0xd4086, 0xd4097, 0xd4246, 0xd4257, 0xd4406, - 0xd4417, 0xd45c6, 0xd45d7, 0xd4786, 0xd4797, 0xd4946, - 0xd4957, 0xd4b06, 0xd4b17, 0xd4cc6, 0xd4cd7, 0xd4e86, - 0xd4e97, 0xd5046, 0xd5057, 0xd5206, 0xd5217, 0xd53c6, - 0xd53d7, 0xd5586, 0xd5597, 0xd5746, 0xd5757, 0xd5906, - 0xd5917, 0xd5ac6, 0xd5ad7, 0xd5c86, 0xd5c97, 0xd5e46, - 0xd5e57, 0xd6006, 0xd6017, 0xd61c6, 0xd61d7, 0xd6386, - 0xd6397, 0xd6546, 0xd6557, 0xd6706, 0xd6717, 0xd68c6, - 0xd68d7, 0xd6a86, 0xd6a97, 0xd6c46, 0xd6c57, 0xd6e06, - 0xd6e17, 0xd6fc6, 0xd6fd7, 0xd7186, 0xd7197, 0xd7346, - 0xd7357, 0xd7506, 0xd7517, 0xd76c6, 0xd76d7, 0xd7886, - 0xd7897, 0xd7a48, 0xd7b0d, 0xd7c78, 0xd7cbc, 0xd7fc8, - 0xfb1e3, 0xfb1f8, 0xfe003, 0xfe108, 0xfe203, 0xfe308, - 0xfeff2, 0xff008, 0xff9e3, 0xffa08, 0xfff02, 0xfffc8, - 0x101fd3, 0x101fe8, 0x102e03, 0x102e18, 0x103763, 0x1037b8, - 0x10a013, 0x10a048, 0x10a053, 0x10a078, 0x10a0c3, 0x10a108, - 0x10a383, 0x10a3b8, 0x10a3f3, 0x10a408, 0x10ae53, 0x10ae78, - 0x10d243, 0x10d288, 0x10eab3, 0x10ead8, 0x10efd3, 0x10f008, - 0x10f463, 0x10f518, 0x10f823, 0x10f868, 0x11000b, 0x110013, - 0x11002b, 0x110038, 0x110383, 0x110478, 0x110703, 0x110718, - 0x110733, 0x110758, 0x1107f3, 0x11082b, 0x110838, 0x110b0b, - 0x110b33, 0x110b7b, 0x110b93, 0x110bb8, 0x110bd9, 0x110be8, - 0x110c23, 0x110c38, 0x110cd9, 0x110ce8, 0x111003, 0x111038, - 0x111273, 0x1112cb, 0x1112d3, 0x111358, 0x11145b, 0x111478, - 0x111733, 0x111748, 0x111803, 0x11182b, 0x111838, 0x111b3b, - 0x111b63, 0x111bfb, 0x111c18, 0x111c29, 0x111c48, 0x111c93, - 0x111cd8, 0x111ceb, 0x111cf3, 0x111d08, 0x1122cb, 0x1122f3, - 0x11232b, 0x112343, 0x11235b, 0x112363, 0x112388, 0x1123e3, - 0x1123f8, 0x112413, 0x112428, 0x112df3, 0x112e0b, 0x112e33, - 0x112eb8, 0x113003, 0x11302b, 0x113048, 0x1133b3, 0x1133d8, - 0x1133e3, 0x1133fb, 0x113403, 0x11341b, 0x113458, 0x11347b, - 0x113498, 0x1134bb, 0x1134e8, 0x113573, 0x113588, 0x11362b, - 0x113648, 0x113663, 0x1136d8, 0x113703, 0x113758, 0x11435b, - 0x114383, 0x11440b, 0x114423, 0x11445b, 0x114463, 0x114478, - 0x1145e3, 0x1145f8, 0x114b03, 0x114b1b, 0x114b33, 0x114b9b, - 0x114ba3, 0x114bbb, 0x114bd3, 0x114beb, 0x114bf3, 0x114c1b, - 0x114c23, 0x114c48, 0x115af3, 0x115b0b, 0x115b23, 0x115b68, - 0x115b8b, 0x115bc3, 0x115beb, 0x115bf3, 0x115c18, 0x115dc3, - 0x115de8, 0x11630b, 0x116333, 0x1163bb, 0x1163d3, 0x1163eb, - 0x1163f3, 0x116418, 0x116ab3, 0x116acb, 0x116ad3, 0x116aeb, - 0x116b03, 0x116b6b, 0x116b73, 0x116b88, 0x1171d3, 0x117208, - 0x117223, 0x11726b, 0x117273, 0x1172c8, 0x1182cb, 0x1182f3, - 0x11838b, 0x118393, 0x1183b8, 0x119303, 0x11931b, 0x119368, - 0x11937b, 0x119398, 0x1193b3, 0x1193db, 0x1193e3, 0x1193f9, - 0x11940b, 0x119419, 0x11942b, 0x119433, 0x119448, 0x119d1b, - 0x119d43, 0x119d88, 0x119da3, 0x119dcb, 0x119e03, 0x119e18, - 0x119e4b, 0x119e58, 0x11a013, 0x11a0b8, 0x11a333, 0x11a39b, - 0x11a3a9, 0x11a3b3, 0x11a3f8, 0x11a473, 0x11a488, 0x11a513, - 0x11a57b, 0x11a593, 0x11a5c8, 0x11a849, 0x11a8a3, 0x11a97b, - 0x11a983, 0x11a9a8, 0x11c2fb, 0x11c303, 0x11c378, 0x11c383, - 0x11c3eb, 0x11c3f3, 0x11c408, 0x11c923, 0x11ca88, 0x11ca9b, - 0x11caa3, 0x11cb1b, 0x11cb23, 0x11cb4b, 0x11cb53, 0x11cb78, - 0x11d313, 0x11d378, 0x11d3a3, 0x11d3b8, 0x11d3c3, 0x11d3e8, - 0x11d3f3, 0x11d469, 0x11d473, 0x11d488, 0x11d8ab, 0x11d8f8, - 0x11d903, 0x11d928, 0x11d93b, 0x11d953, 0x11d96b, 0x11d973, - 0x11d988, 0x11ef33, 0x11ef5b, 0x11ef78, 0x11f003, 0x11f029, - 0x11f03b, 0x11f048, 0x11f34b, 0x11f363, 0x11f3b8, 0x11f3eb, - 0x11f403, 0x11f41b, 0x11f423, 0x11f438, 0x134302, 0x134403, - 0x134418, 0x134473, 0x134568, 0x16af03, 0x16af58, 0x16b303, - 0x16b378, 0x16f4f3, 0x16f508, 0x16f51b, 0x16f888, 0x16f8f3, - 0x16f938, 0x16fe43, 0x16fe58, 0x16ff0b, 0x16ff28, 0x1bc9d3, - 0x1bc9f8, 0x1bca02, 0x1bca48, 0x1cf003, 0x1cf2e8, 0x1cf303, - 0x1cf478, 0x1d1653, 0x1d166b, 0x1d1673, 0x1d16a8, 0x1d16db, - 0x1d16e3, 0x1d1732, 0x1d17b3, 0x1d1838, 0x1d1853, 0x1d18c8, - 0x1d1aa3, 0x1d1ae8, 0x1d2423, 0x1d2458, 0x1da003, 0x1da378, - 0x1da3b3, 0x1da6d8, 0x1da753, 0x1da768, 0x1da843, 0x1da858, - 0x1da9b3, 0x1daa08, 0x1daa13, 0x1dab08, 0x1e0003, 0x1e0078, - 0x1e0083, 0x1e0198, 0x1e01b3, 0x1e0228, 0x1e0233, 0x1e0258, - 0x1e0263, 0x1e02b8, 0x1e08f3, 0x1e0908, 0x1e1303, 0x1e1378, - 0x1e2ae3, 0x1e2af8, 0x1e2ec3, 0x1e2f08, 0x1e4ec3, 0x1e4f08, - 0x1e8d03, 0x1e8d78, 0x1e9443, 0x1e94b8, 0x1f1e6a, 0x1f2008, - 0x1f3fb3, 0x1f4008, 0xe00002, 0xe00203, 0xe00802, 0xe01003, - 0xe01f02, 0xe10008, + 0x1, 0xa2, 0xb1, 0xd3, 0xe1, 0x200, + 0x7f1, 0xa00, 0xad1, 0xae0, 0x3004, 0x3700, + 0x4834, 0x48a0, 0x5914, 0x5be0, 0x5bf4, 0x5c00, + 0x5c14, 0x5c30, 0x5c44, 0x5c60, 0x5c74, 0x5c80, + 0x6005, 0x6060, 0x6104, 0x61b0, 0x61c1, 0x61d0, + 0x64b4, 0x6600, 0x6704, 0x6710, 0x6d64, 0x6dd5, + 0x6de0, 0x6df4, 0x6e50, 0x6e74, 0x6e90, 0x6ea4, + 0x6ee0, 0x70f5, 0x7100, 0x7114, 0x7120, 0x7304, + 0x74b0, 0x7a64, 0x7b10, 0x7eb4, 0x7f40, 0x7fd4, + 0x7fe0, 0x8164, 0x81a0, 0x81b4, 0x8240, 0x8254, + 0x8280, 0x8294, 0x82e0, 0x8594, 0x85c0, 0x8905, + 0x8920, 0x8984, 0x8a00, 0x8ca4, 0x8e25, 0x8e34, + 0x9036, 0x9040, 0x93a4, 0x93b6, 0x93c4, 0x93d0, + 0x93e6, 0x9414, 0x9496, 0x94d4, 0x94e6, 0x9500, + 0x9514, 0x9580, 0x9624, 0x9640, 0x9814, 0x9826, + 0x9840, 0x9bc4, 0x9bd0, 0x9be4, 0x9bf6, 0x9c14, + 0x9c50, 0x9c76, 0x9c90, 0x9cb6, 0x9cd4, 0x9ce0, + 0x9d74, 0x9d80, 0x9e24, 0x9e40, 0x9fe4, 0x9ff0, + 0xa014, 0xa036, 0xa040, 0xa3c4, 0xa3d0, 0xa3e6, + 0xa414, 0xa430, 0xa474, 0xa490, 0xa4b4, 0xa4e0, + 0xa514, 0xa520, 0xa704, 0xa720, 0xa754, 0xa760, + 0xa814, 0xa836, 0xa840, 0xabc4, 0xabd0, 0xabe6, + 0xac14, 0xac60, 0xac74, 0xac96, 0xaca0, 0xacb6, + 0xacd4, 0xace0, 0xae24, 0xae40, 0xafa4, 0xb000, + 0xb014, 0xb026, 0xb040, 0xb3c4, 0xb3d0, 0xb3e4, + 0xb406, 0xb414, 0xb450, 0xb476, 0xb490, 0xb4b6, + 0xb4d4, 0xb4e0, 0xb554, 0xb580, 0xb624, 0xb640, + 0xb824, 0xb830, 0xbbe4, 0xbbf6, 0xbc04, 0xbc16, + 0xbc30, 0xbc66, 0xbc90, 0xbca6, 0xbcd4, 0xbce0, + 0xbd74, 0xbd80, 0xc004, 0xc016, 0xc044, 0xc050, + 0xc3c4, 0xc3d0, 0xc3e4, 0xc416, 0xc450, 0xc464, + 0xc490, 0xc4a4, 0xc4e0, 0xc554, 0xc570, 0xc624, + 0xc640, 0xc814, 0xc826, 0xc840, 0xcbc4, 0xcbd0, + 0xcbe6, 0xcbf4, 0xcc06, 0xcc24, 0xcc36, 0xcc50, + 0xcc64, 0xcc76, 0xcc90, 0xcca6, 0xccc4, 0xcce0, + 0xcd54, 0xcd70, 0xce24, 0xce40, 0xcf36, 0xcf40, + 0xd004, 0xd026, 0xd040, 0xd3b4, 0xd3d0, 0xd3e4, + 0xd3f6, 0xd414, 0xd450, 0xd466, 0xd490, 0xd4a6, + 0xd4d4, 0xd4e5, 0xd4f0, 0xd574, 0xd580, 0xd624, + 0xd640, 0xd814, 0xd826, 0xd840, 0xdca4, 0xdcb0, + 0xdcf4, 0xdd06, 0xdd24, 0xdd50, 0xdd64, 0xdd70, + 0xdd86, 0xddf4, 0xde00, 0xdf26, 0xdf40, 0xe314, + 0xe320, 0xe336, 0xe344, 0xe3b0, 0xe474, 0xe4f0, + 0xeb14, 0xeb20, 0xeb36, 0xeb44, 0xebd0, 0xec84, + 0xecf0, 0xf184, 0xf1a0, 0xf354, 0xf360, 0xf374, + 0xf380, 0xf394, 0xf3a0, 0xf3e6, 0xf400, 0xf714, + 0xf7f6, 0xf804, 0xf850, 0xf864, 0xf880, 0xf8d4, + 0xf980, 0xf994, 0xfbd0, 0xfc64, 0xfc70, 0x102d4, + 0x10316, 0x10324, 0x10380, 0x10394, 0x103b6, 0x103d4, + 0x103f0, 0x10566, 0x10584, 0x105a0, 0x105e4, 0x10610, + 0x10714, 0x10750, 0x10824, 0x10830, 0x10846, 0x10854, + 0x10870, 0x108d4, 0x108e0, 0x109d4, 0x109e0, 0x11007, + 0x11608, 0x11a89, 0x12000, 0x135d4, 0x13600, 0x17124, + 0x17156, 0x17160, 0x17324, 0x17346, 0x17350, 0x17524, + 0x17540, 0x17724, 0x17740, 0x17b44, 0x17b66, 0x17b74, + 0x17be6, 0x17c64, 0x17c76, 0x17c94, 0x17d40, 0x17dd4, + 0x17de0, 0x180b4, 0x180e1, 0x180f4, 0x18100, 0x18854, + 0x18870, 0x18a94, 0x18aa0, 0x19204, 0x19236, 0x19274, + 0x19296, 0x192c0, 0x19306, 0x19324, 0x19336, 0x19394, + 0x193c0, 0x1a174, 0x1a196, 0x1a1b4, 0x1a1c0, 0x1a556, + 0x1a564, 0x1a576, 0x1a584, 0x1a5f0, 0x1a604, 0x1a610, + 0x1a624, 0x1a630, 0x1a654, 0x1a6d6, 0x1a734, 0x1a7d0, + 0x1a7f4, 0x1a800, 0x1ab04, 0x1acf0, 0x1b004, 0x1b046, + 0x1b050, 0x1b344, 0x1b3b6, 0x1b3c4, 0x1b3d6, 0x1b424, + 0x1b436, 0x1b450, 0x1b6b4, 0x1b740, 0x1b804, 0x1b826, + 0x1b830, 0x1ba16, 0x1ba24, 0x1ba66, 0x1ba84, 0x1baa6, + 0x1bab4, 0x1bae0, 0x1be64, 0x1be76, 0x1be84, 0x1bea6, + 0x1bed4, 0x1bee6, 0x1bef4, 0x1bf26, 0x1bf40, 0x1c246, + 0x1c2c4, 0x1c346, 0x1c364, 0x1c380, 0x1cd04, 0x1cd30, + 0x1cd44, 0x1ce16, 0x1ce24, 0x1ce90, 0x1ced4, 0x1cee0, + 0x1cf44, 0x1cf50, 0x1cf76, 0x1cf84, 0x1cfa0, 0x1dc04, + 0x1e000, 0x200b1, 0x200c4, 0x200da, 0x200e1, 0x20100, + 0x20281, 0x202f0, 0x20601, 0x20700, 0x20d04, 0x20f10, + 0x2cef4, 0x2cf20, 0x2d7f4, 0x2d800, 0x2de04, 0x2e000, + 0x302a4, 0x30300, 0x30994, 0x309b0, 0xa66f4, 0xa6730, + 0xa6744, 0xa67e0, 0xa69e4, 0xa6a00, 0xa6f04, 0xa6f20, + 0xa8024, 0xa8030, 0xa8064, 0xa8070, 0xa80b4, 0xa80c0, + 0xa8236, 0xa8254, 0xa8276, 0xa8280, 0xa82c4, 0xa82d0, + 0xa8806, 0xa8820, 0xa8b46, 0xa8c44, 0xa8c60, 0xa8e04, + 0xa8f20, 0xa8ff4, 0xa9000, 0xa9264, 0xa92e0, 0xa9474, + 0xa9526, 0xa9540, 0xa9607, 0xa97d0, 0xa9804, 0xa9836, + 0xa9840, 0xa9b34, 0xa9b46, 0xa9b64, 0xa9ba6, 0xa9bc4, + 0xa9be6, 0xa9c10, 0xa9e54, 0xa9e60, 0xaa294, 0xaa2f6, + 0xaa314, 0xaa336, 0xaa354, 0xaa370, 0xaa434, 0xaa440, + 0xaa4c4, 0xaa4d6, 0xaa4e0, 0xaa7c4, 0xaa7d0, 0xaab04, + 0xaab10, 0xaab24, 0xaab50, 0xaab74, 0xaab90, 0xaabe4, + 0xaac00, 0xaac14, 0xaac20, 0xaaeb6, 0xaaec4, 0xaaee6, + 0xaaf00, 0xaaf56, 0xaaf64, 0xaaf70, 0xabe36, 0xabe54, + 0xabe66, 0xabe84, 0xabe96, 0xabeb0, 0xabec6, 0xabed4, + 0xabee0, 0xac00b, 0xac01c, 0xac1cb, 0xac1dc, 0xac38b, + 0xac39c, 0xac54b, 0xac55c, 0xac70b, 0xac71c, 0xac8cb, + 0xac8dc, 0xaca8b, 0xaca9c, 0xacc4b, 0xacc5c, 0xace0b, + 0xace1c, 0xacfcb, 0xacfdc, 0xad18b, 0xad19c, 0xad34b, + 0xad35c, 0xad50b, 0xad51c, 0xad6cb, 0xad6dc, 0xad88b, + 0xad89c, 0xada4b, 0xada5c, 0xadc0b, 0xadc1c, 0xaddcb, + 0xadddc, 0xadf8b, 0xadf9c, 0xae14b, 0xae15c, 0xae30b, + 0xae31c, 0xae4cb, 0xae4dc, 0xae68b, 0xae69c, 0xae84b, + 0xae85c, 0xaea0b, 0xaea1c, 0xaebcb, 0xaebdc, 0xaed8b, + 0xaed9c, 0xaef4b, 0xaef5c, 0xaf10b, 0xaf11c, 0xaf2cb, + 0xaf2dc, 0xaf48b, 0xaf49c, 0xaf64b, 0xaf65c, 0xaf80b, + 0xaf81c, 0xaf9cb, 0xaf9dc, 0xafb8b, 0xafb9c, 0xafd4b, + 0xafd5c, 0xaff0b, 0xaff1c, 0xb00cb, 0xb00dc, 0xb028b, + 0xb029c, 0xb044b, 0xb045c, 0xb060b, 0xb061c, 0xb07cb, + 0xb07dc, 0xb098b, 0xb099c, 0xb0b4b, 0xb0b5c, 0xb0d0b, + 0xb0d1c, 0xb0ecb, 0xb0edc, 0xb108b, 0xb109c, 0xb124b, + 0xb125c, 0xb140b, 0xb141c, 0xb15cb, 0xb15dc, 0xb178b, + 0xb179c, 0xb194b, 0xb195c, 0xb1b0b, 0xb1b1c, 0xb1ccb, + 0xb1cdc, 0xb1e8b, 0xb1e9c, 0xb204b, 0xb205c, 0xb220b, + 0xb221c, 0xb23cb, 0xb23dc, 0xb258b, 0xb259c, 0xb274b, + 0xb275c, 0xb290b, 0xb291c, 0xb2acb, 0xb2adc, 0xb2c8b, + 0xb2c9c, 0xb2e4b, 0xb2e5c, 0xb300b, 0xb301c, 0xb31cb, + 0xb31dc, 0xb338b, 0xb339c, 0xb354b, 0xb355c, 0xb370b, + 0xb371c, 0xb38cb, 0xb38dc, 0xb3a8b, 0xb3a9c, 0xb3c4b, + 0xb3c5c, 0xb3e0b, 0xb3e1c, 0xb3fcb, 0xb3fdc, 0xb418b, + 0xb419c, 0xb434b, 0xb435c, 0xb450b, 0xb451c, 0xb46cb, + 0xb46dc, 0xb488b, 0xb489c, 0xb4a4b, 0xb4a5c, 0xb4c0b, + 0xb4c1c, 0xb4dcb, 0xb4ddc, 0xb4f8b, 0xb4f9c, 0xb514b, + 0xb515c, 0xb530b, 0xb531c, 0xb54cb, 0xb54dc, 0xb568b, + 0xb569c, 0xb584b, 0xb585c, 0xb5a0b, 0xb5a1c, 0xb5bcb, + 0xb5bdc, 0xb5d8b, 0xb5d9c, 0xb5f4b, 0xb5f5c, 0xb610b, + 0xb611c, 0xb62cb, 0xb62dc, 0xb648b, 0xb649c, 0xb664b, + 0xb665c, 0xb680b, 0xb681c, 0xb69cb, 0xb69dc, 0xb6b8b, + 0xb6b9c, 0xb6d4b, 0xb6d5c, 0xb6f0b, 0xb6f1c, 0xb70cb, + 0xb70dc, 0xb728b, 0xb729c, 0xb744b, 0xb745c, 0xb760b, + 0xb761c, 0xb77cb, 0xb77dc, 0xb798b, 0xb799c, 0xb7b4b, + 0xb7b5c, 0xb7d0b, 0xb7d1c, 0xb7ecb, 0xb7edc, 0xb808b, + 0xb809c, 0xb824b, 0xb825c, 0xb840b, 0xb841c, 0xb85cb, + 0xb85dc, 0xb878b, 0xb879c, 0xb894b, 0xb895c, 0xb8b0b, + 0xb8b1c, 0xb8ccb, 0xb8cdc, 0xb8e8b, 0xb8e9c, 0xb904b, + 0xb905c, 0xb920b, 0xb921c, 0xb93cb, 0xb93dc, 0xb958b, + 0xb959c, 0xb974b, 0xb975c, 0xb990b, 0xb991c, 0xb9acb, + 0xb9adc, 0xb9c8b, 0xb9c9c, 0xb9e4b, 0xb9e5c, 0xba00b, + 0xba01c, 0xba1cb, 0xba1dc, 0xba38b, 0xba39c, 0xba54b, + 0xba55c, 0xba70b, 0xba71c, 0xba8cb, 0xba8dc, 0xbaa8b, + 0xbaa9c, 0xbac4b, 0xbac5c, 0xbae0b, 0xbae1c, 0xbafcb, + 0xbafdc, 0xbb18b, 0xbb19c, 0xbb34b, 0xbb35c, 0xbb50b, + 0xbb51c, 0xbb6cb, 0xbb6dc, 0xbb88b, 0xbb89c, 0xbba4b, + 0xbba5c, 0xbbc0b, 0xbbc1c, 0xbbdcb, 0xbbddc, 0xbbf8b, + 0xbbf9c, 0xbc14b, 0xbc15c, 0xbc30b, 0xbc31c, 0xbc4cb, + 0xbc4dc, 0xbc68b, 0xbc69c, 0xbc84b, 0xbc85c, 0xbca0b, + 0xbca1c, 0xbcbcb, 0xbcbdc, 0xbcd8b, 0xbcd9c, 0xbcf4b, + 0xbcf5c, 0xbd10b, 0xbd11c, 0xbd2cb, 0xbd2dc, 0xbd48b, + 0xbd49c, 0xbd64b, 0xbd65c, 0xbd80b, 0xbd81c, 0xbd9cb, + 0xbd9dc, 0xbdb8b, 0xbdb9c, 0xbdd4b, 0xbdd5c, 0xbdf0b, + 0xbdf1c, 0xbe0cb, 0xbe0dc, 0xbe28b, 0xbe29c, 0xbe44b, + 0xbe45c, 0xbe60b, 0xbe61c, 0xbe7cb, 0xbe7dc, 0xbe98b, + 0xbe99c, 0xbeb4b, 0xbeb5c, 0xbed0b, 0xbed1c, 0xbeecb, + 0xbeedc, 0xbf08b, 0xbf09c, 0xbf24b, 0xbf25c, 0xbf40b, + 0xbf41c, 0xbf5cb, 0xbf5dc, 0xbf78b, 0xbf79c, 0xbf94b, + 0xbf95c, 0xbfb0b, 0xbfb1c, 0xbfccb, 0xbfcdc, 0xbfe8b, + 0xbfe9c, 0xc004b, 0xc005c, 0xc020b, 0xc021c, 0xc03cb, + 0xc03dc, 0xc058b, 0xc059c, 0xc074b, 0xc075c, 0xc090b, + 0xc091c, 0xc0acb, 0xc0adc, 0xc0c8b, 0xc0c9c, 0xc0e4b, + 0xc0e5c, 0xc100b, 0xc101c, 0xc11cb, 0xc11dc, 0xc138b, + 0xc139c, 0xc154b, 0xc155c, 0xc170b, 0xc171c, 0xc18cb, + 0xc18dc, 0xc1a8b, 0xc1a9c, 0xc1c4b, 0xc1c5c, 0xc1e0b, + 0xc1e1c, 0xc1fcb, 0xc1fdc, 0xc218b, 0xc219c, 0xc234b, + 0xc235c, 0xc250b, 0xc251c, 0xc26cb, 0xc26dc, 0xc288b, + 0xc289c, 0xc2a4b, 0xc2a5c, 0xc2c0b, 0xc2c1c, 0xc2dcb, + 0xc2ddc, 0xc2f8b, 0xc2f9c, 0xc314b, 0xc315c, 0xc330b, + 0xc331c, 0xc34cb, 0xc34dc, 0xc368b, 0xc369c, 0xc384b, + 0xc385c, 0xc3a0b, 0xc3a1c, 0xc3bcb, 0xc3bdc, 0xc3d8b, + 0xc3d9c, 0xc3f4b, 0xc3f5c, 0xc410b, 0xc411c, 0xc42cb, + 0xc42dc, 0xc448b, 0xc449c, 0xc464b, 0xc465c, 0xc480b, + 0xc481c, 0xc49cb, 0xc49dc, 0xc4b8b, 0xc4b9c, 0xc4d4b, + 0xc4d5c, 0xc4f0b, 0xc4f1c, 0xc50cb, 0xc50dc, 0xc528b, + 0xc529c, 0xc544b, 0xc545c, 0xc560b, 0xc561c, 0xc57cb, + 0xc57dc, 0xc598b, 0xc599c, 0xc5b4b, 0xc5b5c, 0xc5d0b, + 0xc5d1c, 0xc5ecb, 0xc5edc, 0xc608b, 0xc609c, 0xc624b, + 0xc625c, 0xc640b, 0xc641c, 0xc65cb, 0xc65dc, 0xc678b, + 0xc679c, 0xc694b, 0xc695c, 0xc6b0b, 0xc6b1c, 0xc6ccb, + 0xc6cdc, 0xc6e8b, 0xc6e9c, 0xc704b, 0xc705c, 0xc720b, + 0xc721c, 0xc73cb, 0xc73dc, 0xc758b, 0xc759c, 0xc774b, + 0xc775c, 0xc790b, 0xc791c, 0xc7acb, 0xc7adc, 0xc7c8b, + 0xc7c9c, 0xc7e4b, 0xc7e5c, 0xc800b, 0xc801c, 0xc81cb, + 0xc81dc, 0xc838b, 0xc839c, 0xc854b, 0xc855c, 0xc870b, + 0xc871c, 0xc88cb, 0xc88dc, 0xc8a8b, 0xc8a9c, 0xc8c4b, + 0xc8c5c, 0xc8e0b, 0xc8e1c, 0xc8fcb, 0xc8fdc, 0xc918b, + 0xc919c, 0xc934b, 0xc935c, 0xc950b, 0xc951c, 0xc96cb, + 0xc96dc, 0xc988b, 0xc989c, 0xc9a4b, 0xc9a5c, 0xc9c0b, + 0xc9c1c, 0xc9dcb, 0xc9ddc, 0xc9f8b, 0xc9f9c, 0xca14b, + 0xca15c, 0xca30b, 0xca31c, 0xca4cb, 0xca4dc, 0xca68b, + 0xca69c, 0xca84b, 0xca85c, 0xcaa0b, 0xcaa1c, 0xcabcb, + 0xcabdc, 0xcad8b, 0xcad9c, 0xcaf4b, 0xcaf5c, 0xcb10b, + 0xcb11c, 0xcb2cb, 0xcb2dc, 0xcb48b, 0xcb49c, 0xcb64b, + 0xcb65c, 0xcb80b, 0xcb81c, 0xcb9cb, 0xcb9dc, 0xcbb8b, + 0xcbb9c, 0xcbd4b, 0xcbd5c, 0xcbf0b, 0xcbf1c, 0xcc0cb, + 0xcc0dc, 0xcc28b, 0xcc29c, 0xcc44b, 0xcc45c, 0xcc60b, + 0xcc61c, 0xcc7cb, 0xcc7dc, 0xcc98b, 0xcc99c, 0xccb4b, + 0xccb5c, 0xccd0b, 0xccd1c, 0xccecb, 0xccedc, 0xcd08b, + 0xcd09c, 0xcd24b, 0xcd25c, 0xcd40b, 0xcd41c, 0xcd5cb, + 0xcd5dc, 0xcd78b, 0xcd79c, 0xcd94b, 0xcd95c, 0xcdb0b, + 0xcdb1c, 0xcdccb, 0xcdcdc, 0xcde8b, 0xcde9c, 0xce04b, + 0xce05c, 0xce20b, 0xce21c, 0xce3cb, 0xce3dc, 0xce58b, + 0xce59c, 0xce74b, 0xce75c, 0xce90b, 0xce91c, 0xceacb, + 0xceadc, 0xcec8b, 0xcec9c, 0xcee4b, 0xcee5c, 0xcf00b, + 0xcf01c, 0xcf1cb, 0xcf1dc, 0xcf38b, 0xcf39c, 0xcf54b, + 0xcf55c, 0xcf70b, 0xcf71c, 0xcf8cb, 0xcf8dc, 0xcfa8b, + 0xcfa9c, 0xcfc4b, 0xcfc5c, 0xcfe0b, 0xcfe1c, 0xcffcb, + 0xcffdc, 0xd018b, 0xd019c, 0xd034b, 0xd035c, 0xd050b, + 0xd051c, 0xd06cb, 0xd06dc, 0xd088b, 0xd089c, 0xd0a4b, + 0xd0a5c, 0xd0c0b, 0xd0c1c, 0xd0dcb, 0xd0ddc, 0xd0f8b, + 0xd0f9c, 0xd114b, 0xd115c, 0xd130b, 0xd131c, 0xd14cb, + 0xd14dc, 0xd168b, 0xd169c, 0xd184b, 0xd185c, 0xd1a0b, + 0xd1a1c, 0xd1bcb, 0xd1bdc, 0xd1d8b, 0xd1d9c, 0xd1f4b, + 0xd1f5c, 0xd210b, 0xd211c, 0xd22cb, 0xd22dc, 0xd248b, + 0xd249c, 0xd264b, 0xd265c, 0xd280b, 0xd281c, 0xd29cb, + 0xd29dc, 0xd2b8b, 0xd2b9c, 0xd2d4b, 0xd2d5c, 0xd2f0b, + 0xd2f1c, 0xd30cb, 0xd30dc, 0xd328b, 0xd329c, 0xd344b, + 0xd345c, 0xd360b, 0xd361c, 0xd37cb, 0xd37dc, 0xd398b, + 0xd399c, 0xd3b4b, 0xd3b5c, 0xd3d0b, 0xd3d1c, 0xd3ecb, + 0xd3edc, 0xd408b, 0xd409c, 0xd424b, 0xd425c, 0xd440b, + 0xd441c, 0xd45cb, 0xd45dc, 0xd478b, 0xd479c, 0xd494b, + 0xd495c, 0xd4b0b, 0xd4b1c, 0xd4ccb, 0xd4cdc, 0xd4e8b, + 0xd4e9c, 0xd504b, 0xd505c, 0xd520b, 0xd521c, 0xd53cb, + 0xd53dc, 0xd558b, 0xd559c, 0xd574b, 0xd575c, 0xd590b, + 0xd591c, 0xd5acb, 0xd5adc, 0xd5c8b, 0xd5c9c, 0xd5e4b, + 0xd5e5c, 0xd600b, 0xd601c, 0xd61cb, 0xd61dc, 0xd638b, + 0xd639c, 0xd654b, 0xd655c, 0xd670b, 0xd671c, 0xd68cb, + 0xd68dc, 0xd6a8b, 0xd6a9c, 0xd6c4b, 0xd6c5c, 0xd6e0b, + 0xd6e1c, 0xd6fcb, 0xd6fdc, 0xd718b, 0xd719c, 0xd734b, + 0xd735c, 0xd750b, 0xd751c, 0xd76cb, 0xd76dc, 0xd788b, + 0xd789c, 0xd7a40, 0xd7b08, 0xd7c70, 0xd7cb9, 0xd7fc0, + 0xfb1e4, 0xfb1f0, 0xfe004, 0xfe100, 0xfe204, 0xfe300, + 0xfeff1, 0xff000, 0xff9e4, 0xffa00, 0xfff01, 0xfffc0, + 0x101fd4, 0x101fe0, 0x102e04, 0x102e10, 0x103764, 0x1037b0, + 0x10a014, 0x10a040, 0x10a054, 0x10a070, 0x10a0c4, 0x10a100, + 0x10a384, 0x10a3b0, 0x10a3f4, 0x10a400, 0x10ae54, 0x10ae70, + 0x10d244, 0x10d280, 0x10eab4, 0x10ead0, 0x10efd4, 0x10f000, + 0x10f464, 0x10f510, 0x10f824, 0x10f860, 0x110006, 0x110014, + 0x110026, 0x110030, 0x110384, 0x110470, 0x110704, 0x110710, + 0x110734, 0x110750, 0x1107f4, 0x110826, 0x110830, 0x110b06, + 0x110b34, 0x110b76, 0x110b94, 0x110bb0, 0x110bd5, 0x110be0, + 0x110c24, 0x110c30, 0x110cd5, 0x110ce0, 0x111004, 0x111030, + 0x111274, 0x1112c6, 0x1112d4, 0x111350, 0x111456, 0x111470, + 0x111734, 0x111740, 0x111804, 0x111826, 0x111830, 0x111b36, + 0x111b64, 0x111bf6, 0x111c10, 0x111c25, 0x111c40, 0x111c94, + 0x111cd0, 0x111ce6, 0x111cf4, 0x111d00, 0x1122c6, 0x1122f4, + 0x112326, 0x112344, 0x112356, 0x112364, 0x112380, 0x1123e4, + 0x1123f0, 0x112414, 0x112420, 0x112df4, 0x112e06, 0x112e34, + 0x112eb0, 0x113004, 0x113026, 0x113040, 0x1133b4, 0x1133d0, + 0x1133e4, 0x1133f6, 0x113404, 0x113416, 0x113450, 0x113476, + 0x113490, 0x1134b6, 0x1134e0, 0x113574, 0x113580, 0x113626, + 0x113640, 0x113664, 0x1136d0, 0x113704, 0x113750, 0x114356, + 0x114384, 0x114406, 0x114424, 0x114456, 0x114464, 0x114470, + 0x1145e4, 0x1145f0, 0x114b04, 0x114b16, 0x114b34, 0x114b96, + 0x114ba4, 0x114bb6, 0x114bd4, 0x114be6, 0x114bf4, 0x114c16, + 0x114c24, 0x114c40, 0x115af4, 0x115b06, 0x115b24, 0x115b60, + 0x115b86, 0x115bc4, 0x115be6, 0x115bf4, 0x115c10, 0x115dc4, + 0x115de0, 0x116306, 0x116334, 0x1163b6, 0x1163d4, 0x1163e6, + 0x1163f4, 0x116410, 0x116ab4, 0x116ac6, 0x116ad4, 0x116ae6, + 0x116b04, 0x116b66, 0x116b74, 0x116b80, 0x1171d4, 0x117200, + 0x117224, 0x117266, 0x117274, 0x1172c0, 0x1182c6, 0x1182f4, + 0x118386, 0x118394, 0x1183b0, 0x119304, 0x119316, 0x119360, + 0x119376, 0x119390, 0x1193b4, 0x1193d6, 0x1193e4, 0x1193f5, + 0x119406, 0x119415, 0x119426, 0x119434, 0x119440, 0x119d16, + 0x119d44, 0x119d80, 0x119da4, 0x119dc6, 0x119e04, 0x119e10, + 0x119e46, 0x119e50, 0x11a014, 0x11a0b0, 0x11a334, 0x11a396, + 0x11a3a5, 0x11a3b4, 0x11a3f0, 0x11a474, 0x11a480, 0x11a514, + 0x11a576, 0x11a594, 0x11a5c0, 0x11a845, 0x11a8a4, 0x11a976, + 0x11a984, 0x11a9a0, 0x11c2f6, 0x11c304, 0x11c370, 0x11c384, + 0x11c3e6, 0x11c3f4, 0x11c400, 0x11c924, 0x11ca80, 0x11ca96, + 0x11caa4, 0x11cb16, 0x11cb24, 0x11cb46, 0x11cb54, 0x11cb70, + 0x11d314, 0x11d370, 0x11d3a4, 0x11d3b0, 0x11d3c4, 0x11d3e0, + 0x11d3f4, 0x11d465, 0x11d474, 0x11d480, 0x11d8a6, 0x11d8f0, + 0x11d904, 0x11d920, 0x11d936, 0x11d954, 0x11d966, 0x11d974, + 0x11d980, 0x11ef34, 0x11ef56, 0x11ef70, 0x11f004, 0x11f025, + 0x11f036, 0x11f040, 0x11f346, 0x11f364, 0x11f3b0, 0x11f3e6, + 0x11f404, 0x11f416, 0x11f424, 0x11f430, 0x134301, 0x134404, + 0x134410, 0x134474, 0x134560, 0x16af04, 0x16af50, 0x16b304, + 0x16b370, 0x16f4f4, 0x16f500, 0x16f516, 0x16f880, 0x16f8f4, + 0x16f930, 0x16fe44, 0x16fe50, 0x16ff06, 0x16ff20, 0x1bc9d4, + 0x1bc9f0, 0x1bca01, 0x1bca40, 0x1cf004, 0x1cf2e0, 0x1cf304, + 0x1cf470, 0x1d1654, 0x1d1666, 0x1d1674, 0x1d16a0, 0x1d16d6, + 0x1d16e4, 0x1d1731, 0x1d17b4, 0x1d1830, 0x1d1854, 0x1d18c0, + 0x1d1aa4, 0x1d1ae0, 0x1d2424, 0x1d2450, 0x1da004, 0x1da370, + 0x1da3b4, 0x1da6d0, 0x1da754, 0x1da760, 0x1da844, 0x1da850, + 0x1da9b4, 0x1daa00, 0x1daa14, 0x1dab00, 0x1e0004, 0x1e0070, + 0x1e0084, 0x1e0190, 0x1e01b4, 0x1e0220, 0x1e0234, 0x1e0250, + 0x1e0264, 0x1e02b0, 0x1e08f4, 0x1e0900, 0x1e1304, 0x1e1370, + 0x1e2ae4, 0x1e2af0, 0x1e2ec4, 0x1e2f00, 0x1e4ec4, 0x1e4f00, + 0x1e8d04, 0x1e8d70, 0x1e9444, 0x1e94b0, 0x1f1e6d, 0x1f2000, + 0x1f3fb4, 0x1f4000, 0xe00001, 0xe00204, 0xe00801, 0xe01004, + 0xe01f01, 0xe10000, }; inline constexpr char32_t __incb_linkers[] = { diff --git a/libstdc++-v3/include/bits/unicode.h b/libstdc++-v3/include/bits/unicode.h index e49498a..f1b2b35 100644 --- a/libstdc++-v3/include/bits/unicode.h +++ b/libstdc++-v3/include/bits/unicode.h @@ -163,7 +163,7 @@ namespace __unicode else _M_read(); } - else if (_M_buf_index + 1 <= _M_buf_last) + else if (_M_buf_index + 1 < _M_buf_last) ++_M_buf_index; return *this; } @@ -603,6 +603,7 @@ inline namespace __v15_1_0 return (__p - __width_edges) % 2 + 1; } + // @pre c <= 0x10FFFF constexpr _Gcb_property __grapheme_cluster_break_property(char32_t __c) noexcept { @@ -621,9 +622,13 @@ inline namespace __v15_1_0 return std::find(__incb_linkers, __end, __c) != __end; } + // @pre c <= 0x10FFFF constexpr _InCB __incb_property(char32_t __c) noexcept { + if ((__c << 2) < __incb_edges[0]) [[likely]] + return _InCB(0); + constexpr uint32_t __mask = 0x3; auto* __end = std::end(__incb_edges); auto* __p = std::lower_bound(__incb_edges, __end, (__c << 2) | __mask); @@ -634,10 +639,10 @@ inline namespace __v15_1_0 __is_extended_pictographic(char32_t __c) { if (__c < __xpicto_edges[0]) [[likely]] - return 1; + return 0; auto* __p = std::upper_bound(__xpicto_edges, std::end(__xpicto_edges), __c); - return (__p - __xpicto_edges) % 2 + 1; + return (__p - __xpicto_edges) % 2; } struct _Grapheme_cluster_iterator_base @@ -732,22 +737,22 @@ inline namespace __v15_1_0 public: // TODO: Change value_type to be subrange<_U32_iterator> instead? - // That would be the whole cluster, not just the first code point. - // Would need to change type of _M_start to _U32_iterator, so that - // operator* just does return value_type{_M_start, _M_next}. // Alternatively, value_type could be _Utf32_view>. + // That would be the whole cluster, not just the first code point. + // Would need to store two iterators and find end of current cluster + // on increment, so operator* returns value_type(_M_base, _M_next). using value_type = char32_t; using iterator_concept = forward_iterator_tag; + using difference_type = ptrdiff_t; constexpr _Iterator(_U32_iterator __i) - : _M_start(__i.base()), _M_next(__i) + : _M_base(__i) { - if (_M_start != __i.end()) + if (__i != __i.end()) { _M_c = *__i; _M_prop = __grapheme_cluster_break_property(_M_c); - operator++(); // Finds the end of the first cluster. } } @@ -764,11 +769,11 @@ inline namespace __v15_1_0 constexpr _Iterator& operator++() { - const auto __end = _M_next.end(); - if (_M_next != __end) + const auto __end = _M_base.end(); + if (_M_base != __end) { auto __p_prev = _M_prop; - auto __it = _M_next; + auto __it = _M_base; while (++__it != __end) { char32_t __c = *__it; @@ -784,11 +789,8 @@ inline namespace __v15_1_0 } __p_prev = __p; } - _M_start = _M_next.base(); - _M_next = __it; + _M_base = __it; } - else - _M_start = __end; return *this; } @@ -802,18 +804,18 @@ inline namespace __v15_1_0 constexpr bool operator==(const _Iterator& __i) const - { return _M_start == __i._M_start; } + { return _M_base == __i._M_base; } // This supports iter != iter.end() constexpr bool operator==(const ranges::sentinel_t<_View>& __i) const - { return _M_start == __i; } + { return _M_base == __i; } // Iterator to the start of the current cluster. - constexpr auto base() const { return _M_start; } + constexpr auto base() const { return _M_base.base(); } // The end of the underlying view (not the end of the current cluster!) - constexpr auto end() const { return _M_next.end(); } + constexpr auto end() const { return _M_base.end(); } // Field width of the first code point in the cluster. constexpr int @@ -821,8 +823,7 @@ inline namespace __v15_1_0 { return __field_width(_M_c); } private: - ranges::iterator_t<_View> _M_start; - _U32_iterator _M_next; + _U32_iterator _M_base; // Implement the Grapheme Cluster Boundary Rules from Unicode Annex #29 // http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules @@ -891,13 +892,13 @@ inline namespace __v15_1_0 // Do not break within certain combinations with // Indic_Conjunct_Break (InCB)=Linker. if (_M_incb_linker_seen - && __incb_property(*_M_start) == _InCB::_Consonant + && __incb_property(_M_c) == _InCB::_Consonant && __incb_property(*__curr) == _InCB::_Consonant) { - // Match [_M_start, __curr] against regular expression + // Match [_M_base, __curr] against regular expression // Consonant ([Extend Linker]* Linker [Extend Linker]* Consonant)+ bool __have_linker = false; - auto __it = _M_start; + auto __it = _M_base; while (++__it != __curr) { if (__is_incb_linker(*__it)) diff --git a/libstdc++-v3/testsuite/ext/unicode/grapheme_view.cc b/libstdc++-v3/testsuite/ext/unicode/grapheme_view.cc new file mode 100644 index 0000000..ac1e8c5 --- /dev/null +++ b/libstdc++-v3/testsuite/ext/unicode/grapheme_view.cc @@ -0,0 +1,95 @@ +// { dg-do compile { target c++20 } } + +#include // includes +#include +#include +#include + +namespace uc = std::__unicode; +using namespace std::string_view_literals; + +constexpr void +test_breaks() +{ + VERIFY(uc::__field_width(u8"\N{LATIN SMALL LETTER E WITH ACUTE}"sv) == 1 ); + + auto sv = u8"ee\N{COMBINING ACUTE ACCENT}e"sv; + auto data = sv.data(); + VERIFY( uc::__field_width(sv) == 3 ); + VERIFY( uc::__truncate(sv, 3) == 3 ); + VERIFY( uc::__truncate(sv, 4) == 3 ); + VERIFY( sv == data ); + + VERIFY( uc::__truncate(sv, 2) == 2 ); + VERIFY( sv == u8"ee\N{COMBINING ACUTE ACCENT}"sv ); + + sv = data; + sv.remove_prefix(1); + VERIFY( uc::__field_width(sv) == 2 ); + VERIFY( uc::__truncate(sv, 3) == 2 ); + VERIFY( sv == data+1 ); + + sv = u8"\N{REGIONAL INDICATOR SYMBOL LETTER G}" + "\N{REGIONAL INDICATOR SYMBOL LETTER B}"; // GB flag emoji + data = sv.data(); + VERIFY( uc::__field_width(sv) == 1 ); + VERIFY( uc::__truncate(sv, 2) == 1 ); + VERIFY( sv == data ); + VERIFY( uc::__truncate(sv, 1) == 1 ); // Do not break inside a flag emoji. + VERIFY( sv == data ); + + sv = u8"abcd" + "\N{REGIONAL INDICATOR SYMBOL LETTER G}" // 4 bytes + "\N{REGIONAL INDICATOR SYMBOL LETTER B}" // 4 bytes + "\N{DEVANAGARI LETTER KA}" // 3 bytes + "\N{DEVANAGARI SIGN VIRAMA}" // 3 bytes + "\N{DEVANAGARI LETTER RA}" // 3 bytes + "\N{MAN}\N{ZERO WIDTH JOINER}" // 4+3 bytes + "\N{WOMAN}\N{ZERO WIDTH JOINER}" // 4+3 bytes + "\N{GIRL}\N{ZERO WIDTH JOINER}" // 4+3 bytes + "\N{BOY}\N{ZERO WIDTH JOINER}" // 4+3 bytes + "\N{HANGUL CHOSEONG KIYEOK}" // 3 bytes + "\N{HANGUL CHOSEONG KIYEOK}" // 3 bytes + "\N{HANGUL CHOSEONG KIYEOK}" // 3 bytes + "\N{HANGUL CHOSEONG KIYEOK}" // 3 bytes + "\N{HANGUL JUNGSEONG A}" // 3 bytes + "\N{HANGUL JONGSEONG KIYEOK}" // 3 bytes + "\N{HANGUL JONGSEONG KIYEOK}" // 3 bytes + "\N{HANGUL JONGSEONG KIYEOK}"; // 3 bytes + + uc::_Grapheme_cluster_view gv(sv); + auto iter = gv.begin(); + VERIFY( iter.base() == sv.data() ); + VERIFY( *iter == U'a' ); + std::ranges::advance(iter, 3); + VERIFY( *iter == U'd' ); + VERIFY( iter.base() == sv.data() + 3 ); + ++iter; + VERIFY( *iter == U'\N{REGIONAL INDICATOR SYMBOL LETTER G}' ); + VERIFY( iter.base() == sv.data() + 4 ); + ++iter; + VERIFY( *iter == U'\N{DEVANAGARI LETTER KA}' ); + VERIFY( iter.base() == sv.data() + 4 + 8 ); + ++iter; + VERIFY( *iter == U'\N{MAN}' ); + VERIFY( iter.base() == sv.data() + 4 + 8 + 9 ); + ++iter; + VERIFY( iter.base() == sv.data() + 4 + 8 + 9 + 28 ); + VERIFY( *iter == U'\N{HANGUL CHOSEONG KIYEOK}' ); + ++iter; + VERIFY( iter.base() == sv.data() + 4 + 8 + 9 + 28 + 24 ); + VERIFY( iter == gv.end() ); + ++iter; + VERIFY( iter == gv.end() ); +} + +int main() +{ + auto run_tests = []{ + test_breaks(); + return true; + }; + + VERIFY( run_tests() ); + static_assert( run_tests() ); +} diff --git a/libstdc++-v3/testsuite/ext/unicode/properties.cc b/libstdc++-v3/testsuite/ext/unicode/properties.cc new file mode 100644 index 0000000..8600a3d --- /dev/null +++ b/libstdc++-v3/testsuite/ext/unicode/properties.cc @@ -0,0 +1,128 @@ +// { dg-do compile { target c++20 } } + +#include // includes +#include +#include +#include + +namespace uc = std::__unicode; +using namespace std::string_view_literals; + +constexpr char32_t riA = U'\N{REGIONAL INDICATOR SYMBOL LETTER A}'; +constexpr char32_t riZ = U'\N{REGIONAL INDICATOR SYMBOL LETTER Z}'; + +static_assert( uc::__field_width(U'\0') == 1 ); +static_assert( uc::__field_width(U'1') == 1 ); +static_assert( uc::__field_width(U'a') == 1 ); +static_assert( uc::__field_width(riA) == 1 ); +static_assert( uc::__field_width(U'\N{OBLIQUE HYPHEN}') == 1 ); +static_assert( uc::__field_width(U'\N{CIRCLED NUMBER EIGHTY ON BLACK SQUARE}') + == 1 ); + +static_assert( uc::__field_width(U'\N{SESQUIQUADRATE}') == 1 ); +static_assert( uc::__field_width(U'\N{SOCCER BALL}') == 2 ); +static_assert( uc::__field_width(U'\N{BASEBALL}') == 2 ); +static_assert( uc::__field_width(U'\N{SQUARED KEY}') == 1 ); +static_assert( uc::__field_width(U'\N{BLACK DRAUGHTS KING}') == 1 ); +static_assert( uc::__field_width(U'\N{SNOWMAN WITHOUT SNOW}') == 2 ); + +static_assert( uc::__field_width(U'\N{IDEOGRAPHIC SPACE}') == 2 ); +static_assert( uc::__field_width(U'\N{IDEOGRAPHIC COMMA}') == 2 ); +static_assert( uc::__field_width(U'\N{CIRCLED IDEOGRAPH ONE}') == 2 ); + +// EastAsianWidth.txt says these are normal width, but C++ says width 2: +static_assert( uc::__field_width(U'\u4DC0') == 2 ); +static_assert( uc::__field_width(U'\u4DC1') == 2 ); +static_assert( uc::__field_width(U'\u4DFF') == 2 ); +// EastAsianWidth.txt says W and C++ says 2: +static_assert( uc::__field_width(U'\U0001F300') == 2 ); +static_assert( uc::__field_width(U'\U0001F320') == 2 ); +// EastAsianWidth.txt says N but C++ says 2: +static_assert( uc::__field_width(U'\U0001F321') == 2 ); +static_assert( uc::__field_width(U'\U0001F5FA') == 2 ); +// EastAsianWidth.txt says W and C++ says 2: +static_assert( uc::__field_width(U'\U0001F5FF') == 2 ); +static_assert( uc::__field_width(U'\U0001F600') == 2 ); + +static_assert( uc::__field_width(U'\U0001F900') == 2 ); +static_assert( uc::__field_width(U'\U0001F90B') == 2 ); +static_assert( uc::__field_width(U'\U0001F90C') == 2 ); +static_assert( uc::__field_width(U'\U0001F93B') == 2 ); +static_assert( uc::__field_width(U'\U0001F9FF') == 2 ); +static_assert( uc::__field_width(U'\U0001FA00') == 1 ); +static_assert( uc::__field_width(U'\U0001FA69') == 1 ); +static_assert( uc::__field_width(U'\U0001FA70') == 2 ); +static_assert( uc::__field_width(U'\U0001FAF8') == 2 ); +static_assert( uc::__field_width(U'\U0001FAF9') == 1 ); + +using enum uc::_Gcb_property; +static_assert( uc::__grapheme_cluster_break_property(U'\0') == _Gcb_Control ); +static_assert( uc::__grapheme_cluster_break_property(U'a') == _Gcb_Other ); +static_assert( uc::__grapheme_cluster_break_property(riA) + == _Gcb_Regional_Indicator ); +static_assert( uc::__grapheme_cluster_break_property(riZ) + == _Gcb_Regional_Indicator ); +static_assert( uc::__grapheme_cluster_break_property(riA - 1) == _Gcb_Other ); +static_assert( uc::__grapheme_cluster_break_property(riZ + 1) == _Gcb_Other ); +static_assert( uc::__grapheme_cluster_break_property(U'\uD788') == _Gcb_LV ); +static_assert( uc::__grapheme_cluster_break_property(U'\uD7A3') == _Gcb_LVT ); +static_assert( uc::__grapheme_cluster_break_property(U'\u200D') == _Gcb_ZWJ ); +static_assert( uc::__grapheme_cluster_break_property(U'\U0001D16D') + == _Gcb_SpacingMark ); +static_assert( uc::__grapheme_cluster_break_property(U'\U0001D16E') + == _Gcb_Extend ); +static_assert( uc::__grapheme_cluster_break_property(U'\U000E01EF') + == _Gcb_Extend ); +static_assert( uc::__grapheme_cluster_break_property(U'\U000E01F0') + == _Gcb_Control ); +static_assert( uc::__grapheme_cluster_break_property(U'\U000E0FFF') + == _Gcb_Control ); +static_assert( uc::__grapheme_cluster_break_property(U'\U000E1000') + == _Gcb_Other ); + +static_assert( uc::__incb_property(U'\0') == uc::_InCB{0} ); +static_assert( uc::__incb_property(U'a') == uc::_InCB{0} ); +static_assert( uc::__incb_property(U'\N{DEVANAGARI LETTER KA}') + == uc::_InCB::_Consonant ); +static_assert( uc::__incb_property(U'\N{DEVANAGARI LETTER RA}') + == uc::_InCB::_Consonant ); +static_assert( uc::__incb_property(U'\N{DEVANAGARI LETTER YYA}') + == uc::_InCB::_Consonant ); +static_assert( uc::__incb_property(U'\N{DEVANAGARI LETTER YYA}' + 1) + == uc::_InCB{0} ); +static_assert( uc::__incb_property(U'\N{DEVANAGARI SIGN NUKTA}') + == uc::_InCB::_Extend ); +static_assert( uc::__incb_property(U'\N{DEVANAGARI SIGN NUKTA}' + 1) + == uc::_InCB{0} ); +static_assert( uc::__incb_property(U'\U0001E94A') == uc::_InCB::_Extend ); +static_assert( uc::__incb_property(U'\U0001E94B') == uc::_InCB{0} ); + +static_assert( ! uc::__is_incb_linker(U'\0') ); +static_assert( ! uc::__is_incb_linker(U'a') ); +static_assert( uc::__is_incb_linker(U'\N{DEVANAGARI SIGN VIRAMA}') ); +static_assert( ! uc::__is_incb_linker(U'\N{DEVANAGARI SIGN VIRAMA}' + 1) ); +static_assert( ! uc::__is_incb_linker(U'\N{DEVANAGARI SIGN VIRAMA}' - 1) ); +static_assert( ! uc::__is_incb_linker(U'\u0FFF') ); +static_assert( ! uc::__is_incb_linker(U'\uFFFD') ); + +static_assert( ! uc::__is_extended_pictographic(U'\0') ); +static_assert( ! uc::__is_extended_pictographic(U'a') ); +static_assert( ! uc::__is_extended_pictographic(riA) ); +static_assert( ! uc::__is_extended_pictographic(riZ) ); +static_assert( ! uc::__is_extended_pictographic(U'\N{COPYRIGHT SIGN}' - 1) ); +static_assert( uc::__is_extended_pictographic(U'\N{COPYRIGHT SIGN}') ); +static_assert( ! uc::__is_extended_pictographic(U'\N{COPYRIGHT SIGN}' + 1) ); +static_assert( ! uc::__is_extended_pictographic(U'\N{INFORMATION SOURCE}' - 1) ); +static_assert( uc::__is_extended_pictographic(U'\N{INFORMATION SOURCE}') ); +static_assert( ! uc::__is_extended_pictographic(U'\N{INFORMATION SOURCE}' + 1) ); +static_assert( ! uc::__is_extended_pictographic(U'\N{LEFT RIGHT ARROW}' - 1) ); +static_assert( uc::__is_extended_pictographic(U'\N{LEFT RIGHT ARROW}') ); +static_assert( uc::__is_extended_pictographic(U'\N{LEFT RIGHT ARROW}' + 1) ); +static_assert( uc::__is_extended_pictographic(U'\N{SOUTH WEST ARROW}') ); +static_assert( ! uc::__is_extended_pictographic(U'\N{SOUTH WEST ARROW}' + 1) ); +static_assert( uc::__is_extended_pictographic(U'\N{POSTBOX}') ); +static_assert( ! uc::__is_extended_pictographic(U'\U0001EFFF') ); +static_assert( uc::__is_extended_pictographic(U'\U0001F000') ); +static_assert( uc::__is_extended_pictographic(U'\U0001FFFD') ); +static_assert( ! uc::__is_extended_pictographic(U'\U0001FFFE') ); +static_assert( ! uc::__is_extended_pictographic(U'\U0001FFFF') ); diff --git a/libstdc++-v3/testsuite/ext/unicode/view.cc b/libstdc++-v3/testsuite/ext/unicode/view.cc index eaab5c7..79ea2bb 100644 --- a/libstdc++-v3/testsuite/ext/unicode/view.cc +++ b/libstdc++-v3/testsuite/ext/unicode/view.cc @@ -85,6 +85,35 @@ test_illformed_utf32() VERIFY( std::ranges::equal(uc::_Utf32_view(s), U"\uFFFD"sv) ); } +constexpr void +test_past_the_end() +{ + const auto s8 = u8"1234"sv; + uc::_Utf32_view v(s8); + auto iter = v.begin(); + std::advance(iter, 4); + VERIFY( iter == v.end() ); + // Incrementing past the end has well-defined behaviour. + ++iter; + VERIFY( iter == v.end() ); + VERIFY( *iter == U'4' ); // Still dereferenceable. + ++iter; + VERIFY( iter == v.end() ); + VERIFY( *iter == U'4' ); + iter++; + VERIFY( iter == v.end() ); + VERIFY( *iter == U'4' ); + + std::string_view empty; + uc::_Utf32_view v2(empty); + auto iter2 = v2.begin(); + VERIFY( iter2 == v2.end() ); + VERIFY( *iter2 == U'\0' ); + iter++; + VERIFY( iter2 == v2.end() ); + VERIFY( *iter2 == U'\0' ); +} + int main() { auto run_tests = []{ @@ -94,6 +123,7 @@ int main() test_illformed_utf8(); test_illformed_utf16(); test_illformed_utf32(); + test_past_the_end(); return true; }; -- cgit v1.1 From 73ce73fcaded5525c070ee4c9ed3ae16c98e86eb Mon Sep 17 00:00:00 2001 From: GCC Administrator Date: Wed, 10 Jan 2024 00:18:30 +0000 Subject: Daily bump. --- ChangeLog | 11 +++ contrib/ChangeLog | 5 ++ gcc/ChangeLog | 199 +++++++++++++++++++++++++++++++++++++++++++++++ gcc/DATESTAMP | 2 +- gcc/ada/ChangeLog | 201 +++++++++++++++++++++++++++++++++++++++++++++++ gcc/c-family/ChangeLog | 25 ++++++ gcc/c/ChangeLog | 5 ++ gcc/cp/ChangeLog | 189 ++++++++++++++++++++++++++++++++++++++++++++ gcc/m2/ChangeLog | 52 +++++++++++++ gcc/testsuite/ChangeLog | 203 ++++++++++++++++++++++++++++++++++++++++++++++++ include/ChangeLog | 5 ++ libcc1/ChangeLog | 7 ++ libgomp/ChangeLog | 23 ++++++ libstdc++-v3/ChangeLog | 26 +++++++ 14 files changed, 952 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 3b32e6d..513ee47 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2024-01-09 Siddhesh Poyarekar + + * SECURITY.txt: Drop "exploitable" in the hardening section. + +2024-01-09 Tom Tromey + + * Makefile.in: Rebuild. + * Makefile.tpl (BASE_EXPORTS): Add GUILE. + (GUILE): New variable. + * Makefile.def (flags_to_pass): Add GUILE. + 2024-01-08 Joseph Myers * MAINTAINERS: Update my email address. diff --git a/contrib/ChangeLog b/contrib/ChangeLog index 569b889..04bde02 100644 --- a/contrib/ChangeLog +++ b/contrib/ChangeLog @@ -1,3 +1,8 @@ +2024-01-09 Jonathan Wakely + + * unicode/gen_libstdcxx_unicode_data.py: Print out Gcb_property + enumerators in the order they're seen, not alphabetical order. + 2024-01-08 Jonathan Wakely * unicode/README: Add notes about generating libstdc++ tables. diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 5d5cf71..55754ed 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,202 @@ +2024-01-09 Jeff Law + + * config/epiphany/constraints.md (Car): Allow -1024..1023, no more, + no less. + +2024-01-09 Richard Sandiford + + * config/mn10300/mn10300.md (subdi3_degenerate): Add isa attribute. + +2024-01-09 Tamar Christina + + * tree-vect-loop.cc (vectorizable_live_operation_1): Drop unused + restart_loop. + (vectorizable_live_operation): Likewise. + +2024-01-09 Tamar Christina + + PR tree-optimization/113199 + * tree-vect-loop.cc (vectorizable_live_operation_1): Use + BIT_FIELD_REF. + +2024-01-09 Jakub Jelinek + + PR target/113270 + * config.gcc (aarch64*-*-*): Add aarch64-builtins.h to target_gtfiles. + * config/aarch64/aarch64-builtins.cc (aarch64_simd_types): Add extern + GTY(()) declaration before the definition, drop GTY(()) drom the + definition. + +2024-01-09 Richard Biener + + PR tree-optimization/113026 + * tree-vect-loop-manip.cc (vect_do_peeling): Remove + redundant and wrong niter bound setting. Move niter + bound adjustment down. + +2024-01-09 Tamar Christina + + PR middle-end/113163 + * tree-vect-loop-manip.cc (vect_can_peel_nonlinear_iv_p): + Reject non-linear inductions that aren't supported. + +2024-01-09 Roger Sayle + + * config/arc/arc.cc (arc_shift_alg): New enumerated type for + left shift implementation strategies. + (arc_shift_info): Type for each entry of the shift strategy table. + (arc_shift_context_idx): Return a integer value for each code + generation context, used as an index + (arc_ashl_alg): Table indexed by context and shifted bit count. + (arc_split_ashl): Use the arc_ashl_alg table to select SImode + left shift implementation. + (arc_rtx_costs) : Use the arc_ashl_alg table to + provide accurate costs, when optimizing for speed or size. + +2024-01-09 Juzhe-Zhong + + * config/riscv/riscv-vector-costs.cc (loop_invariant_op_p): Fix loop invariant check. + +2024-01-09 Julian Brown + + * gimplify.cc (gimplify_expr): Ensure OMP_ARRAY_SECTION has been + processed out before gimplification. + * tree-pretty-print.cc (dump_generic_node): Support OMP_ARRAY_SECTION. + * tree.def (OMP_ARRAY_SECTION): New tree code. + +2024-01-09 Jakub Jelinek + + PR tree-optimization/113210 + * tree-vect-loop.cc (vect_get_loop_niters): If non-INTEGER_CST + value in *number_of_iterationsm1 PLUS_EXPR 1 is folded into + INTEGER_CST, recompute *number_of_iterationsm1 as the INTEGER_CST + minus 1. + +2024-01-09 Eric Botcazou + + PR rtl-optimization/113140 + * reorg.cc (fill_slots_from_thread): If we are to branch after the + last instruction of the function, create an end label. + +2024-01-09 Roger Sayle + Hongtao Liu + + PR target/112992 + * config/i386/i386-expand.cc + (ix86_convert_const_wide_int_to_broadcast): Allow call to + ix86_expand_vector_init_duplicate to fail, and return NULL_RTX. + (ix86_broadcast_from_constant): Revert recent change; Return a + suitable MEMREF independently of mode/target combinations. + (ix86_expand_vector_move): Allow ix86_expand_vector_init_duplicate + to decide whether expansion is possible/preferrable. Only try + forcing DImode constants to memory (and trying again) if calling + ix86_expand_vector_init_duplicate fails with an DImode immediate + constant. + (ix86_expand_vector_init_duplicate) : Try using + V4SImode for suitable immediate constants. + : Try using V8SImode for suitable constants. + : Fail for CONST_INT_P, i.e. use constant pool. + : Likewise. + : For CONST_INT_P try using V4SImode via widen. + : For CONT_INT_P try using V8HImode via widen. +