aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorwaffl3x <waffl3x@protonmail.com>2024-01-07 00:01:48 +0000
committerJason Merrill <jason@redhat.com>2024-01-09 15:59:04 -0500
commitfbc980d85149409ce62c22f48d3693113803929e (patch)
tree1337c2b1223d0be16a0ab3567643372211ceef04 /gcc/cp
parentf9fbf93dc82525a0f54a2293b7ec92d65776bf19 (diff)
downloadgcc-fbc980d85149409ce62c22f48d3693113803929e.zip
gcc-fbc980d85149409ce62c22f48d3693113803929e.tar.gz
gcc-fbc980d85149409ce62c22f48d3693113803929e.tar.bz2
c++: P0847R7 (deducing this) - initial functionality. [PR102609]
This implements the initial functionality for P0847R7. CWG2789 is implemented, but instead of "same type" for the object parameters we take correspondence into account instead. Without this alteration, the behavior here would be slightly different than the behavior with constrained member function templates, which I believe would be undesirable. There are a few outstanding issues related to xobj member functions overloading iobj member functions that are introduced by using declarations. Unfortunately, fixing this will be a little more involved and will have to be pushed back until later. Most diagnostics have been split out into another patch to improve its clarity and allow all the strictly functional changes to be more distinct. Explicit object lambdas and CWG2586 are addressed in a follow up patch. PR c++/102609 gcc/cp/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - initial functionality. * class.cc (xobj_iobj_parameters_correspond): New function, checks for corresponding object parameters between xobj and iobj member functions. (add_method): Handle object parameters of xobj member functions, use xobj_iobj_parameters_correspond. * call.cc (build_over_call): Refactor, handle xobj member functions. (cand_parms_match): Handle object parameters of xobj and iobj member functions, use xobj_iobj_parameters_correspond. * cp-tree.h (enum cp_decl_spec): Add ds_this, add comments. * decl.cc (grokfndecl): Add xobj_func_p parameter. For xobj member functions, Set xobj_flag, don't set static_function flag. (grokdeclarator): Handle xobj member functions, tell grokfndecl. (grok_op_properties): Don't error for xobj operators. * parser.cc (cp_parser_decl_specifier_seq): Handle this specifier. (cp_parser_parameter_declaration): Set default argument to "this_identifier" for xobj parameters. (set_and_check_decl_spec_loc): Add "this", add comments. * tree.cc (build_min_non_dep_op_overload): Handle xobj operators. * typeck.cc (cp_build_addr_expr_1): Handle address-of xobj member functions. gcc/testsuite/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - initial functionality. * g++.dg/cpp23/explicit-obj-basic1.C: New test. * g++.dg/cpp23/explicit-obj-basic2.C: New test. * g++.dg/cpp23/explicit-obj-basic3.C: New test. * g++.dg/cpp23/explicit-obj-basic4.C: New test. * g++.dg/cpp23/explicit-obj-basic5.C: New test. * g++.dg/cpp23/explicit-obj-by-value1.C: New test. * g++.dg/cpp23/explicit-obj-by-value2.C: New test. * g++.dg/cpp23/explicit-obj-by-value3.C: New test. * g++.dg/cpp23/explicit-obj-by-value4.C: New test. * g++.dg/cpp23/explicit-obj-constraints.C: New test. * g++.dg/cpp23/explicit-obj-constraints2.C: New test. * g++.dg/cpp23/explicit-obj-ops-mem-arrow.C: New test. * g++.dg/cpp23/explicit-obj-ops-mem-assignment.C: New test. * g++.dg/cpp23/explicit-obj-ops-mem-call.C: New test. * g++.dg/cpp23/explicit-obj-ops-mem-subscript.C: New test. * g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C: New test. * g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C: New test. * g++.dg/cpp23/explicit-obj-ops-non-mem.h: New test. * g++.dg/cpp23/explicit-obj-ops-requires-mem.C: New test. * g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C: New test. * g++.dg/cpp23/explicit-obj-redecl.C: New test. * g++.dg/cpp23/explicit-obj-redecl2.C: New test. * g++.dg/cpp23/explicit-obj-redecl3.C: New test. * g++.dg/cpp23/explicit-obj-redecl4.C: New test. Signed-off-by: Waffl3x <waffl3x@protonmail.com>
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/call.cc184
-rw-r--r--gcc/cp/class.cc244
-rw-r--r--gcc/cp/cp-tree.h7
-rw-r--r--gcc/cp/decl.cc44
-rw-r--r--gcc/cp/parser.cc22
-rw-r--r--gcc/cp/tree.cc10
-rw-r--r--gcc/cp/typeck.cc9
7 files changed, 423 insertions, 97 deletions
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index be89ff2..dca8e50 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -9870,14 +9870,9 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
const vec<tree, va_gc> *args = cand->args;
tree first_arg = cand->first_arg;
conversion **convs = cand->convs;
- conversion *conv;
tree parm = TYPE_ARG_TYPES (TREE_TYPE (fn));
int parmlen;
tree val;
- int i = 0;
- int j = 0;
- unsigned int arg_index = 0;
- int is_method = 0;
int nargs;
tree *argarray;
bool already_used = false;
@@ -10074,45 +10069,46 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
if (immediate_invocation_p (STRIP_TEMPLATE (fn)))
in_consteval_if_p = true;
+ int argarray_size = 0;
+ unsigned int arg_index = 0;
+ int conv_index = 0;
+ int param_index = 0;
+
+ auto consume_object_arg = [&arg_index, &first_arg, args]()
+ {
+ if (!first_arg)
+ return (*args)[arg_index++];
+ tree object_arg = first_arg;
+ first_arg = NULL_TREE;
+ return object_arg;
+ };
+
/* The implicit parameters to a constructor are not considered by overload
resolution, and must be of the proper type. */
if (DECL_CONSTRUCTOR_P (fn))
{
- tree object_arg;
- if (first_arg != NULL_TREE)
- {
- object_arg = first_arg;
- first_arg = NULL_TREE;
- }
- else
- {
- object_arg = (*args)[arg_index];
- ++arg_index;
- }
- argarray[j++] = build_this (object_arg);
+ tree object_arg = consume_object_arg ();
+ argarray[argarray_size++] = build_this (object_arg);
parm = TREE_CHAIN (parm);
/* We should never try to call the abstract constructor. */
gcc_assert (!DECL_HAS_IN_CHARGE_PARM_P (fn));
if (DECL_HAS_VTT_PARM_P (fn))
{
- argarray[j++] = (*args)[arg_index];
+ argarray[argarray_size++] = (*args)[arg_index];
++arg_index;
parm = TREE_CHAIN (parm);
}
}
/* Bypass access control for 'this' parameter. */
- else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
+ else if (DECL_IOBJ_MEMBER_FUNCTION_P (fn))
{
- tree arg = build_this (first_arg != NULL_TREE
- ? first_arg
- : (*args)[arg_index]);
+ tree arg = build_this (consume_object_arg ());
tree argtype = TREE_TYPE (arg);
if (arg == error_mark_node)
return error_mark_node;
-
- if (convs[i]->bad_p)
+ if (convs[conv_index++]->bad_p)
{
if (complain & tf_error)
{
@@ -10187,25 +10183,57 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
tree converted_arg = build_base_path (PLUS_EXPR, arg,
base_binfo, 1, complain);
- argarray[j++] = converted_arg;
+ argarray[argarray_size++] = converted_arg;
parm = TREE_CHAIN (parm);
- if (first_arg != NULL_TREE)
- first_arg = NULL_TREE;
+ }
+
+ auto handle_arg = [fn, flags, complain](tree type,
+ tree arg,
+ int const param_index,
+ conversion *conv,
+ bool const conversion_warning)
+ {
+ /* Set user_conv_p on the argument conversions, so rvalue/base handling
+ knows not to allow any more UDCs. This needs to happen after we
+ process cand->warnings. */
+ if (flags & LOOKUP_NO_CONVERSION)
+ conv->user_conv_p = true;
+
+ tsubst_flags_t const arg_complain
+ = conversion_warning ? complain : complain & ~tf_warning;
+
+ if (arg_complain & tf_warning)
+ maybe_warn_pessimizing_move (arg, type, /*return_p=*/false);
+
+ tree val = convert_like_with_context (conv, arg, fn,
+ param_index, arg_complain);
+ val = convert_for_arg_passing (type, val, arg_complain);
+ return val;
+ };
+
+ if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ {
+ gcc_assert (cand->num_convs > 0);
+ static constexpr bool conversion_warning = true;
+ tree object_arg = consume_object_arg ();
+ val = handle_arg (TREE_VALUE (parm),
+ object_arg,
+ param_index++,
+ convs[conv_index++],
+ conversion_warning);
+
+ if (val == error_mark_node)
+ return error_mark_node;
else
- ++arg_index;
- ++i;
- is_method = 1;
+ argarray[argarray_size++] = val;
+ parm = TREE_CHAIN (parm);
}
gcc_assert (first_arg == NULL_TREE);
for (; arg_index < vec_safe_length (args) && parm;
- parm = TREE_CHAIN (parm), ++arg_index, ++i)
+ parm = TREE_CHAIN (parm), ++arg_index, ++param_index, ++conv_index)
{
- tree type = TREE_VALUE (parm);
- tree arg = (*args)[arg_index];
- bool conversion_warning = true;
-
- conv = convs[i];
+ tree current_arg = (*args)[arg_index];
/* If the argument is NULL and used to (implicitly) instantiate a
template function (and bind one of the template arguments to
@@ -10227,47 +10255,35 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
func(NULL);
}
*/
- if (null_node_p (arg)
- && DECL_TEMPLATE_INFO (fn)
- && cand->template_decl
- && !cand->explicit_targs)
- conversion_warning = false;
-
- /* Set user_conv_p on the argument conversions, so rvalue/base handling
- knows not to allow any more UDCs. This needs to happen after we
- process cand->warnings. */
- if (flags & LOOKUP_NO_CONVERSION)
- conv->user_conv_p = true;
+ bool const conversion_warning = !(null_node_p (current_arg)
+ && DECL_TEMPLATE_INFO (fn)
+ && cand->template_decl
+ && !cand->explicit_targs);
- tsubst_flags_t arg_complain = complain;
- if (!conversion_warning)
- arg_complain &= ~tf_warning;
-
- if (arg_complain & tf_warning)
- maybe_warn_pessimizing_move (arg, type, /*return_p*/false);
-
- val = convert_like_with_context (conv, arg, fn, i - is_method,
- arg_complain);
- val = convert_for_arg_passing (type, val, arg_complain);
+ val = handle_arg (TREE_VALUE (parm),
+ current_arg,
+ param_index,
+ convs[conv_index],
+ conversion_warning);
if (val == error_mark_node)
- return error_mark_node;
+ return error_mark_node;
else
- argarray[j++] = val;
+ argarray[argarray_size++] = val;
}
/* Default arguments */
- for (; parm && parm != void_list_node; parm = TREE_CHAIN (parm), i++)
+ for (; parm && parm != void_list_node; parm = TREE_CHAIN (parm), param_index++)
{
if (TREE_VALUE (parm) == error_mark_node)
return error_mark_node;
val = convert_default_arg (TREE_VALUE (parm),
TREE_PURPOSE (parm),
- fn, i - is_method,
+ fn, param_index,
complain);
if (val == error_mark_node)
- return error_mark_node;
- argarray[j++] = val;
+ return error_mark_node;
+ argarray[argarray_size++] = val;
}
/* Ellipsis */
@@ -10304,11 +10320,11 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
a = convert_arg_to_ellipsis (a, complain);
if (a == error_mark_node)
return error_mark_node;
- argarray[j++] = a;
+ argarray[argarray_size++] = a;
}
- gcc_assert (j <= nargs);
- nargs = j;
+ gcc_assert (argarray_size <= nargs);
+ nargs = argarray_size;
icip.reset ();
/* Avoid performing argument transformation if warnings are disabled.
@@ -10324,7 +10340,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
{
tree *fargs = (!nargs ? argarray
: (tree *) alloca (nargs * sizeof (tree)));
- for (j = 0; j < nargs; j++)
+ for (int j = 0; j < nargs; j++)
{
/* For -Wformat undo the implicit passing by hidden reference
done by convert_arg_to_ellipsis. */
@@ -12697,15 +12713,37 @@ cand_parms_match (z_candidate *c1, z_candidate *c2)
}
tree parms1 = TYPE_ARG_TYPES (TREE_TYPE (fn1));
tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (fn2));
- if (DECL_FUNCTION_MEMBER_P (fn1)
- && DECL_FUNCTION_MEMBER_P (fn2)
- && (DECL_STATIC_FUNCTION_P (fn1)
- != DECL_STATIC_FUNCTION_P (fn2)))
+ auto skip_parms = [](tree fn, tree parms){
+ if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ return TREE_CHAIN (parms);
+ else
+ return skip_artificial_parms_for (fn, parms);
+ };
+ if (!(DECL_FUNCTION_MEMBER_P (fn1)
+ && DECL_FUNCTION_MEMBER_P (fn2)))
+ /* Early escape. */;
+ else if ((DECL_STATIC_FUNCTION_P (fn1)
+ != DECL_STATIC_FUNCTION_P (fn2)))
{
/* Ignore 'this' when comparing the parameters of a static member
function with those of a non-static one. */
- parms1 = skip_artificial_parms_for (fn1, parms1);
- parms2 = skip_artificial_parms_for (fn2, parms2);
+ parms1 = skip_parms (fn1, parms1);
+ parms2 = skip_parms (fn2, parms2);
+ }
+ else if ((DECL_XOBJ_MEMBER_FUNCTION_P (fn1)
+ || DECL_XOBJ_MEMBER_FUNCTION_P (fn2))
+ && (DECL_IOBJ_MEMBER_FUNCTION_P (fn1)
+ || DECL_IOBJ_MEMBER_FUNCTION_P (fn2)))
+ {
+ bool xobj_iobj_parameters_correspond (tree, tree);
+ /* CWG2789 is not adequate, it should specify corresponding object
+ parameters, not same typed object parameters. */
+ if (!xobj_iobj_parameters_correspond (fn1, fn2))
+ return false;
+ /* We just compared the object parameters, if they don't correspond
+ we already return false. */
+ parms1 = skip_parms (fn1, parms1);
+ parms2 = skip_parms (fn2, parms2);
}
return compparms (parms1, parms2);
}
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 5bfdabe..6f924d5 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -1019,6 +1019,217 @@ modify_vtable_entry (tree t,
}
+/* Check if the object parameters of an xobj and iobj member function
+ correspond. This function assumes that the iobj parameter has been correctly
+ adjusted when the function is introduced by a using declaration per
+ [over.match.funcs.general.4]. */
+
+bool
+xobj_iobj_parameters_correspond (tree fn1, tree fn2)
+{
+ gcc_assert (DECL_IOBJ_MEMBER_FUNCTION_P (fn1)
+ || DECL_IOBJ_MEMBER_FUNCTION_P (fn2));
+ gcc_assert (DECL_XOBJ_MEMBER_FUNCTION_P (fn1)
+ || DECL_XOBJ_MEMBER_FUNCTION_P (fn2));
+ gcc_assert (fn1 != fn2);
+
+ tree xobj_fn = DECL_XOBJ_MEMBER_FUNCTION_P (fn1) ? fn1 : fn2;
+ /* A reference, pointer, or something else. */
+ tree xobj_param = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (xobj_fn)));
+
+ tree iobj_fn = DECL_IOBJ_MEMBER_FUNCTION_P (fn1) ? fn1 : fn2;
+ tree iobj_fn_type = TREE_TYPE (iobj_fn);
+ /* Will work for a pointer or reference param type. So this will continue
+ to work even if we change how the object parameter of an iobj member
+ function is represented. */
+ tree iobj_param_type
+ = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (iobj_fn_type)));
+
+ /* If the iobj member function was introduced with a using declaration, the
+ type of its object parameter is considered to be that of the class it was
+ introduced into.
+
+ [over.match.funcs.general.4]
+ For non-conversion functions that are implicit object member
+ functions nominated by a using-declaration in a derived class, the
+ function is considered to be a member of the derived class for the purpose
+ of defining the type of the implicit object parameter.
+
+ Unfortunately, because of this rule, we can't just compare the xobj member
+ function's DECL_CONTEXT to its object parameter.
+
+ struct S;
+
+ struct B {
+ int f(this S&) { return 5; }
+ };
+
+ struct S : B {
+ using B::f;
+ int f() { return 10; }
+ };
+
+ The using declaration does not change the object parameter of B::f as it
+ is an xobj member function. However, its object parameter still
+ corresponds to S::f as it was declared with an object parameter of type
+ S const&. The DECL_CONTEXT of B::f is B, so if we compare the type of the
+ object parameter to that, it will not match. If we naively assume a
+ different type from the DECL_CONTEXT for an xobj parameter means that the
+ object parameters do not correspond, then the object parameters in the
+ above example will be considered non-corresponding.
+
+ As a result of this, B::f would incorrectly not be discarded, causing an
+ ambiguity when f is called on an object of type S.
+
+ This also impacts member functions with constraints as in the following
+ example.
+
+ template<typename = void>
+ struct S;
+
+ template<typename = void>
+ struct B {
+ int f(this S<>&) requires true { return 5; }
+ };
+
+ template<typename>
+ struct S : B<> {
+ using B<>::f;
+ int f() { return 10; }
+ };
+
+ Once again, if we compare the DECL_CONTEXT of B<>::f to it's xobj
+ parameter, it would not match. If the object parameters do not
+ correspond, constraints are not taken into account, so in this example we
+ would (probably) get an ambiguous lookup instead of correctly picking
+ B<>::f.
+
+ Because of this caveat, we must actually compare the type of the iobj
+ parameter to the type of the xobj parameter, shortcuts will have these
+ edge cases.
+
+ Aside from the more complex reasons above, this logic also implicitly
+ handles xobj parameters of pointer type, we don't have to explicitly
+ check for that case. */
+
+ /* FIXME:
+
+ template<typename>
+ struct S;
+
+ template<typename>
+ struct B {
+ int f(this S<void>&) requires true { return 5; }
+ };
+
+ template<typename>
+ struct S : B<void> {
+ using B<void>::f;
+ int f() { return 10; }
+ };
+
+ This case is broken, the incomplete type seems to screw with things.
+ I'm not sure how to fix that so I'm just noting the issue here, I have a
+ feeling it's trivial to do if you know how. */
+
+ if (TYPE_MAIN_VARIANT (iobj_param_type)
+ != TYPE_MAIN_VARIANT (non_reference (xobj_param)))
+ return false;
+ /* We don't get to bail yet even if we have a by-value xobj parameter,
+ a by-value xobj parameter can correspond to an iobj parameter provided the
+ iobj member function is not declared with a reference qualifier.
+
+ From this point on, we know we are dealing with an xobj parameter that has
+ an object parameter of the same type as the class it was declared in.
+ We still don't know if we have a reference or by-value parameter yet
+ though. */
+
+ cp_ref_qualifier const iobj_ref_qual = type_memfn_rqual (iobj_fn_type);
+ /* We only care about cv qualifiers when determining correspondence. */
+ static constexpr cp_cv_quals cv_bits = TYPE_QUAL_VOLATILE
+ | TYPE_QUAL_CONST;
+ cp_cv_quals const iobj_cv_quals = type_memfn_quals (iobj_fn_type) & cv_bits;
+ /* We need to ignore the ref qualifier of the xobj parameter if the iobj
+ member function lacks a ref qualifier.
+
+ [basic.scope.scope.3]
+ Two non-static member functions have corresponding object parameters if:
+ -- exactly one is an implicit object member function with no ref-qualifier
+ and the types of their object parameters ([dcl.fct]), after removing
+ top-level references, are the same, or
+ -- their object parameters have the same type.
+
+ The cv qualifiers of a by-value parameter are supposed to be discarded, so
+ we ignore them.
+
+ [dcl.fct.5]
+ After producing the list of parameter types, any top-level cv-qualifiers
+ modifying a parameter type are deleted when forming the function type.
+
+ However, they still need to be taken into account when our xobj parameter
+ is a reference that is being ignored (according to [basic.scope.scope.3]
+ quoted above), but when we are actually dealing with a by-value xobj
+ parameter we can proceed following this table.
+ | iobj | xobj | equal |
+ | none | none | X |
+ | none | c | X |
+ | none | v | X |
+ | none | cv | X |
+ | c | none | O |
+ | c | c | O |
+ | c | v | O |
+ | c | cv | O |
+ | v | none | O |
+ | v | c | O |
+ | v | v | O |
+ | v | cv | O |
+ | cv | none | O |
+ | cv | c | O |
+ | cv | v | O |
+ | cv | cv | O |
+
+ Additionally, if the iobj member function is ref qualified, we aren't
+ ignoring the ref qualifier of the iobj parameter, so we can't be dealing
+ with correspondence in that case either.
+
+ So to recap, if we have a by-value xobj parameter, we know for sure that
+ we aren't dealing with corresponding object parameters if the iobj member
+ function has any cv-ref qualifiers. The only case where we might still be
+ dealing with corresponding object parameters is when the iobj member
+ function lacks any cv-ref qualification. */
+ if (!TYPE_REF_P (xobj_param))
+ {
+ if (iobj_ref_qual || iobj_cv_quals)
+ return false;
+ }
+ else
+ {
+ /* We are dealing with an xobj parameter that is a reference now, but due
+ to [basic.scope.scope.3] we need to ignore its ref qual. */
+ cp_ref_qualifier const xobj_ref_qual = [&](){
+ if (!TYPE_REF_P (xobj_param) || !iobj_ref_qual)
+ return REF_QUAL_NONE;
+ return TYPE_REF_IS_RVALUE (xobj_param) ? REF_QUAL_RVALUE
+ : REF_QUAL_LVALUE;
+ }(); /* IILE. */
+
+ /* Even if we are ignoring the reference qualifier, the xobj parameter
+ was still a reference so we still take the cv qualifiers into
+ account. */
+ cp_cv_quals const xobj_cv_quals
+ = cp_type_quals (TREE_TYPE (xobj_param)) & cv_bits;
+
+ /* Finally, if the qualifications don't match exactly, the object
+ parameters don't correspond. */
+ if (iobj_ref_qual != xobj_ref_qual
+ || iobj_cv_quals != xobj_cv_quals)
+ return false;
+ }
+ /* If we got past everything else, the object parameters of fn1 and fn2
+ definitely correspond. */
+ return true;
+}
+
/* Add method METHOD to class TYPE. If VIA_USING indicates whether
METHOD is being injected via a using_decl. Returns true if the
method could be added to the method vec. */
@@ -1079,8 +1290,8 @@ add_method (tree type, tree method, bool via_using)
/* Compare the quals on the 'this' parm. Don't compare
the whole types, as used functions are treated as
coming from the using class in overload resolution. */
- if (! DECL_STATIC_FUNCTION_P (fn)
- && ! DECL_STATIC_FUNCTION_P (method)
+ if (DECL_IOBJ_MEMBER_FUNCTION_P (fn)
+ && DECL_IOBJ_MEMBER_FUNCTION_P (method)
/* Either both or neither need to be ref-qualified for
differing quals to allow overloading. */
&& (FUNCTION_REF_QUALIFIED (fn_type)
@@ -1089,6 +1300,35 @@ add_method (tree type, tree method, bool via_using)
|| type_memfn_rqual (fn_type) != type_memfn_rqual (method_type)))
continue;
+ /* Handle special correspondence rules for xobj vs xobj and xobj vs iobj
+ member function declarations.
+ We don't worry about static member functions here. */
+ if ((!DECL_XOBJ_MEMBER_FUNCTION_P (fn)
+ && !DECL_XOBJ_MEMBER_FUNCTION_P (method))
+ || DECL_STATIC_FUNCTION_P (fn) || DECL_STATIC_FUNCTION_P (method))
+ /* Early escape. */;
+ else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)
+ && DECL_XOBJ_MEMBER_FUNCTION_P (method))
+ {
+ auto get_object_param = [](tree fn){
+ return TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fn)));
+ };
+ /* We skip the object parameter below, check it here instead of
+ making changes to that code. */
+ tree fn_param = get_object_param (fn);
+ tree method_param = get_object_param (method);
+ if (!same_type_p (fn_param, method_param))
+ continue;
+ }
+ else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)
+ || DECL_XOBJ_MEMBER_FUNCTION_P (method))
+ {
+ if (!xobj_iobj_parameters_correspond (fn, method))
+ continue;
+ }
+ else
+ gcc_unreachable ();
+
tree real_fn = fn;
tree real_method = method;
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index f0bddb7..ce57716 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6341,11 +6341,13 @@ enum cp_storage_class {
/* An individual decl-specifier. This is used to index the array of
locations for the declspecs in struct cp_decl_specifier_seq
- below. */
+ below.
+ A subset of these enums also corresponds to elements of
+ cp_parser_set_decl_spec_type:decl_spec_names in parser.cc. */
enum cp_decl_spec {
ds_first,
- ds_signed = ds_first,
+ ds_signed = ds_first, /* Index of first element of decl_spec_names. */
ds_unsigned,
ds_short,
ds_long,
@@ -6362,6 +6364,7 @@ enum cp_decl_spec {
ds_complex,
ds_constinit,
ds_consteval,
+ ds_this, /* Index of last element of decl_spec_names. */
ds_thread,
ds_type_spec,
ds_redefined_builtin_type_spec,
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 398ec6b..16ccdb3 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -10516,6 +10516,7 @@ grokfndecl (tree ctype,
int publicp,
int inlinep,
bool deletedp,
+ bool xobj_func_p,
special_function_kind sfk,
bool funcdef_flag,
bool late_return_type_p,
@@ -10525,7 +10526,6 @@ grokfndecl (tree ctype,
location_t location)
{
tree decl;
- int staticp = ctype && TREE_CODE (type) == FUNCTION_TYPE;
tree t;
if (location == UNKNOWN_LOCATION)
@@ -10723,12 +10723,9 @@ grokfndecl (tree ctype,
(IDENTIFIER_POINTER (declarator))))))
SET_DECL_LANGUAGE (decl, lang_c);
- /* Should probably propagate const out from type to decl I bet (mrs). */
- if (staticp)
- {
- DECL_STATIC_FUNCTION_P (decl) = 1;
- DECL_CONTEXT (decl) = ctype;
- }
+ DECL_STATIC_FUNCTION_P (decl)
+ = !xobj_func_p && ctype && TREE_CODE (type) == FUNCTION_TYPE;
+ DECL_FUNCTION_XOBJ_FLAG (decl) = xobj_func_p;
if (deletedp)
DECL_DELETED_FN (decl) = 1;
@@ -13198,6 +13195,8 @@ grokdeclarator (const cp_declarator *declarator,
if (attrlist)
diagnose_misapplied_contracts (*attrlist);
+ /* Skip over build_memfn_type when a FUNCTION_DECL is an xobj memfn. */
+ bool is_xobj_member_function = false;
/* Determine the type of the entity declared by recurring on the
declarator. */
for (; declarator; declarator = declarator->declarator)
@@ -13313,6 +13312,22 @@ grokdeclarator (const cp_declarator *declarator,
if (raises == error_mark_node)
raises = NULL_TREE;
+ auto find_xobj_parm = [](tree parm_list)
+ {
+ /* There is no need to iterate over the list,
+ only the first parm can be a valid xobj parm. */
+ if (!parm_list || TREE_PURPOSE (parm_list) != this_identifier)
+ return false;
+ /* If we make it here, we are looking at an xobj parm.
+
+ Non-null 'purpose' usually means the parm has a default
+ argument, we don't want to violate this assumption. */
+ TREE_PURPOSE (parm_list) = NULL_TREE;
+ return true;
+ };
+
+ is_xobj_member_function
+ = find_xobj_parm (declarator->u.function.parameters);
if (reqs)
error_at (location_of (reqs), "requires-clause on return type");
reqs = declarator->u.function.requires_clause;
@@ -14386,6 +14401,8 @@ grokdeclarator (const cp_declarator *declarator,
}
if (ctype && TREE_CODE (type) == FUNCTION_TYPE && staticp < 2
+ /* Don't convert xobj member functions to METHOD_TYPE. */
+ && !is_xobj_member_function
&& !(unqualified_id
&& identifier_p (unqualified_id)
&& IDENTIFIER_NEWDEL_OP_P (unqualified_id)))
@@ -14607,7 +14624,8 @@ grokdeclarator (const cp_declarator *declarator,
friendp ? -1 : 0, friendp, publicp,
inlinep | (2 * constexpr_p) | (4 * concept_p)
| (8 * consteval_p),
- initialized == SD_DELETED, sfk,
+ initialized == SD_DELETED,
+ is_xobj_member_function, sfk,
funcdef_flag, late_return_type_p,
template_count, in_namespace,
attrlist, id_loc);
@@ -14942,8 +14960,8 @@ grokdeclarator (const cp_declarator *declarator,
inlinep | (2 * constexpr_p) | (4 * concept_p)
| (8 * consteval_p),
initialized == SD_DELETED,
- sfk,
- funcdef_flag,
+ is_xobj_member_function, sfk,
+ funcdef_flag,
late_return_type_p,
template_count, in_namespace, attrlist,
id_loc);
@@ -15744,7 +15762,7 @@ bool
grok_op_properties (tree decl, bool complain)
{
tree argtypes = TYPE_ARG_TYPES (TREE_TYPE (decl));
- bool methodp = TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE;
+ bool const methodp = DECL_IOBJ_MEMBER_FUNCTION_P (decl);
tree name = DECL_NAME (decl);
location_t loc = DECL_SOURCE_LOCATION (decl);
@@ -15837,7 +15855,7 @@ grok_op_properties (tree decl, bool complain)
/* An operator function must either be a non-static member function
or have at least one parameter of a class, a reference to a class,
an enumeration, or a reference to an enumeration. 13.4.0.6 */
- if (! methodp || DECL_STATIC_FUNCTION_P (decl))
+ if (!DECL_OBJECT_MEMBER_FUNCTION_P (decl))
{
if (operator_code == TYPE_EXPR
|| operator_code == COMPONENT_REF
@@ -15926,7 +15944,7 @@ grok_op_properties (tree decl, bool complain)
}
++arity;
}
-
+ /* FIXME: We need tests for these errors with xobj member functions. */
/* Verify correct number of arguments. */
switch (op_flags)
{
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 706ed21..046c48d 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -16260,6 +16260,16 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
decl_specs->locations[ds_attribute] = token->location;
continue;
}
+ /* Special case for "this" specifier, indicating a parm is an xobj parm.
+ The "this" specifier must be the first specifier in the declaration,
+ after any attributes. */
+ if (token->keyword == RID_THIS)
+ {
+ cp_lexer_consume_token (parser->lexer);
+ set_and_check_decl_spec_loc (decl_specs, ds_this, token);
+ continue;
+ }
+
/* Assume we will find a decl-specifier keyword. */
found_decl_spec = true;
/* If the next token is an appropriate keyword, we can simply
@@ -25721,6 +25731,13 @@ cp_parser_parameter_declaration (cp_parser *parser,
if (default_argument)
STRIP_ANY_LOCATION_WRAPPER (default_argument);
+ if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_this))
+ {
+ /* Xobj parameters can not have default arguments, thus
+ we can reuse the default argument field to flag the param as such. */
+ default_argument = this_identifier;
+ }
+
/* Generate a location for the parameter, ranging from the start of the
initial token to the end of the final token (using input_location for
the latter, set up by cp_lexer_set_source_position_from_token when
@@ -34091,6 +34108,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
}
else
{
+ /* These correspond to cp-tree.h:cp_decl_spec,
+ changes here should also be reflected there. */
static const char *const decl_spec_names[] = {
"signed",
"unsigned",
@@ -34108,7 +34127,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
"constexpr",
"__complex",
"constinit",
- "consteval"
+ "consteval",
+ "this"
};
gcc_rich_location richloc (location);
richloc.add_fixit_remove ();
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index f2e0f28..77f57e0 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -3677,7 +3677,7 @@ build_min_non_dep_op_overload (enum tree_code op,
nargs = call_expr_nargs (non_dep);
expected_nargs = cp_tree_code_length (op);
- if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE
+ if (DECL_OBJECT_MEMBER_FUNCTION_P (overload)
/* For ARRAY_REF, operator[] is either a non-static member or newly
static member, never out of class and for the static member case
if user uses single index the operator[] needs to have a single
@@ -3695,7 +3695,7 @@ build_min_non_dep_op_overload (enum tree_code op,
releasing_vec args;
va_start (p, overload);
- if (TREE_CODE (TREE_TYPE (overload)) == FUNCTION_TYPE)
+ if (!DECL_OBJECT_MEMBER_FUNCTION_P (overload))
{
fn = overload;
if (op == ARRAY_REF)
@@ -3706,7 +3706,7 @@ build_min_non_dep_op_overload (enum tree_code op,
vec_safe_push (args, arg);
}
}
- else if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE)
+ else
{
tree object = va_arg (p, tree);
tree binfo = TYPE_BINFO (TREE_TYPE (object));
@@ -3719,8 +3719,6 @@ build_min_non_dep_op_overload (enum tree_code op,
vec_safe_push (args, arg);
}
}
- else
- gcc_unreachable ();
va_end (p);
call = build_min_non_dep_call_vec (non_dep, fn, args);
@@ -3747,7 +3745,7 @@ build_min_non_dep_op_overload (tree non_dep, tree overload, tree object,
unsigned int nargs = call_expr_nargs (non_dep);
tree fn = overload;
- if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE)
+ if (DECL_OBJECT_MEMBER_FUNCTION_P (overload))
{
tree binfo = TYPE_BINFO (TREE_TYPE (object));
tree method = build_baselink (binfo, binfo, overload, NULL_TREE);
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index d101cf0..7e66913 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -7265,6 +7265,15 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
&& !mark_used (t, complain) && !(complain & tf_error))
return error_mark_node;
+ /* Pull out the function_decl for a single xobj member function, and
+ let the rest of this function handle it. This is similar to how
+ static member functions are handled in the BASELINK case above. */
+ if (DECL_XOBJ_MEMBER_FUNCTION_P (t))
+ {
+ arg = t;
+ break;
+ }
+
type = build_ptrmem_type (context_for_name_lookup (t),
TREE_TYPE (t));
t = make_ptrmem_cst (type, t);