From 2c0fc2b4704743558d595eb00ec046f5554643a6 Mon Sep 17 00:00:00 2001 From: waffl3x Date: Tue, 21 Nov 2023 05:42:38 -0700 Subject: [PATCH] This is a temporary message. gcc/cp/ChangeLog: * call.cc (add_candidates): (build_over_call): * class.cc (add_method): (resolve_address_of_overloaded_function): * cp-tree.h (struct lang_decl_fn): (DECL_IOBJ_MEMBER_FUNC_P): (DECL_FUNCTION_XOBJ_FLAG): (DECL_XOBJ_MEMBER_FUNC_P): (DECL_OBJECT_MEMBER_FUNC_P): (DECL_FUNCTION_MEMBER_P): (enum auto_deduction_context): (TFF_XOBJ_FUNC): (enum cp_decl_spec): * decl.cc (grokfndecl): (grokdeclarator): (grok_op_properties): (start_preparsed_function): * error.cc (dump_function_decl): (dump_parameters): (function_category): * lambda.cc (build_capture_proxy): * module.cc (trees_out::lang_decl_bools): (trees_in::lang_decl_bools): * parser.cc (cp_parser_lambda_declarator_opt): (cp_parser_decl_specifier_seq): (cp_parser_parameter_declaration): (set_and_check_decl_spec_loc): * pt.cc (instantiate_body): * search.cc (look_for_overrides_here): (look_for_overrides_r): * semantics.cc (finish_this_expr): * tree.cc (build_min_non_dep_op_overload): * typeck.cc (cp_build_addr_expr_1): gcc/testsuite/ChangeLog: * 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-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-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-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. * g++.dg/cpp23/explicit-obj-lambda1.C: New test. * g++.dg/cpp23/explicit-obj-lambdaX2.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. --- gcc/cp/call.cc | 155 +++--- gcc/cp/class.cc | 205 +++++++- gcc/cp/cp-tree.h | 41 +- gcc/cp/decl.cc | 181 ++++++- gcc/cp/error.cc | 8 +- gcc/cp/lambda.cc | 8 +- gcc/cp/module.cc | 2 + gcc/cp/parser.cc | 159 +++++- gcc/cp/pt.cc | 22 +- gcc/cp/search.cc | 16 +- gcc/cp/semantics.cc | 35 +- gcc/cp/tree.cc | 25 +- gcc/cp/typeck.cc | 23 + .../g++.dg/cpp23/explicit-obj-basic1.C | 113 ++++ .../g++.dg/cpp23/explicit-obj-basic2.C | 27 + .../g++.dg/cpp23/explicit-obj-basic3.C | 495 ++++++++++++++++++ .../g++.dg/cpp23/explicit-obj-basic4.C | 111 ++++ .../g++.dg/cpp23/explicit-obj-by-value1.C | 49 ++ .../g++.dg/cpp23/explicit-obj-by-value2.C | 59 +++ .../g++.dg/cpp23/explicit-obj-by-value3.C | 42 ++ .../g++.dg/cpp23/explicit-obj-by-value4.C | 19 + .../g++.dg/cpp23/explicit-obj-cxx-dialect-A.C | 6 + .../g++.dg/cpp23/explicit-obj-cxx-dialect-B.C | 6 + .../g++.dg/cpp23/explicit-obj-cxx-dialect-C.C | 8 + .../g++.dg/cpp23/explicit-obj-cxx-dialect-D.C | 7 + .../g++.dg/cpp23/explicit-obj-cxx-dialect-E.C | 7 + .../g++.dg/cpp23/explicit-obj-diagnostics1.C | 138 +++++ .../g++.dg/cpp23/explicit-obj-diagnostics2.C | 25 + .../g++.dg/cpp23/explicit-obj-diagnostics4.C | 19 + .../g++.dg/cpp23/explicit-obj-diagnostics5.C | 15 + .../g++.dg/cpp23/explicit-obj-diagnostics6.C | 22 + .../g++.dg/cpp23/explicit-obj-diagnostics7.C | 25 + .../g++.dg/cpp23/explicit-obj-lambda1.C | 11 + .../g++.dg/cpp23/explicit-obj-lambdaX2.C | 22 + .../g++.dg/cpp23/explicit-obj-ops-mem-arrow.C | 27 + .../cpp23/explicit-obj-ops-mem-assignment.C | 26 + .../g++.dg/cpp23/explicit-obj-ops-mem-call.C | 39 ++ .../cpp23/explicit-obj-ops-mem-subscript.C | 39 ++ .../cpp23/explicit-obj-ops-non-mem-dep.C | 57 ++ .../cpp23/explicit-obj-ops-non-mem-non-dep.C | 56 ++ .../g++.dg/cpp23/explicit-obj-ops-non-mem.h | 209 ++++++++ .../cpp23/explicit-obj-ops-requires-mem.C | 170 ++++++ .../cpp23/explicit-obj-ops-requires-non-mem.C | 236 +++++++++ .../g++.dg/cpp23/explicit-obj-redecl.C | 245 +++++++++ .../g++.dg/cpp23/explicit-obj-redecl2.C | 160 ++++++ 45 files changed, 3229 insertions(+), 141 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-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-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-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 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX2.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 diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 2eb54b5b6ed..82ae5facd20 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -6527,7 +6527,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_FUNC_P (fn)) { /* Figure out where the object arg comes from. If this function is a non-static member and we didn't get an @@ -9728,14 +9728,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; @@ -9921,45 +9916,70 @@ 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; + 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; + }; + + 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_FUNC_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) { @@ -10034,25 +10054,37 @@ 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; + } + else if (DECL_XOBJ_MEMBER_FUNC_P (fn)) + { + /* We currently handle for the case where first_arg is NULL_TREE + in the future this should be changed and the assert reactivated. */ + #if 0 + gcc_assert (first_arg); + #endif + 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 @@ -10074,47 +10106,38 @@ 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; - - 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); + auto determine_conversion_warning = [&]() + { + return !(null_node_p (current_arg) + && DECL_TEMPLATE_INFO (fn) + && cand->template_decl + && !cand->explicit_targs); + }; - 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], + determine_conversion_warning ()); if (val == 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; + argarray[argarray_size++] = val; } /* Ellipsis */ @@ -10151,11 +10174,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. @@ -10171,7 +10194,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. */ diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index 0d8b780ba2f..4537de15fdc 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -1079,8 +1079,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_FUNC_P (fn) + && DECL_IOBJ_MEMBER_FUNC_P (method) /* Either both or neither need to be ref-qualified for differing quals to allow overloading. */ && (FUNCTION_REF_QUALIFIED (fn_type) @@ -1089,6 +1089,164 @@ add_method (tree type, tree method, bool via_using) || type_memfn_rqual (fn_type) != type_memfn_rqual (method_type))) continue; + auto get_object_param = [](tree fn) + { + gcc_assert (DECL_OBJECT_MEMBER_FUNC_P (fn)); + return TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fn))); + }; + auto reference_qual = [](tree ref) + { + gcc_assert (TYPE_REF_P (ref)); + return TYPE_REF_IS_RVALUE (ref) ? REF_QUAL_RVALUE + : REF_QUAL_LVALUE; + }; + + /* 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_FUNC_P (fn) && !DECL_XOBJ_MEMBER_FUNC_P (method)) + || DECL_STATIC_FUNCTION_P (fn) || DECL_STATIC_FUNCTION_P (method)) + /* Early escape. */; + else if (DECL_XOBJ_MEMBER_FUNC_P (fn) + && DECL_XOBJ_MEMBER_FUNC_P (method)) + { + 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_FUNC_P (fn) + || DECL_XOBJ_MEMBER_FUNC_P (method)) + { + tree xobj_fn = DECL_XOBJ_MEMBER_FUNC_P (fn) ? fn : method; + /* A reference, pointer, or something else. */ + tree xobj_param = get_object_param (xobj_fn); + + /* We know an iobj parameter must be a reference. If our xobj + parameter is a pointer, we know this is not a redeclaration. + This also catches array parameters, those are pointers too. */ + if (TYPE_PTR_P (xobj_param)) + continue; + + /* An iobj member function's object parameter can't be an unrelated + type, we know this can't be a redeclaration if the xobj member + function's object parameter is an unrelated type. + If the iobj member function was introduced with a using + declaration, then the type of it's object parameter is still + that of the class we are currently adding a member function to, + so this assumption holds true in that case as well. + + [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. + + We don't get to bail yet out even if the xobj parameter is + by-value as elaborated on below. */ + if (DECL_CONTEXT (xobj_fn) + != TYPE_MAIN_VARIANT (non_reference (xobj_param))) + continue; + + /* 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. */ + + tree iobj_fn = DECL_IOBJ_MEMBER_FUNC_P (fn) ? fn : method; + tree iobj_fn_type = TREE_TYPE (iobj_fn); + cp_ref_qualifier const iobj_ref_qual + = type_memfn_rqual (iobj_fn_type); + /* I am concerned about the other qualifier bits, for now I will mask + them off. */ + 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. + + 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 procede 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 a redeclaration 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 a redeclaration if the iobj + member function has any cv-ref qualifiers. The only case where + we might still be dealing with a redeclaration is when the iobj + member function lacks any cv-ref qualification. */ + if (!TYPE_REF_P (xobj_param)) + { + if (iobj_ref_qual || iobj_cv_quals) + continue; + } + else + { + /* We are dealing with an xobj parameter that is a reference now, + but due to [basic.scope.scope.3] we need to ignore it's + reference qualifier. */ + cp_ref_qualifier const xobj_ref_qual + = !TYPE_REF_P (xobj_param) || !iobj_ref_qual + ? REF_QUAL_NONE : reference_qual (xobj_param); + + /* 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, this + definitely isn't a redeclaration. */ + if (iobj_ref_qual != xobj_ref_qual + || iobj_cv_quals != xobj_cv_quals) + continue; + } + } + else + gcc_unreachable (); + tree real_fn = fn; tree real_method = method; @@ -8724,21 +8882,40 @@ 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) - && !(complain & tf_ptrmem_ok) && !flag_ms_extensions) + if (DECL_OBJECT_MEMBER_FUNC_P (fn) + && !(complain & tf_ptrmem_ok)) { - static int explained; - - if (!(complain & tf_error)) - return error_mark_node; - - auto_diagnostic_group d; - if (permerror (input_location, "assuming pointer to member %qD", fn) - && !explained) + /* Don't disable error even if -fms-extensions is passed, this is a new + feature so we (hopefully) don't need to support the behavior. */ + if (DECL_XOBJ_MEMBER_FUNC_P (fn)) { - inform (input_location, "(a pointer to member can only be " + if (!(complain & tf_error)) + return error_mark_node; + auto_diagnostic_group d; + /* Error message could be better, fixit would be ideal. + Should also match the error in typeck.cc:cp_build_addr_expr_1. */ + error_at (input_location, + "taking the address of an explicit object member " + "function must be qualified"); + inform (input_location, + "(a pointer to explicit object member function can only be " "formed with %<&%E%>)", fn); - explained = 1; + } + else if (DECL_IOBJ_MEMBER_FUNC_P (fn) && !flag_ms_extensions) + { + static int explained; + + if (!(complain & tf_error)) + return error_mark_node; + + 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 98b29e9cf81..cdc98504c99 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -2938,8 +2938,9 @@ struct GTY(()) lang_decl_fn { unsigned maybe_deleted : 1; unsigned coroutine_p : 1; unsigned implicit_constexpr : 1; + unsigned xobj_func : 1; - unsigned spare : 9; + unsigned spare : 8; /* 32-bits padding on 64-bit host. */ @@ -3338,14 +3339,34 @@ struct GTY(()) lang_decl { (LANG_DECL_FN_CHECK (NODE)->static_function) /* Nonzero for FUNCTION_DECL means that this decl is a non-static - member function. */ + member function, use DECL_IOBJ_MEMBER_FUNC_P instead. */ #define DECL_NONSTATIC_MEMBER_FUNCTION_P(NODE) \ (TREE_CODE (TREE_TYPE (NODE)) == METHOD_TYPE) +/* Nonzero for FUNCTION_DECL means that this decl is an implicit object + member function. */ +#define DECL_IOBJ_MEMBER_FUNC_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_FUNC_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_FUNC_P(NODE) \ + (DECL_IOBJ_MEMBER_FUNC_P (NODE) || DECL_XOBJ_MEMBER_FUNC_P (NODE)) + /* Nonzero for FUNCTION_DECL means that this decl is a member function - (static or non-static). */ + (static or object). */ #define DECL_FUNCTION_MEMBER_P(NODE) \ - (DECL_NONSTATIC_MEMBER_FUNCTION_P (NODE) || DECL_STATIC_FUNCTION_P (NODE)) + (DECL_OBJECT_MEMBER_FUNC_P (NODE) || DECL_STATIC_FUNCTION_P (NODE)) \ /* Nonzero for FUNCTION_DECL means that this member function has `this' as const X *const. */ @@ -6104,7 +6125,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) @@ -6122,6 +6145,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. @@ -6264,11 +6288,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, @@ -6285,6 +6311,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 16af59de696..1234544b4fb 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -10318,6 +10318,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, @@ -10327,7 +10328,6 @@ grokfndecl (tree ctype, location_t location) { tree decl; - int staticp = ctype && TREE_CODE (type) == FUNCTION_TYPE; tree t; if (location == UNKNOWN_LOCATION) @@ -10525,12 +10525,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; @@ -10610,24 +10607,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)) @@ -12998,6 +13001,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) @@ -13113,6 +13118,92 @@ 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 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 TREE_VALUE (parm_list); + }; + + 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 "? */ + "a function pointer 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 it's 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))); + /* I'm keeping it more basic for now. */ + 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; @@ -13400,6 +13491,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 @@ -14177,6 +14300,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))) @@ -14398,7 +14523,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); @@ -14733,8 +14859,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); @@ -15628,7 +15754,8 @@ 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 ((!methodp && !DECL_XOBJ_MEMBER_FUNC_P (decl)) + || DECL_STATIC_FUNCTION_P (decl)) { if (operator_code == TYPE_EXPR || operator_code == COMPONENT_REF @@ -17382,6 +17509,8 @@ start_preparsed_function (tree decl1, tree attrs, int flags) tree fntype = TREE_TYPE (decl1); if (TREE_CODE (fntype) == METHOD_TYPE) ctype = TYPE_METHOD_BASETYPE (fntype); + else if (DECL_XOBJ_MEMBER_FUNC_P (decl1)) + ctype = DECL_CONTEXT (decl1); else { ctype = DECL_FRIEND_CONTEXT (decl1); @@ -17609,7 +17738,9 @@ 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 (ctype && !doing_friend && !DECL_STATIC_FUNCTION_P (decl1)) + /* We don't need deal with 'this' or vtable for xobj member functions. */ + if (ctype && !doing_friend && + !(DECL_STATIC_FUNCTION_P (decl1) || DECL_XOBJ_MEMBER_FUNC_P (decl1))) { /* We know that this was set up by `grokclassfn'. We do not wait until `store_parm_decls', since evil parse errors may diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index 0ed69bca6fc..c9870d45706 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -1831,7 +1831,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags) if (!(flags & TFF_NO_FUNCTION_ARGUMENTS)) { - dump_parameters (pp, parmtypes, flags); + dump_parameters (pp, parmtypes, + DECL_XOBJ_MEMBER_FUNC_P (t) ? TFF_XOBJ_FUNC | flags + : flags); if (TREE_CODE (fntype) == METHOD_TYPE) { @@ -1910,6 +1912,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; @@ -3685,6 +3689,8 @@ function_category (tree fn) return _("In destructor %qD"); else if (LAMBDA_FUNCTION_P (fn)) return _("In lambda function"); + else if (DECL_XOBJ_MEMBER_FUNC_P (fn)) + return _("In explicit object member function %qD"); else return _("In member function %qD"); } diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc index a359bc6ee8d..53d678f2bed 100644 --- a/gcc/cp/lambda.cc +++ b/gcc/cp/lambda.cc @@ -405,8 +405,12 @@ build_capture_proxy (tree member, tree init) fn = lambda_function (closure); lam = CLASSTYPE_LAMBDA_EXPR (closure); - /* The proxy variable forwards to the capture field. */ - object = build_fold_indirect_ref (DECL_ARGUMENTS (fn)); + object = DECL_ARGUMENTS (fn); + /* Making this conditional prevents the ICE, but does not actually work + correctly. */ + if (INDIRECT_TYPE_P (TREE_TYPE (object))) + /* The proxy variable forwards to the capture field. */ + 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/module.cc b/gcc/cp/module.cc index c1c8c226bc1..8723f23c4ae 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -5683,6 +5683,7 @@ 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.xobj_func); goto lds_min; case lds_decomp: /* lang_decl_decomp. */ @@ -5751,6 +5752,7 @@ 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.xobj_func); goto lds_min; case lds_decomp: /* lang_decl_decomp. */ diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 20e18365906..f2deeca40f2 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -11750,6 +11750,31 @@ 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; + /* We just need to peek here, + grokdeclarator does the rest with this so don't mutate it. */ + tree const xobj_param + = param_list && TREE_PURPOSE (param_list) == this_identifier + ? TREE_VALUE (param_list) : NULL_TREE; + + if (xobj_param + && (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 explicit object + parameter is unrelated... at least I believe so. I can think of a + paradoxical case that almost works, where a class is forward declared + and then defined deriving from a lambda that has an explicit object + parameter of that class type, but the only way that can work is with + decltype... okay yeah I think it can work for a struct defined in + block scope, but I'm leaving this as is until I can confirm my + hypothesis. */ + if (!TEMPLATE_PARM_P (non_reference (TREE_TYPE (xobj_param)))) + { + error("a lambda with captures may not have an explicit object " + "parameter of an unrelated type"); + LAMBDA_EXPR_CAPTURE_LIST (lambda_expr) = NULL_TREE; + } + } /* In the decl-specifier-seq of the lambda-declarator, each decl-specifier shall either be mutable or constexpr. */ @@ -11767,24 +11792,81 @@ 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) { - LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1; - quals = TYPE_UNQUALIFIED; + /* This might not be the most appropriate place for this, but I'm going + to hold back on changing it for the time being since we are short on + time. It's not really a parser error, it's more a semantic error. + But this is where the other errors were so... */ + if (xobj_param) + { + 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"); + /* Error reporting here is a little awkward, if the type of the + object parameter is deduced, we should tell them the lambda + is effectively already const, or to make the param const if it is + not, but if it is deduced and taken by value shouldn't we say + that it's taken by copy and won't mutate? + Seems right to me, but it's a little strange. */ + + /* An xobj parameter with an unrelated type should already have been + diagnosed, that means we definitely have a template type param. + We don't suppress these informs right now when the xobj param is + unrelated, we probably should though. */ + if (!TYPE_REF_P (TREE_TYPE (xobj_param))) + inform (DECL_SOURCE_LOCATION(xobj_param), + "a by-value explicit object parameter will never be " + "mutated"); + else if (TYPE_READONLY (TREE_TYPE (TREE_TYPE (xobj_param)))) + inform (DECL_SOURCE_LOCATION(xobj_param), + "declare the explicit object parameter without const"); + else + inform (DECL_SOURCE_LOCATION(xobj_param), + "explicit object parameter is already mutable"); + } + else + { + LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1; + quals = TYPE_UNQUALIFIED; + } } else if (lambda_specs.storage_class == sc_static) { + bool error_emitted = false; if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE || LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)) - error_at (lambda_specs.locations[ds_storage_class], - "% lambda specifier with lambda capture"); - else + { + error_at (lambda_specs.locations[ds_storage_class], + "% lambda specifier with lambda capture"); + error_emitted = true; + } + if (xobj_param) + { + 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"); + error_emitted = true; + } + if (!error_emitted) { LAMBDA_EXPR_STATIC_P (lambda_expr) = 1; quals = TYPE_UNQUALIFIED; } } + if (xobj_param) + { + quals = TYPE_UNQUALIFIED; + if (TYPE_REF_P (xobj_param) + && !(cp_type_quals (TREE_TYPE (xobj_param)) & TYPE_QUAL_CONST)) + LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1; + } tx_qual = cp_parser_tx_qualifier_opt (parser); if (omitted_parms_loc && tx_qual) @@ -11899,7 +11981,8 @@ 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 (!LAMBDA_EXPR_STATIC_P (lambda_expr) + && !DECL_XOBJ_MEMBER_FUNC_P (fco)) /* Give the object parameter a different name. */ DECL_NAME (DECL_ARGUMENTS (fco)) = closure_identifier; DECL_SET_LAMBDA_FUNCTION (fco, true); @@ -16019,6 +16102,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) { @@ -16091,6 +16176,40 @@ 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); + /* Ideally we synthesize a full rewrite, at the moment + there are issues with it though. + It rewrites "f(S this & s)" correctly, but fails + to rewrite "f(const this S s)" correctly. It also + does not handle "f(S& this s)" correctly at all. */ + 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; + } + /* Assume we will find a decl-specifier keyword. */ found_decl_spec = true; /* If the next token is an appropriate keyword, we can simply @@ -25421,12 +25540,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 @@ -25495,6 +25616,25 @@ 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)) + { + 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; + } + /* 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 @@ -33873,7 +34013,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/pt.cc b/gcc/cp/pt.cc index 86c95b278ba..9a8ebb4e5d7 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -26740,8 +26740,26 @@ instantiate_body (tree pattern, tree args, tree d, bool nested_p) tf_warning_or_error, d); else { - tsubst_stmt (DECL_SAVED_TREE (code_pattern), args, - tf_warning_or_error, DECL_TI_TEMPLATE (d)); + bool error_emitted = false; + if (LAMBDA_FUNCTION_P (d) && DECL_XOBJ_MEMBER_FUNC_P (d)) + { + tree lambda_expr = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (d)); + if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE + || LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)) + { + gcc_assert (TREE_VEC_LENGTH (args) > 0); + tree object_arg = TREE_VEC_ELT (args, 0); + tree lambda = DECL_CONTEXT (d); + if (!same_or_base_type_p (lambda, non_reference (object_arg))) + { + error ("call to lambda with captures object type must be the lambda or derived from the lambda"); + error_emitted = true; + } + } + } + if (!error_emitted) + tsubst_stmt (DECL_SAVED_TREE (code_pattern), args, + tf_warning_or_error, DECL_TI_TEMPLATE (d)); /* Set the current input_location to the end of the function so that finish_function knows where we are. */ diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc index cd80f285ac9..08c22bcd5d8 100644 --- a/gcc/cp/search.cc +++ b/gcc/cp/search.cc @@ -2212,11 +2212,14 @@ 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_FUNC_P (fndecl)) { tree btypes = TYPE_ARG_TYPES (TREE_TYPE (fn)); tree dtypes = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); - if (compparms (TREE_CHAIN (btypes), dtypes)) + if (compparms (TREE_CHAIN (btypes), + DECL_XOBJ_MEMBER_FUNC_P (fndecl) + ? TREE_CHAIN (dtypes) : dtypes)) return fn; } else if (same_signature_p (fndecl, fn)) @@ -2243,6 +2246,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_FUNC_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 52044be7af8..512b2f45950 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -45,6 +45,7 @@ along with GCC; see the file COPYING3. If not see #include "gomp-constants.h" #include "predict.h" #include "memmodel.h" +#include "gcc-rich-location.h" /* There routines provide a modular interface to perform many parsing operations. They may therefore be used during actual parsing, or @@ -3085,7 +3086,39 @@ finish_this_expr (void) return rvalue (result); tree fn = current_nonlambda_function (); - if (fn && DECL_STATIC_FUNCTION_P (fn)) + if (fn && DECL_XOBJ_MEMBER_FUNC_P (fn)) + { + auto_diagnostic_group d; + error ("% is unavailable for explicit object member " + "functions"); + /* I can imagine doing a fixit here, suggesting replacing + this / *this / this-> with &name / name / "name." but it would be + very difficult to get it perfect and I've been advised against + making imperfect fixits. + Perhaps it would be as simple as the replacements listed, + even if one is move'ing/forward'ing, if the replacement is just + done in the same place, it will be exactly what the user wants? + Even if this is true though, there's still a problem of getting the + context of the expression to find which tokens to replace. + I would really like for this to be possible though. + I will decide whether or not to persue this after review. */ + tree xobj_parm = DECL_ARGUMENTS (fn); + gcc_assert (xobj_parm); + tree parm_name = DECL_NAME (xobj_parm); + if (parm_name) + inform (DECL_SOURCE_LOCATION (xobj_parm), + "use explicit object parameter %qD instead", + parm_name); + else + { + gcc_rich_location xobj_loc (DECL_SOURCE_LOCATION (xobj_parm)); + xobj_loc.add_fixit_insert_after(" self"); + inform (DECL_SOURCE_LOCATION (xobj_parm), + "name and use the explicit object parameter instead"); + /* Maybe suggest self as a name here? */ + } + } + 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/tree.cc b/gcc/cp/tree.cc index 417c92ba76f..d861593d9e4 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -3659,7 +3659,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_FUNC_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 @@ -3677,24 +3677,25 @@ 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_FUNC_P (overload)) { - fn = overload; - if (op == ARRAY_REF) - obj = va_arg (p, tree); + tree object = va_arg (p, tree); + tree binfo = TYPE_BINFO (TREE_TYPE (object)); + tree method = build_baselink (binfo, binfo, overload, NULL_TREE); + fn = build_min (COMPONENT_REF, TREE_TYPE (overload), + object, method, NULL_TREE); for (int i = 0; i < nargs; i++) { tree arg = va_arg (p, tree); vec_safe_push (args, arg); } } - else if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE) + else if (TREE_CODE (TREE_TYPE (overload)) == FUNCTION_TYPE) { - tree object = va_arg (p, tree); - tree binfo = TYPE_BINFO (TREE_TYPE (object)); - tree method = build_baselink (binfo, binfo, overload, NULL_TREE); - fn = build_min (COMPONENT_REF, TREE_TYPE (overload), - object, method, NULL_TREE); + gcc_assert (!DECL_XOBJ_MEMBER_FUNC_P (overload)); + fn = overload; + if (op == ARRAY_REF) + obj = va_arg (p, tree); for (int i = 0; i < nargs; i++) { tree arg = va_arg (p, tree); @@ -3729,7 +3730,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_FUNC_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 49afbd8fb5e..1aa6545ca4c 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -7133,6 +7133,19 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain) case BASELINK: arg = BASELINK_FUNCTIONS (arg); + if (DECL_XOBJ_MEMBER_FUNC_P (arg)) + { + /* Error should match that in + class.cc:resolve_address_of_overloaded_function. + We really should do a fixit here. */ + error_at (input_location, + "taking the address of an explicit object member " + "function must be qualified"); + inform (input_location, + "(a pointer to explicit object member function can only be " + "formed with %<&%E%>)", arg); + return error_mark_node; + } /* Fall through. */ case OVERLOAD: @@ -7164,6 +7177,16 @@ 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, + similar to the BASELINK case above. If we did the same optimization + as we do for single static member functions (passing in a BASELINK) + then it could be handled the same too. */ + if (DECL_XOBJ_MEMBER_FUNC_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 00000000000..1e44c9123b7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic1.C @@ -0,0 +1,113 @@ +// 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 00000000000..2c2b69ad362 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic2.C @@ -0,0 +1,27 @@ +// 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 00000000000..5f27d45012e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic3.C @@ -0,0 +1,495 @@ +// 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 00000000000..042ac0478e9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic4.C @@ -0,0 +1,111 @@ +// 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 +// member function matters for determining whether these are redeclarations or overloads + +// 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-by-value1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value1.C new file mode 100644 index 00000000000..e85c9ab03b0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value1.C @@ -0,0 +1,49 @@ +// 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. + +// { dg-xfail-run-if "by value explicit object parameter is not supported yet" { *-*-* } } + +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 00000000000..051439bb1df --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value2.C @@ -0,0 +1,59 @@ +// 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 + +// { dg-xfail-run-if "user defined conversions from an implicit object argument to an explicit object parameter are not supported yet" { *-*-* } } + +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 00000000000..30e556bd6cb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value3.C @@ -0,0 +1,42 @@ +// 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 + +// { dg-xfail-run-if "by value explicit object parameter is not supported yet" { *-*-* } } + +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 00000000000..d3c5e393e7b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value4.C @@ -0,0 +1,19 @@ +// 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-cxx-dialect-A.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C new file mode 100644 index 00000000000..033745d5784 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C @@ -0,0 +1,6 @@ +// 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 00000000000..4774750255b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C @@ -0,0 +1,6 @@ +// 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 00000000000..7dbdc64a302 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C @@ -0,0 +1,8 @@ +// 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 00000000000..dccb0cf07df --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C @@ -0,0 +1,7 @@ +// 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 00000000000..1924212fb23 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C @@ -0,0 +1,7 @@ +// 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 00000000000..7b94f7e9c12 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C @@ -0,0 +1,138 @@ +// 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 00000000000..7c930859dae --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C @@ -0,0 +1,25 @@ +// 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 function pointer 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-diagnostics4.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C new file mode 100644 index 00000000000..1f743a8509f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C @@ -0,0 +1,19 @@ +// 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-diagnostics5.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C new file mode 100644 index 00000000000..65a5c63f20b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C @@ -0,0 +1,15 @@ +// 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-diagnostics6.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C new file mode 100644 index 00000000000..e56d6265ea1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C @@ -0,0 +1,22 @@ +// 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 and use the explicit object parameter instead" } + this->_n = 10; // { dg-error "'this' is unavailable for explicit object member functions" } + } +}; 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 00000000000..17ba23df77e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C @@ -0,0 +1,25 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// rejection and diagnosis when taking address of an unqualified xobj member function + +struct S { + static void static_f(S&) {} + void iobj_member_f() {} + void xobj_member_f(this S&) {} + + void test() { + using func_ptr_type = void(*)(S&); + // using mem_func_ptr_type = void (S::*)(); + + // allowed (not testing for this) + // func_ptr_type static_f_ptr = &static_f; + + // not allowed (also not testing for this) + // mem_func_ptr_type iobj_mem_f_ptr = &iobj_member_f; + + // not allowed (this is what we are testing for) + func_ptr_type xobj_mem_f_ptr = &xobj_member_f; // { dg-error "taking the address of an explicit object member function must be qualified" } + } +}; + 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 00000000000..913fb3ca5ce --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C @@ -0,0 +1,11 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// lambda declaration with xobj parameter + +// { dg-excess-errors "explicit object parameter with lambdas not implemented yet" { xfail *-*-* } } + +void test() +{ + (void)[](this auto&& self){}; +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX2.C new file mode 100644 index 00000000000..e792eafb80b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX2.C @@ -0,0 +1,22 @@ +// 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-ops-mem-arrow.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-arrow.C new file mode 100644 index 00000000000..c5b2c805a2f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-arrow.C @@ -0,0 +1,27 @@ +// 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 00000000000..829c7137abc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-assignment.C @@ -0,0 +1,26 @@ +// 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 00000000000..1dfe764de83 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-call.C @@ -0,0 +1,39 @@ +// 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 00000000000..cee5f6e135c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-subscript.C @@ -0,0 +1,39 @@ +// 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 00000000000..134c7e99a29 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C @@ -0,0 +1,57 @@ +// 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 00000000000..9b7af676e4f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C @@ -0,0 +1,56 @@ +// 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 00000000000..5e0a8d993bd --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem.h @@ -0,0 +1,209 @@ +// 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 00000000000..f9a642ad483 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-mem.C @@ -0,0 +1,170 @@ +// 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 00000000000..9f9c7296157 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C @@ -0,0 +1,236 @@ +// 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 00000000000..a1e49c88750 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl.C @@ -0,0 +1,245 @@ +// 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 those 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" } +}; // { dg-bogus "" } + +// 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 00000000000..83c7756f0fd --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl2.C @@ -0,0 +1,160 @@ +// 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 + +// 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 "" } +}; -- 2.42.1