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;
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)
{
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
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 */
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.
{
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. */
}
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);
}
}
\f
+/* 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. */
/* 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)
|| 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;
/* 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,
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,
int publicp,
int inlinep,
bool deletedp,
+ bool xobj_func_p,
special_function_kind sfk,
bool funcdef_flag,
bool late_return_type_p,
location_t location)
{
tree decl;
- int staticp = ctype && TREE_CODE (type) == FUNCTION_TYPE;
tree t;
if (location == UNKNOWN_LOCATION)
(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;
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)
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;
}
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)))
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);
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);
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);
/* 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
}
++arity;
}
-
+ /* FIXME: We need tests for these errors with xobj member functions. */
/* Verify correct number of arguments. */
switch (op_flags)
{
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
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
}
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",
"constexpr",
"__complex",
"constinit",
- "consteval"
+ "consteval",
+ "this"
};
gcc_rich_location richloc (location);
richloc.add_fixit_remove ();
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
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)
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));
vec_safe_push (args, arg);
}
}
- else
- gcc_unreachable ();
va_end (p);
call = build_min_non_dep_call_vec (non_dep, fn, args);
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);
&& !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);
--- /dev/null
+// 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<typename Self>
+ 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<typename Self>
+ 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<typename Self>
+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<typename Self>
+ 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<typename Self>
+ 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<typename Self>
+auto S3::d0(this Self&&) -> void {}
+auto S3::d1(this auto&&) -> void {}
+
+template<typename T>
+void call_with_qualification()
+{
+ T obj{};
+ // by value should take any qualification (f0)
+ T{}.f0();
+ obj.f0();
+ static_cast<T&&>(obj).f0();
+ static_cast<T const&>(obj).f0();
+ static_cast<T const&&>(obj).f0();
+ // specific qualification (f1 - f4)
+ T{}.f2();
+ T{}.f3();
+ T{}.f4();
+ obj.f1();
+ obj.f3();
+ static_cast<T&&>(obj).f2();
+ static_cast<T&&>(obj).f3();
+ static_cast<T&&>(obj).f4();
+ static_cast<T const&>(obj).f3();
+ static_cast<T const&&>(obj).f4();
+ // deduced should (obviously) take any qualification (d0, d1)
+ T{}.d0();
+ obj.d0();
+ static_cast<T&&>(obj).d0();
+ static_cast<T const&>(obj).d0();
+ static_cast<T const&&>(obj).d0();
+ T{}.d1();
+ obj.d1();
+ static_cast<T&&>(obj).d1();
+ static_cast<T const&>(obj).d1();
+ static_cast<T const&&>(obj).d1();
+}
+
+void perform_calls()
+{
+ call_with_qualification<S0>();
+ call_with_qualification<S1>();
+ call_with_qualification<S2>();
+ call_with_qualification<S3>();
+}
+
--- /dev/null
+// 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 ();
+}
+
--- /dev/null
+// 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&&;
+};
+
--- /dev/null
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// valid overloading of iobj member functions without ref qualifiers
+// with xobj member functions (and vice-versa)
+
+// this is the most you can mix these, it may look short but it does test
+// all allowed cases (other than by-value and unrelated types)
+
+// [over.match.funcs.general.4]
+// For implicit object member functions, the type of the implicit
+// object parameter is
+// -- “lvalue reference to cv X” for functions declared
+// without a ref-qualifier or with the & ref-qualifier
+// -- “rvalue reference to cv X” for functions declared with
+// the && ref-qualifier
+
+// [basic.scope.scope.3]
+// Two non-static member functions have corresponding object
+// parameters if:
+// -- exactly one is an implicit object member function with no
+// ref-qualifier and the types of their object parameters
+// ([dcl.fct]), after removing top-level references, are the
+// same, or
+
+// in simpler terms, only the cv qualification of the explicit/implicit object
+// parameter matter for determining whether these are redeclarations or overloads
+// (when a ref qualifier is not present on the iobj member function)
+
+// xobj first, iobj last
+
+struct S0 {
+ void f(this S0 &);
+ void f(this S0 &&);
+ void f() const;
+ void f() volatile;
+ void f() const volatile;
+
+ void fc(this S0 const&);
+ void fc(this S0 const&&);
+ void fc();
+ void fc() volatile;
+ void fc() const volatile;
+
+ void fv(this S0 volatile&);
+ void fv(this S0 volatile&&);
+ void fv();
+ void fv() const;
+ void fv() const volatile;
+
+ void fcv(this S0 const volatile&);
+ void fcv(this S0 const volatile&&);
+ void fcv();
+ void fcv() const;
+ void fcv() volatile;
+};
+
+// iobj first, xobj last
+
+struct S1 {
+ void f() const;
+ void f() volatile;
+ void f() const volatile;
+ void f(this S1 &);
+ void f(this S1 &&);
+
+ void fc();
+ void fc() volatile;
+ void fc() const volatile;
+ void fc(this S1 const&);
+ void fc(this S1 const&&);
+
+ void fv();
+ void fv() const;
+ void fv() const volatile;
+ void fv(this S1 volatile&);
+ void fv(this S1 volatile&&);
+
+ void fcv();
+ void fcv() const;
+ void fcv() volatile;
+ void fcv(this S1 const volatile&);
+ void fcv(this S1 const volatile&&);
+};
+
+// in order
+
+struct S2 {
+ void f(this S2 &);
+ void f(this S2 &&);
+ void f() const;
+ void f() volatile;
+ void f() const volatile;
+
+ void fc();
+ void fc(this S2 const&);
+ void fc(this S2 const&&);
+ void fc() volatile;
+ void fc() const volatile;
+
+ void fv();
+ void fv() const;
+ void fv(this S2 volatile&);
+ void fv(this S2 volatile&&);
+ void fv() const volatile;
+
+ void fcv();
+ void fcv() const;
+ void fcv() volatile;
+ void fcv(this S2 const volatile&);
+ void fcv(this S2 const volatile&&);
+};
+
--- /dev/null
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// conversion operators with xobj parameter
+
+inline constexpr int magic = 42;
+
+struct S0 {
+ operator int(this S0 const&) {
+ return magic;
+ }
+};
+
+struct S1 {
+ int _v;
+ int f(this int self) {
+ return self;
+ }
+ operator int(this S1 const& self) {
+ return self._v;
+ }
+};
+
+int main()
+{
+ if (S0{} != magic)
+ __builtin_abort ();
+
+ S1 s{42};
+ if (static_cast<int>(s) != magic)
+ __builtin_abort ();
+}
+
--- /dev/null
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// conversion of the implicit object argument to an xobj parameter
+// when calling by value xobj member functions
+
+// The initial implementation of xobj member functions incorrectly did not
+// convert the implicit object argument when binding to the xobj
+// parameter. In spite of this, it did correctly check to see if such a
+// conversion would be valid, thus no diagnostic would be emitted when a
+// conversion was valid, but instead of applying the conversion, the
+// argument would silently be reinterpreted as the type of the parameter.
+
+// This is why we use uintptr_t for the value in S and compare the result
+// of f to &s, we want to test for simple reinterpretation of the
+// argument. To accurately test for this we make sure to use an object
+// that has a different address than the value of our magic number. It's
+// an impossibly improbable edge case but it's trivial to work around. We
+// still compare against both the address of s and the magic number so we
+// can additionally test for bugged conversions, while also
+// differentiating that case from reinterpretation of the argument.
+
+using uintptr_t = __UINTPTR_TYPE__;
+inline constexpr uintptr_t magic = 42;
+
+struct S {
+ uintptr_t _v;
+ uintptr_t f(this S self) {
+ return self._v;
+ }
+};
+
+int main()
+{
+ S s0{magic};
+ S s1{magic};
+ // prevent (absurdly improbable) bogus failures
+ S& s = magic != (uintptr_t)(&s0) ? s0 : s1;
+
+ uintptr_t const ret = s.f();
+ // check for reinterpretation of the object argument
+ if (ret == (uintptr_t)(&s))
+ __builtin_abort ();
+ // check for a bugged conversion
+ if (ret != magic)
+ __builtin_abort ();
+}
+
--- /dev/null
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// conversion of the implicit object argument to an xobj parameter
+// using a user defined conversion or converting constructor
+// when calling by value xobj member functions
+
+// see explicit-obj-by-value1.C for details on this test
+
+using uintptr_t = __UINTPTR_TYPE__;
+inline constexpr uintptr_t magic = 42;
+
+struct S;
+
+struct FromS {
+ uintptr_t _v;
+ FromS(S);
+};
+
+struct S {
+ operator uintptr_t() const {
+ return magic;
+ }
+ uintptr_t f(this uintptr_t n) {
+ return n;
+ }
+ uintptr_t g(this FromS from_s) {
+ return from_s._v;
+ }
+};
+
+FromS::FromS(S) : _v(magic) {}
+
+
+int main()
+{
+ S s0{};
+ S s1{};
+ // prevent (absurdly improbable) bogus failures
+ S& s = magic != (uintptr_t)(&s0) ? s0 : s1;
+
+ uintptr_t const ret0 = s.f();
+ // check for reinterpretation of the object argument
+ if (ret0 == (uintptr_t)(&s))
+ __builtin_abort ();
+ // check for a bugged conversion
+ if (ret0 != magic)
+ __builtin_abort ();
+
+ uintptr_t const ret1 = s.g();
+ // check for reinterpretation of the object argument
+ if (ret1 == (uintptr_t)(&s))
+ __builtin_abort ();
+ // check for a bugged conversion
+ if (ret1 != magic)
+ __builtin_abort ();
+}
+
--- /dev/null
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// correct constructor selection when initializing a by value xobj parameter
+
+// see explicit-obj-by-value1.C for details on this test
+
+using uintptr_t = __UINTPTR_TYPE__;
+inline constexpr uintptr_t magic = 42;
+inline constexpr uintptr_t copy_magic = 5;
+inline constexpr uintptr_t move_magic = 10;
+
+struct S {
+ uintptr_t _v;
+ explicit S(uintptr_t v) : _v(v) {}
+ S(S const& other) : _v(other._v + copy_magic) {}
+ S(S&& other) : _v(other._v + move_magic) {}
+ uintptr_t f(this S self) {
+ return self._v;
+ }
+};
+
+int main()
+{
+ S s0{magic};
+ S s1{magic};
+ // prevent (absurdly improbable (^2)) bogus results
+ // it's virtually impossible for both to have a bogus result,
+ // but we can guarantee correct results from both easily, so why not?
+ S& s_copy_from = magic + copy_magic != (uintptr_t)(&s0) ? s0 : s1;
+ S& s_move_from = magic + move_magic != (uintptr_t)(&s0) ? s0 : s1;
+ uintptr_t const copy_ret = static_cast<S const&>(s_copy_from).f();
+ uintptr_t const move_ret = static_cast<S&&>(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 ();
+}
+
--- /dev/null
+// 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'} }
+}
+
--- /dev/null
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// overload resolution of static/xobj and iobj/xobj member functions
+// with constraints
+
+template<typename T>
+concept Constrain = true;
+
+inline constexpr int iobj_fn = 5;
+inline constexpr int xobj_fn = 10;
+inline constexpr int static_fn = 20;
+
+// first 2 letters are the order of the definitions
+// the constraint applies to the first definition,
+// for *_r cases the constraint applies to the second
+
+struct S {
+ // xobj/static
+ int f_xs_v(this S, Constrain auto) { return xobj_fn; };
+ static int f_xs_v(auto) { return static_fn; };
+
+ int f_xs_ref(this S&, Constrain auto) { return xobj_fn; };
+ static int f_xs_ref(auto) { return static_fn; };
+
+ int f_xs_cref(this S const&, Constrain auto) { return xobj_fn; };
+ static int f_xs_cref(auto) { return static_fn; };
+
+ int f_xs_rref(this S&&, Constrain auto) { return xobj_fn; };
+ static int f_xs_rref(auto) { return static_fn; };
+
+ int f_xs_crref(this S const&&, Constrain auto) { return xobj_fn; };
+ static int f_xs_crref(auto) { return static_fn; };
+
+ // _r
+ int f_xs_v_r(this S, auto) { return xobj_fn; };
+ static int f_xs_v_r(Constrain auto) { return static_fn; };
+
+ int f_xs_ref_r(this S&, auto) { return xobj_fn; };
+ static int f_xs_ref_r(Constrain auto) { return static_fn; };
+
+ int f_xs_cref_r(this S const&, auto) { return xobj_fn; };
+ static int f_xs_cref_r(Constrain auto) { return static_fn; };
+
+ int f_xs_rref_r(this S&&, auto) { return xobj_fn; };
+ static int f_xs_rref_r(Constrain auto) { return static_fn; };
+
+ int f_xs_crref_r(this S const&&, auto) { return xobj_fn; };
+ static int f_xs_crref_r(Constrain auto) { return static_fn; };
+
+ // static/xobj
+ static int f_sx_v(Constrain auto) { return static_fn; };
+ int f_sx_v(this S, auto) { return xobj_fn; };
+
+ static int f_sx_ref(Constrain auto) { return static_fn; };
+ int f_sx_ref(this S&, auto) { return xobj_fn; };
+
+ static int f_sx_cref(Constrain auto) { return static_fn; };
+ int f_sx_cref(this S const&, auto) { return xobj_fn; };
+
+ static int f_sx_rref(Constrain auto) { return static_fn; };
+ int f_sx_rref(this S&&, auto) { return xobj_fn; };
+
+ static int f_sx_crref(Constrain auto) { return static_fn; };
+ int f_sx_crref(this S const&&, auto) { return xobj_fn; };
+
+ // _r
+ static int f_sx_v_r(auto) { return static_fn; };
+ int f_sx_v_r(this S, Constrain auto) { return xobj_fn; };
+
+ static int f_sx_ref_r(auto) { return static_fn; };
+ int f_sx_ref_r(this S&, Constrain auto) { return xobj_fn; };
+
+ static int f_sx_cref_r(auto) { return static_fn; };
+ int f_sx_cref_r(this S const&, Constrain auto) { return xobj_fn; };
+
+ static int f_sx_rref_r(auto) { return static_fn; };
+ int f_sx_rref_r(this S&&, Constrain auto) { return xobj_fn; };
+
+ static int f_sx_crref_r(auto) { return static_fn; };
+ int f_sx_crref_r(this S const&&, Constrain auto) { return xobj_fn; };
+
+ // xobj/iobj with matching object parameters
+
+ // We are only testing constraints here, so we need parameter lists
+ // to match, which means we need corresponding object parameters.
+ // Remember, the rules for object parameter correspondence are weird.
+ // ([basic.scope.scope-3.1])
+
+ // *_refqual the iobj member function has a reference qualifier
+ // *_r the constraint applies to the second definition
+
+ // ix
+ int f_ix_m0(Constrain auto) { return iobj_fn; };
+ int f_ix_m0(this S&, auto) { return xobj_fn; };
+ // See note
+ int f_ix_m1(Constrain auto) { return iobj_fn; };
+ int f_ix_m1(this S&&, auto) { return xobj_fn; };
+
+ int f_ix_c0(Constrain auto) const { return iobj_fn; };
+ int f_ix_c0(this S const&, auto) { return xobj_fn; };
+ // See note
+ int f_ix_c1(Constrain auto) const { return iobj_fn; };
+ int f_ix_c1(this S const&&, auto) { return xobj_fn; };
+
+ // xi
+ int f_xi_m0(this S&, Constrain auto) { return xobj_fn; };
+ int f_xi_m0(auto) { return iobj_fn; };
+ // See note
+ int f_xi_m1(this S&&, Constrain auto) { return xobj_fn; };
+ int f_xi_m1(auto) { return iobj_fn; };
+
+ int f_xi_c0(this S const&, Constrain auto) { return xobj_fn; };
+ int f_xi_c0(auto) const { return iobj_fn; };
+ // See note
+ int f_xi_c1(this S const&&, Constrain auto) { return xobj_fn; };
+ int f_xi_c1(auto) const { return iobj_fn; };
+
+ // with ref qualifier
+
+ // ix
+ int f_ix_m0_refqual(Constrain auto) & { return iobj_fn; };
+ int f_ix_m0_refqual(this S&, auto) { return xobj_fn; };
+
+ int f_ix_m1_refqual(Constrain auto) && { return iobj_fn; };
+ int f_ix_m1_refqual(this S&&, auto) { return xobj_fn; };
+
+ int f_ix_c0_refqual(Constrain auto) const& { return iobj_fn; };
+ int f_ix_c0_refqual(this S const&, auto) { return xobj_fn; };
+
+ int f_ix_c1_refqual(Constrain auto) const&& { return iobj_fn; };
+ int f_ix_c1_refqual(this S const&&, auto) { return xobj_fn; };
+
+ // xi
+ int f_xi_m0_refqual(this S&, Constrain auto) { return xobj_fn; };
+ int f_xi_m0_refqual(auto) & { return iobj_fn; };
+
+ int f_xi_m1_refqual(this S&&, Constrain auto) { return xobj_fn; };
+ int f_xi_m1_refqual(auto) && { return iobj_fn; };
+
+ int f_xi_c0_refqual(this S const&, Constrain auto) { return xobj_fn; };
+ int f_xi_c0_refqual(auto) const& { return iobj_fn; };
+
+ int f_xi_c1_refqual(this S const&&, Constrain auto) { return xobj_fn; };
+ int f_xi_c1_refqual(auto) const&& { return iobj_fn; };
+
+ // _r without ref qualifier
+
+ // ix
+ int f_ix_m0_r(auto) { return iobj_fn; };
+ int f_ix_m0_r(this S&, Constrain auto) { return xobj_fn; };
+ // See note
+ int f_ix_m1_r(auto) { return iobj_fn; };
+ int f_ix_m1_r(this S&&, Constrain auto) { return xobj_fn; };
+
+ int f_ix_c0_r(auto) const { return iobj_fn; };
+ int f_ix_c0_r(this S const&, Constrain auto) { return xobj_fn; };
+ // See note
+ int f_ix_c1_r(auto) const { return iobj_fn; };
+ int f_ix_c1_r(this S const&&, Constrain auto) { return xobj_fn; };
+
+ // xi
+ int f_xi_m0_r(this S&, auto) { return xobj_fn; };
+ int f_xi_m0_r(Constrain auto) { return iobj_fn; };
+ // See note
+ int f_xi_m1_r(this S&&, auto) { return xobj_fn; };
+ int f_xi_m1_r(Constrain auto) { return iobj_fn; };
+
+ int f_xi_c0_r(this S const&, auto) { return xobj_fn; };
+ int f_xi_c0_r(Constrain auto) const { return iobj_fn; };
+ // See note
+ int f_xi_c1_r(this S const&&, auto) { return xobj_fn; };
+ int f_xi_c1_r(Constrain auto) const { return iobj_fn; };
+
+ // _r with ref qualifier
+ // ix
+ int f_ix_m0_refqual_r(auto) & { return iobj_fn; };
+ int f_ix_m0_refqual_r(this S&, Constrain auto) { return xobj_fn; };
+
+ int f_ix_m1_refqual_r(auto) && { return iobj_fn; };
+ int f_ix_m1_refqual_r(this S&&, Constrain auto) { return xobj_fn; };
+
+ int f_ix_c0_refqual_r(auto) const& { return iobj_fn; };
+ int f_ix_c0_refqual_r(this S const&, Constrain auto) { return xobj_fn; };
+
+ int f_ix_c1_refqual_r(auto) const&& { return iobj_fn; };
+ int f_ix_c1_refqual_r(this S const&&, Constrain auto) { return xobj_fn; };
+
+ // xi
+ int f_xi_m0_refqual_r(this S&, auto) { return xobj_fn; };
+ int f_xi_m0_refqual_r(Constrain auto) & { return iobj_fn; };
+
+ int f_xi_m1_refqual_r(this S&&, auto) { return xobj_fn; };
+ int f_xi_m1_refqual_r(Constrain auto) && { return iobj_fn; };
+
+ int f_xi_c0_refqual_r(this S const&, auto) { return xobj_fn; };
+ int f_xi_c0_refqual_r(Constrain auto) const& { return iobj_fn; };
+
+ int f_xi_c1_refqual_r(this S const&&, auto) { return xobj_fn; };
+ int f_xi_c1_refqual_r(Constrain auto) const&& { return iobj_fn; };
+};
+
+
+int main()
+{
+ // The commented out cases are ambiguous, which is most likely the correct
+ // behavior. It is something that I want to propose to change, and I want
+ // to leave them in as they are a little weird.
+ //
+ // Furthermore, as the comment at the top of this file indicates, I am not
+ // clear on the correct behavior of the static/xobj cases in general.
+
+ S s{};
+ if (s.f_xs_v (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_xs_ref (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_xs_cref (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xs_rref (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xs_crref (0) != xobj_fn)
+ __builtin_abort ();
+ // if (s.f_xs_dv (0) != xobj_fn)
+ // __builtin_abort ();
+ // if (s.f_xs_dcref (0) != xobj_fn)
+ // __builtin_abort ();
+ // if (s.f_xs_dfwdref (0) != xobj_fn)
+ // __builtin_abort ();
+
+ if (s.f_xs_v_r (0) != static_fn)
+ __builtin_abort ();
+ if (s.f_xs_ref_r (0) != static_fn)
+ __builtin_abort ();
+ if (s.f_xs_cref_r (0) != static_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xs_rref_r (0) != static_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xs_crref_r (0) != static_fn)
+ __builtin_abort ();
+ // if (s.f_xs_dv_r (0) != static_fn)
+ // __builtin_abort ();
+ // if (s.f_xs_dcref_r (0) != static_fn)
+ // __builtin_abort ();
+ // if (s.f_xs_dfwdref_r (0) != static_fn)
+ // __builtin_abort ();
+
+ if (s.f_sx_v (0) != static_fn)
+ __builtin_abort ();
+ if (s.f_sx_ref (0) != static_fn)
+ __builtin_abort ();
+ if (s.f_sx_cref (0) != static_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_sx_rref (0) != static_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_sx_crref (0) != static_fn)
+ __builtin_abort ();
+ // if (s.f_sx_dv (0) != static_fn)
+ // __builtin_abort ();
+ // if (s.f_sx_dcref (0) != static_fn)
+ // __builtin_abort ();
+ // if (s.f_sx_dfwdref (0) != static_fn)
+ // __builtin_abort ();
+
+ if (s.f_sx_v_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_sx_ref_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_sx_cref_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_sx_rref_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_sx_crref_r (0) != xobj_fn)
+ __builtin_abort ();
+ // if (s.f_sx_dv_r (0) != xobj_fn)
+ // __builtin_abort ();
+ // if (s.f_sx_dcref_r (0) != xobj_fn)
+ // __builtin_abort ();
+ // if (s.f_sx_dfwdref_r (0) != xobj_fn)
+ // __builtin_abort ();
+
+ // iobj/xobj
+
+ // The commented out cases are tested below as their correct behavior is
+ // unintuitive, see the note below for details.
+
+ if (s.f_ix_m0 (0) != iobj_fn)
+ __builtin_abort ();
+ // s.f_ix_m1
+ if (s.f_ix_c0 (0) != iobj_fn)
+ __builtin_abort ();
+ // s.f_ix_c1
+ if (s.f_xi_m0 (0) != xobj_fn)
+ __builtin_abort ();
+ // s.f_xi_m1
+ if (s.f_xi_c0 (0) != xobj_fn)
+ __builtin_abort ();
+ // s.f_xi_c1
+ if (s.f_ix_m0_refqual (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_m1_refqual (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_c0_refqual (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_c1_refqual (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_m0_refqual (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_m1_refqual (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_c0_refqual (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_c1_refqual (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_m0_r (0) != xobj_fn)
+ __builtin_abort ();
+ // s.f_ix_m1_r
+ if (s.f_ix_c0_r (0) != xobj_fn)
+ __builtin_abort ();
+ // s.f_ix_c1_r
+ if (s.f_xi_m0_r (0) != iobj_fn)
+ __builtin_abort ();
+ // s.f_xi_m1_r
+ if (s.f_xi_c0_r (0) != iobj_fn)
+ __builtin_abort ();
+ // s.f_xi_c1_r
+ if (s.f_ix_m0_refqual_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_m1_refqual_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_c0_refqual_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_c1_refqual_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_m0_refqual_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_m1_refqual_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_c0_refqual_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_c1_refqual_r (0) != iobj_fn)
+ __builtin_abort ();
+
+
+/* Note:
+ These cases are weird, the object argument correspond, but are not the same
+ type ([basic.scope.scope-3.1]), so we get this funny edge case where the
+ constraint stops them from being considered redeclarations, but isn't taken
+ into account for the lvalue case. You can't bind an lvalue to an rvalue
+ reference so the iobj member function is always taken regardless of which
+ overload is constrained.
+
+ [over.match.funcs.general-4]
+ For implicit object member functions, the type of the implicit object
+ parameter is
+ (4.1) “lvalue reference to cv X” for functions declared without a
+ ref-qualifier or with the & ref-qualifier
+
+ You would think that calling these functions with an rvalue would be the
+ same then, always taking the xobj member function. However, for backwards
+ compatibility reasons, an unqualified member function can be called on an
+ object that is an rvalue.
+
+ [over.match.funcs.general-5]
+ For implicit object member functions declared without a ref-qualifier, even
+ if the implicit object parameter is not const-qualified, an rvalue can be
+ bound to the parameter as long as in all other respects the argument can be
+ converted to the type of the implicit object parameter.
+
+ And finally, since the object parameters correspond ([basic.scope.scope-3.1])
+ the constraints are taken into account.
+
+ So in conclusion, calling these functions with an lvalue always resolves to
+ the iobj member function, and calling them with rvalues take the constraints
+ into account.
+
+ As wacky as this is, this is the correct behavior. */
+
+ // Always takes the iobj member function, can't bind an lvalue to an rvalue
+ // reference.
+ if (s.f_ix_m1 (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_c1 (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_m1 (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_c1 (0) != iobj_fn)
+ __builtin_abort ();
+
+ if (s.f_ix_m1_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_c1_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_m1_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_c1_r (0) != iobj_fn)
+ __builtin_abort ();
+
+ // Constraints are taken into account here, see note for more information.
+ if (static_cast<S&&>(s).f_ix_m1 (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_c1 (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_m1 (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_c1 (0) != xobj_fn)
+ __builtin_abort ();
+
+ if (static_cast<S&&>(s).f_ix_m1_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_c1_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_m1_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_c1_r (0) != iobj_fn)
+ __builtin_abort ();
+}
+
--- /dev/null
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// overload resolution of static/xobj and iobj/xobj non-template member functions
+// with constraints in a class template
+
+template<typename T>
+concept Constrain = true;
+
+inline constexpr int iobj_fn = 5;
+inline constexpr int xobj_fn = 10;
+inline constexpr int static_fn = 20;
+
+// first 2 letters are the order of the definitions
+// the constraint applies to the first definition,
+// for *_r cases the constraint applies to the second
+
+template<typename T>
+struct S {
+ // xobj/static
+ int f_xs_v(this S) requires Constrain<T> { return xobj_fn; };
+ static int f_xs_v() { return static_fn; };
+
+ int f_xs_ref(this S&) requires Constrain<T> { return xobj_fn; };
+ static int f_xs_ref() { return static_fn; };
+
+ int f_xs_cref(this S const&) requires Constrain<T> { return xobj_fn; };
+ static int f_xs_cref() { return static_fn; };
+
+ int f_xs_rref(this S&&) requires Constrain<T> { return xobj_fn; };
+ static int f_xs_rref() { return static_fn; };
+
+ int f_xs_crref(this S const&&) requires Constrain<T> { return xobj_fn; };
+ static int f_xs_crref() { return static_fn; };
+
+ int f_xs_dv(this auto) requires Constrain<T> { return xobj_fn; };
+ static int f_xs_dv() { return static_fn; };
+
+ int f_xs_dcref(this auto const&) requires Constrain<T> { return xobj_fn; };
+ static int f_xs_dcref() { return static_fn; };
+
+ int f_xs_dfwdref(this auto&&) requires Constrain<T> { return xobj_fn; };
+ static int f_xs_dfwdref() { return static_fn; };
+
+ // _r
+ int f_xs_v_r(this S) { return xobj_fn; };
+ static int f_xs_v_r() requires Constrain<T> { return static_fn; };
+
+ int f_xs_ref_r(this S&) { return xobj_fn; };
+ static int f_xs_ref_r() requires Constrain<T> { return static_fn; };
+
+ int f_xs_cref_r(this S const&) { return xobj_fn; };
+ static int f_xs_cref_r() requires Constrain<T> { return static_fn; };
+
+ int f_xs_rref_r(this S&&) { return xobj_fn; };
+ static int f_xs_rref_r() requires Constrain<T> { return static_fn; };
+
+ int f_xs_crref_r(this S const&&) { return xobj_fn; };
+ static int f_xs_crref_r() requires Constrain<T> { return static_fn; };
+
+ int f_xs_dv_r(this auto) { return xobj_fn; };
+ static int f_xs_dv_r() requires Constrain<T> { return static_fn; };
+
+ int f_xs_dcref_r(this auto const&) { return xobj_fn; };
+ static int f_xs_dcref_r() requires Constrain<T> { return static_fn; };
+
+ int f_xs_dfwdref_r(this auto&&) { return xobj_fn; };
+ static int f_xs_dfwdref_r() requires Constrain<T> { return static_fn; };
+
+ // static/xobj
+ static int f_sx_v() requires Constrain<T> { return static_fn; };
+ int f_sx_v(this S) { return xobj_fn; };
+
+ static int f_sx_ref() requires Constrain<T> { return static_fn; };
+ int f_sx_ref(this S&) { return xobj_fn; };
+
+ static int f_sx_cref() requires Constrain<T> { return static_fn; };
+ int f_sx_cref(this S const&) { return xobj_fn; };
+
+ static int f_sx_rref() requires Constrain<T> { return static_fn; };
+ int f_sx_rref(this S&&) { return xobj_fn; };
+
+ static int f_sx_crref() requires Constrain<T> { return static_fn; };
+ int f_sx_crref(this S const&&) { return xobj_fn; };
+
+ static int f_sx_dv() requires Constrain<T> { return static_fn; };
+ int f_sx_dv(this auto) { return xobj_fn; };
+
+ static int f_sx_dcref() requires Constrain<T> { return static_fn; };
+ int f_sx_dcref(this auto const&) { return xobj_fn; };
+
+ static int f_sx_dfwdref() requires Constrain<T> { return static_fn; };
+ int f_sx_dfwdref(this auto&&) { return xobj_fn; };
+
+ // _r
+ static int f_sx_v_r() { return static_fn; };
+ int f_sx_v_r(this S) requires Constrain<T> { return xobj_fn; };
+
+ static int f_sx_ref_r() { return static_fn; };
+ int f_sx_ref_r(this S&) requires Constrain<T> { return xobj_fn; };
+
+ static int f_sx_cref_r() { return static_fn; };
+ int f_sx_cref_r(this S const&) requires Constrain<T> { return xobj_fn; };
+
+ static int f_sx_rref_r() { return static_fn; };
+ int f_sx_rref_r(this S&&) requires Constrain<T> { return xobj_fn; };
+
+ static int f_sx_crref_r() { return static_fn; };
+ int f_sx_crref_r(this S const&&) requires Constrain<T> { return xobj_fn; };
+
+ static int f_sx_dv_r() { return static_fn; };
+ int f_sx_dv_r(this auto) requires Constrain<T> { return xobj_fn; };
+
+ static int f_sx_dcref_r() { return static_fn; };
+ int f_sx_dcref_r(this auto const&) requires Constrain<T> { return xobj_fn; };
+
+ static int f_sx_dfwdref_r() { return static_fn; };
+ int f_sx_dfwdref_r(this auto&&) requires Constrain<T> { return xobj_fn; };
+
+ // xobj/iobj with matching object parameters
+
+ // We are only testing constraints here, so we need parameter lists
+ // to match, which means we need corresponding object parameters.
+ // Remember, the rules for object parameter correspondence are weird.
+ // ([basic.scope.scope-3.1])
+ //
+ // NOTE: CWG2789 does not specify this properly, I am implementing it
+ // assuming the above correspondence rules
+
+ // *_refqual the iobj member function has a reference qualifier
+ // *_r the constraint applies to the second definition
+
+ // ix
+ int f_ix_m0() requires Constrain<T> { return iobj_fn; };
+ int f_ix_m0(this S&) { return xobj_fn; };
+ // See note
+ int f_ix_m1() requires Constrain<T> { return iobj_fn; };
+ int f_ix_m1(this S&&) { return xobj_fn; };
+
+ int f_ix_c0() const requires Constrain<T> { return iobj_fn; };
+ int f_ix_c0(this S const&) { return xobj_fn; };
+ // See note
+ int f_ix_c1() const requires Constrain<T> { return iobj_fn; };
+ int f_ix_c1(this S const&&) { return xobj_fn; };
+
+ // xi
+ int f_xi_m0(this S&) requires Constrain<T> { return xobj_fn; };
+ int f_xi_m0() { return iobj_fn; };
+ // See note
+ int f_xi_m1(this S&&) requires Constrain<T> { return xobj_fn; };
+ int f_xi_m1() { return iobj_fn; };
+
+ int f_xi_c0(this S const&) requires Constrain<T> { return xobj_fn; };
+ int f_xi_c0() const { return iobj_fn; };
+ // See note
+ int f_xi_c1(this S const&&) requires Constrain<T> { return xobj_fn; };
+ int f_xi_c1() const { return iobj_fn; };
+
+ // with ref qualifier
+
+ // ix
+ int f_ix_m0_refqual() & requires Constrain<T> { return iobj_fn; };
+ int f_ix_m0_refqual(this S&) { return xobj_fn; };
+
+ int f_ix_m1_refqual() && requires Constrain<T> { return iobj_fn; };
+ int f_ix_m1_refqual(this S&&) { return xobj_fn; };
+
+ int f_ix_c0_refqual() const& requires Constrain<T> { return iobj_fn; };
+ int f_ix_c0_refqual(this S const&) { return xobj_fn; };
+
+ int f_ix_c1_refqual() const&& requires Constrain<T> { return iobj_fn; };
+ int f_ix_c1_refqual(this S const&&) { return xobj_fn; };
+
+ // xi
+ int f_xi_m0_refqual(this S&) requires Constrain<T> { return xobj_fn; };
+ int f_xi_m0_refqual() & { return iobj_fn; };
+
+ int f_xi_m1_refqual(this S&&) requires Constrain<T> { return xobj_fn; };
+ int f_xi_m1_refqual() && { return iobj_fn; };
+
+ int f_xi_c0_refqual(this S const&) requires Constrain<T> { return xobj_fn; };
+ int f_xi_c0_refqual() const& { return iobj_fn; };
+
+ int f_xi_c1_refqual(this S const&&) requires Constrain<T> { return xobj_fn; };
+ int f_xi_c1_refqual() const&& { return iobj_fn; };
+
+ // _r without ref qualifier
+
+ // ix
+ int f_ix_m0_r() { return iobj_fn; };
+ int f_ix_m0_r(this S&) requires Constrain<T> { return xobj_fn; };
+ // See note
+ int f_ix_m1_r() { return iobj_fn; };
+ int f_ix_m1_r(this S&&) requires Constrain<T> { return xobj_fn; };
+
+ int f_ix_c0_r() const { return iobj_fn; };
+ int f_ix_c0_r(this S const&) requires Constrain<T> { return xobj_fn; };
+ // See note
+ int f_ix_c1_r() const { return iobj_fn; };
+ int f_ix_c1_r(this S const&&) requires Constrain<T> { return xobj_fn; };
+
+ // xi
+ int f_xi_m0_r(this S&) { return xobj_fn; };
+ int f_xi_m0_r() requires Constrain<T> { return iobj_fn; };
+ // See note
+ int f_xi_m1_r(this S&&) { return xobj_fn; };
+ int f_xi_m1_r() requires Constrain<T> { return iobj_fn; };
+
+ int f_xi_c0_r(this S const&) { return xobj_fn; };
+ int f_xi_c0_r() const requires Constrain<T> { return iobj_fn; };
+ // See note
+ int f_xi_c1_r(this S const&&) { return xobj_fn; };
+ int f_xi_c1_r() const requires Constrain<T> { return iobj_fn; };
+
+ // _r with ref qualifier
+ // ix
+ int f_ix_m0_refqual_r() & { return iobj_fn; };
+ int f_ix_m0_refqual_r(this S&) requires Constrain<T> { return xobj_fn; };
+
+ int f_ix_m1_refqual_r() && { return iobj_fn; };
+ int f_ix_m1_refqual_r(this S&&) requires Constrain<T> { return xobj_fn; };
+
+ int f_ix_c0_refqual_r() const& { return iobj_fn; };
+ int f_ix_c0_refqual_r(this S const&) requires Constrain<T> { return xobj_fn; };
+
+ int f_ix_c1_refqual_r() const&& { return iobj_fn; };
+ int f_ix_c1_refqual_r(this S const&&) requires Constrain<T> { return xobj_fn; };
+
+ // xi
+ int f_xi_m0_refqual_r(this S&) { return xobj_fn; };
+ int f_xi_m0_refqual_r() & requires Constrain<T> { return iobj_fn; };
+
+ int f_xi_m1_refqual_r(this S&&) { return xobj_fn; };
+ int f_xi_m1_refqual_r() && requires Constrain<T> { return iobj_fn; };
+
+ int f_xi_c0_refqual_r(this S const&) { return xobj_fn; };
+ int f_xi_c0_refqual_r() const& requires Constrain<T> { return iobj_fn; };
+
+ int f_xi_c1_refqual_r(this S const&&) { return xobj_fn; };
+ int f_xi_c1_refqual_r() const&& requires Constrain<T> { return iobj_fn; };
+};
+
+int main()
+{
+ // The commented out cases are ambiguous, which is most likely the correct
+ // behavior. It is something that I want to propose to change, and I want
+ // to leave them in as they are a little weird.
+ //
+ // Furthermore, as the comment at the top of this file indicates, I am not
+ // clear on the correct behavior of the static/xobj cases in general.
+ using S = S<int>;
+ S s{};
+ if (s.f_xs_v () != xobj_fn)
+ __builtin_abort ();
+ if (s.f_xs_ref () != xobj_fn)
+ __builtin_abort ();
+ if (s.f_xs_cref () != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xs_rref () != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xs_crref () != xobj_fn)
+ __builtin_abort ();
+ // if (s.f_xs_dv () != xobj_fn)
+ // __builtin_abort ();
+ // if (s.f_xs_dcref () != xobj_fn)
+ // __builtin_abort ();
+ // if (s.f_xs_dfwdref () != xobj_fn)
+ // __builtin_abort ();
+
+ if (s.f_xs_v_r () != static_fn)
+ __builtin_abort ();
+ if (s.f_xs_ref_r () != static_fn)
+ __builtin_abort ();
+ if (s.f_xs_cref_r () != static_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xs_rref_r () != static_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xs_crref_r () != static_fn)
+ __builtin_abort ();
+ // if (s.f_xs_dv_r () != static_fn)
+ // __builtin_abort ();
+ // if (s.f_xs_dcref_r () != static_fn)
+ // __builtin_abort ();
+ // if (s.f_xs_dfwdref_r () != static_fn)
+ // __builtin_abort ();
+
+ if (s.f_sx_v () != static_fn)
+ __builtin_abort ();
+ if (s.f_sx_ref () != static_fn)
+ __builtin_abort ();
+ if (s.f_sx_cref () != static_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_sx_rref () != static_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_sx_crref () != static_fn)
+ __builtin_abort ();
+ // if (s.f_sx_dv () != static_fn)
+ // __builtin_abort ();
+ // if (s.f_sx_dcref () != static_fn)
+ // __builtin_abort ();
+ // if (s.f_sx_dfwdref () != static_fn)
+ // __builtin_abort ();
+
+ if (s.f_sx_v_r () != xobj_fn)
+ __builtin_abort ();
+ if (s.f_sx_ref_r () != xobj_fn)
+ __builtin_abort ();
+ if (s.f_sx_cref_r () != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_sx_rref_r () != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_sx_crref_r () != xobj_fn)
+ __builtin_abort ();
+ // if (s.f_sx_dv_r () != xobj_fn)
+ // __builtin_abort ();
+ // if (s.f_sx_dcref_r () != xobj_fn)
+ // __builtin_abort ();
+ // if (s.f_sx_dfwdref_r () != xobj_fn)
+ // __builtin_abort ();
+
+ // iobj/xobj
+
+ // The commented out cases are tested below as their correct behavior is
+ // unintuitive, see the note below for details.
+
+ if (s.f_ix_m0 () != iobj_fn)
+ __builtin_abort ();
+ // s.f_ix_m1
+ if (s.f_ix_c0 () != iobj_fn)
+ __builtin_abort ();
+ // s.f_ix_c1
+ if (s.f_xi_m0 () != xobj_fn)
+ __builtin_abort ();
+ // s.f_xi_m1
+ if (s.f_xi_c0 () != xobj_fn)
+ __builtin_abort ();
+ // s.f_xi_c1
+ if (s.f_ix_m0_refqual () != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_m1_refqual () != iobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_c0_refqual () != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_c1_refqual () != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_m0_refqual () != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_m1_refqual () != xobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_c0_refqual () != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_c1_refqual () != xobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_m0_r () != xobj_fn)
+ __builtin_abort ();
+ // s.f_ix_m1_r
+ if (s.f_ix_c0_r () != xobj_fn)
+ __builtin_abort ();
+ // s.f_ix_c1_r
+ if (s.f_xi_m0_r () != iobj_fn)
+ __builtin_abort ();
+ // s.f_xi_m1_r
+ if (s.f_xi_c0_r () != iobj_fn)
+ __builtin_abort ();
+ // s.f_xi_c1_r
+ if (s.f_ix_m0_refqual_r () != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_m1_refqual_r () != xobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_c0_refqual_r () != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_c1_refqual_r () != xobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_m0_refqual_r () != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_m1_refqual_r () != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_c0_refqual_r () != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_c1_refqual_r () != iobj_fn)
+ __builtin_abort ();
+
+
+/* Foreword: CWG2789 does not specifiy this correctly, it needs to be changed
+ to consider correspondence instead of "same type" or else the following
+ does not make sense. My implementation assumes correspondence should be
+ considered.
+
+ Note:
+ These cases are weird, the object argument correspond, but are not the same
+ type ([basic.scope.scope-3.1]), so we get this funny edge case where the
+ constraint stops them from being considered redeclarations, but isn't taken
+ into account for the lvalue case. You can't bind an lvalue to an rvalue
+ reference so the iobj member function is always taken regardless of which
+ overload is constrained.
+
+ [over.match.funcs.general-4]
+ For implicit object member functions, the type of the implicit object
+ parameter is
+ (4.1) “lvalue reference to cv X” for functions declared without a
+ ref-qualifier or with the & ref-qualifier
+
+ You would think that calling these functions with an rvalue would be the
+ same then, always taking the xobj member function. However, for backwards
+ compatibility reasons, an unqualified member function can be called on an
+ object that is an rvalue.
+
+ [over.match.funcs.general-5]
+ For implicit object member functions declared without a ref-qualifier, even
+ if the implicit object parameter is not const-qualified, an rvalue can be
+ bound to the parameter as long as in all other respects the argument can be
+ converted to the type of the implicit object parameter.
+
+ And finally, since the object parameters correspond ([basic.scope.scope-3.1])
+ the constraints are taken into account.
+
+ So in conclusion, calling these functions with an lvalue always resolves to
+ the iobj member function, and calling them with rvalues take the constraints
+ into account.
+
+ As wacky as this is, this is the correct behavior. */
+
+ // Always takes the iobj member function, can't bind an lvalue to an rvalue
+ // reference.
+ if (s.f_ix_m1 () != iobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_c1 () != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_m1 () != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_c1 () != iobj_fn)
+ __builtin_abort ();
+
+ if (s.f_ix_m1_r () != iobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_c1_r () != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_m1_r () != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_c1_r () != iobj_fn)
+ __builtin_abort ();
+
+ // Constraints are taken into account here, see note for more information.
+ if (static_cast<S&&>(s).f_ix_m1 () != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_c1 () != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_m1 () != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_c1 () != xobj_fn)
+ __builtin_abort ();
+
+ if (static_cast<S&&>(s).f_ix_m1_r () != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_c1_r () != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_m1_r () != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_c1_r () != iobj_fn)
+ __builtin_abort ();
+}
+
--- /dev/null
+// 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<typename = void>
+void dependent()
+{
+ S s{};
+ (void)s->_v;
+}
+
+void call()
+{
+ dependent();
+}
+
--- /dev/null
+// 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<typename = void>
+void dependent()
+{
+ S s{};
+ s = 0;
+}
+
+void call()
+{
+ dependent();
+}
+
--- /dev/null
+// 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<typename... Args>
+ void operator()(this S&, Args... args) {}
+};
+
+void non_dep()
+{
+ S s{};
+ s();
+ s(0);
+ s(0, 0);
+ s(0, 0, 0);
+}
+
+template<typename = void>
+void dependent()
+{
+ S s{};
+ s();
+ s(0);
+ s(0, 0);
+ s(0, 0, 0);
+}
+
+void call()
+{
+ dependent();
+}
+
--- /dev/null
+// 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<typename... Args>
+ void operator[](this S&, Args... args) {}
+};
+
+void non_dep()
+{
+ S s{};
+ s[];
+ s[0];
+ s[0, 0];
+ s[0, 0, 0];
+}
+
+template<typename = void>
+void dependent()
+{
+ S s{};
+ s[];
+ s[0];
+ s[0, 0];
+ s[0, 0, 0];
+}
+
+void call()
+{
+ dependent();
+}
+
--- /dev/null
+// 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<typename T = void>
+void do_calls()
+{
+ Value value{};
+ TEST_OPS(value)
+ TEST_OPS(static_cast<Value&&>(value))
+ TEST_OPS(static_cast<Value const&>(value))
+ TEST_OPS(static_cast<Value const&&>(value))
+
+ LRef l_ref{};
+ TEST_OPS(l_ref)
+ TEST_INVALID(static_cast<LRef&&>(l_ref))
+ TEST_INVALID(static_cast<LRef const&>(l_ref))
+ TEST_INVALID(static_cast<LRef const&&>(l_ref))
+
+ RRef r_ref{};
+ TEST_INVALID(r_ref)
+ TEST_OPS(static_cast<RRef&&>(r_ref))
+ TEST_INVALID(static_cast<RRef const&>(r_ref))
+ TEST_INVALID(static_cast<RRef const&&>(r_ref))
+
+ ConstLRef const_l_ref{};
+ TEST_OPS(const_l_ref)
+ TEST_OPS(static_cast<ConstLRef&&>(const_l_ref))
+ TEST_OPS(static_cast<ConstLRef const&>(const_l_ref))
+ TEST_OPS(static_cast<ConstLRef const&&>(const_l_ref))
+
+ ConstRRef const_r_ref{};
+ TEST_INVALID(const_r_ref)
+ TEST_OPS(static_cast<ConstRRef&&>(const_r_ref))
+ TEST_INVALID(static_cast<ConstRRef const&>(const_r_ref))
+ TEST_OPS(static_cast<ConstRRef const&&>(const_r_ref))
+
+ Deduced deduced{};
+ TEST_OPS(deduced)
+ TEST_OPS(static_cast<Deduced&&>(deduced))
+ TEST_OPS(static_cast<Deduced const&>(deduced))
+ TEST_OPS(static_cast<Deduced const&&>(deduced))
+
+ VALIDATE_RETURN_TYPES(deduced, Deduced&)
+ VALIDATE_RETURN_TYPES(static_cast<Deduced&&>(deduced), Deduced&&)
+ VALIDATE_RETURN_TYPES(static_cast<Deduced const&>(deduced), Deduced const&)
+ VALIDATE_RETURN_TYPES(static_cast<Deduced const&&>(deduced), Deduced const&&)
+}
+
--- /dev/null
+// 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&&>(value))
+ TEST_OPS(static_cast<Value const&>(value))
+ TEST_OPS(static_cast<Value const&&>(value))
+
+ LRef l_ref{};
+ TEST_OPS(l_ref)
+ TEST_INVALID(static_cast<LRef&&>(l_ref))
+ TEST_INVALID(static_cast<LRef const&>(l_ref))
+ TEST_INVALID(static_cast<LRef const&&>(l_ref))
+
+ RRef r_ref{};
+ TEST_INVALID(r_ref)
+ TEST_OPS(static_cast<RRef&&>(r_ref))
+ TEST_INVALID(static_cast<RRef const&>(r_ref))
+ TEST_INVALID(static_cast<RRef const&&>(r_ref))
+
+ ConstLRef const_l_ref{};
+ TEST_OPS(const_l_ref)
+ TEST_OPS(static_cast<ConstLRef&&>(const_l_ref))
+ TEST_OPS(static_cast<ConstLRef const&>(const_l_ref))
+ TEST_OPS(static_cast<ConstLRef const&&>(const_l_ref))
+
+ ConstRRef const_r_ref{};
+ TEST_INVALID(const_r_ref)
+ TEST_OPS(static_cast<ConstRRef&&>(const_r_ref))
+ TEST_INVALID(static_cast<ConstRRef const&>(const_r_ref))
+ TEST_OPS(static_cast<ConstRRef const&&>(const_r_ref))
+
+ Deduced deduced{};
+ TEST_OPS(deduced)
+ TEST_OPS(static_cast<Deduced&&>(deduced))
+ TEST_OPS(static_cast<Deduced const&>(deduced))
+ TEST_OPS(static_cast<Deduced const&&>(deduced))
+
+ VALIDATE_RETURN_TYPES(deduced, Deduced&)
+ VALIDATE_RETURN_TYPES(static_cast<Deduced&&>(deduced), Deduced&&)
+ VALIDATE_RETURN_TYPES(static_cast<Deduced const&>(deduced), Deduced const&)
+ VALIDATE_RETURN_TYPES(static_cast<Deduced const&&>(deduced), Deduced const&&)
+}
+
--- /dev/null
+// 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<typename Self> Self&& operator+=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator-=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator*=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator/=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator%=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator&=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator|=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator^=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator<<=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator>>=(this Self&& self, int) { return static_cast<Self&&>(self); }
+
+ template<typename Self> Self&& operator++(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator--(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator++(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator--(this Self&& self, int) { return static_cast<Self&&>(self); }
+
+ template<typename Self> Self&& operator+(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator-(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator+(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator-(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator*(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator/(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator%(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator&(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator|(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator^(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator<<(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator>>(this Self&& self, int) { return static_cast<Self&&>(self); }
+
+ template<typename Self> Self&& operator!(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator&&(this Self&& self, int const&) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator||(this Self&& self, int const&) { return static_cast<Self&&>(self); }
+
+ template<typename Self> Self&& operator==(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator!=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator<(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator>(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator<=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator>=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator<=>(this Self&& self, int) { return static_cast<Self&&>(self); }
+
+ template<typename Self> Self&& operator*(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator->*(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator&(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator,(this Self&& self, int) { return static_cast<Self&&>(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)));
+
--- /dev/null
+// 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<RRef&&>(self); }
+ RRef&& operator()(this RRef&& self) { return static_cast<RRef&&>(self); }
+ RRef&& operator[](this RRef&& self) { return static_cast<RRef&&>(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<ConstRRef const&&>(self); }
+ ConstRRef const&& operator()(this ConstRRef const&& self) { return static_cast<ConstRRef const&&>(self); }
+ ConstRRef const&& operator[](this ConstRRef const&& self) { return static_cast<ConstRRef const&&>(self); }
+ ConstRRef const* operator->(this ConstRRef const&& self) { return &self; }
+};
+
+// needed to implement deduced operator->
+template<typename T> struct remove_ref { using type = T; };
+template<typename T> struct remove_ref<T&> { using type = T; };
+template<typename T> struct remove_ref<T&&> { using type = T; };
+template<typename T> using remove_ref_t = typename remove_ref<T>::type;
+
+struct Deduced {
+ int _v;
+ template<typename Self>
+ Self&& operator=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self>
+ Self&& operator()(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self>
+ Self&& operator[](this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self>
+ remove_ref_t<Self>* 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<typename T, typename U>
+concept same_as = __is_same(T, U);
+
+#define TEST_VALID_WITH_RETURN_TYPES(OPERAND, CORRECT_TYPE) \
+ static_assert(requires{ {(OPERAND) = 0} -> same_as<CORRECT_TYPE>; },"Unexpected failure with return type check calling operator = with " #OPERAND " -> expected return type: " #CORRECT_TYPE); \
+ static_assert(requires{ {(OPERAND)()} -> same_as<CORRECT_TYPE>; }, "Unexpected failure with return type check calling operator () with " #OPERAND " -> expected return type: " #CORRECT_TYPE); \
+ static_assert(requires{ {(OPERAND)[]} -> same_as<CORRECT_TYPE>; }, "Unexpected failure with return type check calling operator [] with " #OPERAND " -> expected return type: " #CORRECT_TYPE);
+
+
+template<typename DepValue = Value>
+void test_value()
+{
+ DepValue value{};
+ TEST_VALID(value)
+ TEST_VALID(static_cast<DepValue&&>(value))
+ TEST_VALID(static_cast<DepValue const&>(value))
+ TEST_VALID(static_cast<DepValue const&&>(value))
+}
+
+template<typename DepLRef = LRef>
+void test_l_ref()
+{
+ DepLRef l_ref{};
+ TEST_VALID(l_ref)
+ TEST_INVALID(static_cast<DepLRef&&>(l_ref))
+ TEST_INVALID(static_cast<DepLRef const&>(l_ref))
+ TEST_INVALID(static_cast<DepLRef const&&>(l_ref))
+}
+
+template<typename DepRRef = RRef>
+void test_r_ref()
+{
+ DepRRef r_ref{};
+ TEST_INVALID(r_ref)
+ TEST_VALID(static_cast<DepRRef&&>(r_ref))
+ TEST_INVALID(static_cast<DepRRef const&>(r_ref))
+ TEST_INVALID(static_cast<DepRRef const&&>(r_ref))
+}
+
+template<typename DepConstLRef = ConstLRef>
+void test_const_l_ref()
+{
+ DepConstLRef const_l_ref{};
+ TEST_VALID(const_l_ref)
+ TEST_VALID(static_cast<DepConstLRef&&>(const_l_ref))
+ TEST_VALID(static_cast<DepConstLRef const&>(const_l_ref))
+ TEST_VALID(static_cast<DepConstLRef const&&>(const_l_ref))
+}
+
+template<typename DepConstRRef = ConstRRef>
+void test_const_r_ref()
+{
+ DepConstRRef const_r_ref{};
+ TEST_INVALID(const_r_ref)
+ TEST_VALID(static_cast<DepConstRRef&&>(const_r_ref))
+ TEST_INVALID(static_cast<DepConstRRef const&>(const_r_ref))
+ TEST_VALID(static_cast<DepConstRRef const&&>(const_r_ref))
+}
+
+template<typename DepDeduced = Deduced>
+void test_deduced()
+{
+ DepDeduced deduced{};
+
+ TEST_VALID(deduced)
+ TEST_VALID(static_cast<DepDeduced&&>(deduced))
+ TEST_VALID(static_cast<DepDeduced const&>(deduced))
+ TEST_VALID(static_cast<DepDeduced const&&>(deduced))
+
+ TEST_VALID_WITH_RETURN_TYPES(deduced, DepDeduced&)
+ TEST_VALID_WITH_RETURN_TYPES(static_cast<DepDeduced&&>(deduced), DepDeduced&&)
+ TEST_VALID_WITH_RETURN_TYPES(static_cast<DepDeduced const&>(deduced), DepDeduced const&)
+ TEST_VALID_WITH_RETURN_TYPES(static_cast<DepDeduced const&&>(deduced), DepDeduced const&&)
+ // arrow operator needs to be seperate to check the type of _v
+ static_assert(requires{ {(deduced->_v)} -> same_as<int&>; }, "Unexpected failure with return type check calling operator -> with deduced->_v");
+ static_assert(requires{ {(static_cast<DepDeduced&&>(deduced)->_v)} -> same_as<int&>; }, "Unexpected failure with return type check calling operator -> with static_cast<DepDeduced&&>(deduced)->_v");
+ static_assert(requires{ {(static_cast<DepDeduced const&>(deduced)->_v)} -> same_as<int const&>; }, "Unexpected failure with return type check calling operator -> with static_cast<DepDeduced const&>(deduced)->_v");
+ static_assert(requires{ {(static_cast<DepDeduced const&&>(deduced)->_v)} -> same_as<int const&>; }, "Unexpected failure with return type check calling operator -> with static_cast<DepDeduced const&&>(deduced)->_v");
+}
+
+void test()
+{
+ test_value();
+ test_l_ref();
+ test_r_ref();
+ test_const_l_ref();
+ test_const_r_ref();
+ test_deduced();
+}
+
--- /dev/null
+// 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<typename T, typename U>
+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<CORRECT_TYPE>; }, \
+ "Unexpected success calling operator unary& with " #OPERAND); \
+ static_assert(!requires{ {(OPERAND), 0} -> same_as<CORRECT_TYPE>; }, \
+ "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<CORRECT_TYPE>; }, \
+ "Unexpected failure calling operator unary& with " #OPERAND); \
+ static_assert(requires{ {(OPERAND), 0} -> same_as<CORRECT_TYPE>; }, \
+ "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<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) -= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) *= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) /= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) %= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) &= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) |= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) ^= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) <<= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) >>= 0} -> same_as<CORRECT_TYPE>; }); \
+ \
+ static_assert(requires{ {++(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {--(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND)++} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND)--} -> same_as<CORRECT_TYPE>; }); \
+ \
+ static_assert(requires{ {+(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {-(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) + 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) - 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) * 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) / 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) % 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) & 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) | 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) ^ 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) << 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) >> 0} -> same_as<CORRECT_TYPE>; }); \
+ \
+ static_assert(requires{ {!(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) && 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) || 0} -> same_as<CORRECT_TYPE>; }); \
+ \
+ static_assert(requires{ {(OPERAND) == 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) != 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) < 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) > 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) <= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) >= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) <=> 0} -> same_as<CORRECT_TYPE>; }); \
+ \
+ static_assert(requires{ {*(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) ->* 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {&(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND), 0} -> same_as<CORRECT_TYPE>; });
+
+template<typename DepValue = Value>
+void test_value()
+{
+ DepValue value{};
+ TEST_VALID(value, DepValue)
+ TEST_VALID(static_cast<DepValue&&>(value), DepValue)
+ TEST_VALID(static_cast<DepValue const&>(value), DepValue)
+ TEST_VALID(static_cast<DepValue const&&>(value), DepValue)
+}
+
+template<typename DepLRef = LRef>
+void test_l_ref()
+{
+ DepLRef l_ref{};
+ TEST_VALID(l_ref, DepLRef&)
+ TEST_INVALID(static_cast<DepLRef&&>(l_ref), DepLRef&)
+ TEST_INVALID(static_cast<DepLRef const&>(l_ref), DepLRef&)
+ TEST_INVALID(static_cast<DepLRef const&&>(l_ref), DepLRef&)
+}
+
+template<typename DepRRef = RRef>
+void test_r_ref()
+{
+ DepRRef r_ref{};
+ TEST_INVALID(r_ref, DepRRef&&)
+ TEST_VALID(static_cast<DepRRef&&>(r_ref), DepRRef&&)
+ TEST_INVALID(static_cast<DepRRef const&>(r_ref), DepRRef&&)
+ TEST_INVALID(static_cast<DepRRef const&&>(r_ref), DepRRef&&)
+}
+
+template<typename DepConstLRef = ConstLRef>
+void test_const_l_ref()
+{
+ DepConstLRef const_l_ref{};
+ TEST_VALID(const_l_ref, DepConstLRef const&)
+ TEST_VALID(static_cast<DepConstLRef&&>(const_l_ref), DepConstLRef const&)
+ TEST_VALID(static_cast<DepConstLRef const&>(const_l_ref), DepConstLRef const&)
+ TEST_VALID(static_cast<DepConstLRef const&&>(const_l_ref), DepConstLRef const&)
+}
+
+template<typename DepConstRRef = ConstRRef>
+void test_const_r_ref()
+{
+ DepConstRRef const_r_ref{};
+ TEST_INVALID(const_r_ref, DepConstRRef const&&)
+ TEST_VALID(static_cast<DepConstRRef&&>(const_r_ref), DepConstRRef const&&)
+ TEST_INVALID(static_cast<DepConstRRef const&>(const_r_ref), DepConstRRef const&&)
+ TEST_VALID(static_cast<DepConstRRef const&&>(const_r_ref), DepConstRRef const&&)
+}
+
+template<typename DepDeduced = Deduced>
+void test_deduced()
+{
+ DepDeduced deduced{};
+
+ TEST_VALID_WITH_RETURN_TYPES(deduced, DepDeduced&)
+ TEST_VALID_WITH_RETURN_TYPES(static_cast<DepDeduced&&>(deduced), DepDeduced&&)
+ TEST_VALID_WITH_RETURN_TYPES(static_cast<DepDeduced const&>(deduced), DepDeduced const&)
+ TEST_VALID_WITH_RETURN_TYPES(static_cast<DepDeduced const&&>(deduced), DepDeduced const&&)
+}
+
+void test()
+{
+ test_value();
+ test_l_ref();
+ test_r_ref();
+ test_const_l_ref();
+ test_const_r_ref();
+ test_deduced();
+}
+
--- /dev/null
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// redeclarations of xobj/iobj member functions where the iobj member function
+// is not ref qualified
+
+// it does not make sense to check for the inverse in this test (7 iobj, 1 xobj)
+// because you are not allowed to overload iobj member functions without ref qualifiers
+// with iobj member functions that do (and vice versa)
+
+// iobj first
+
+struct S0 {
+ void f0(); // { dg-note "previous declaration" }
+ void f0(this S0 &&); // { dg-error "cannot be overloaded with" }
+ void f0(this S0 const&); // { dg-bogus "" }
+ void f0(this S0 const&&); // { dg-bogus "" }
+ void f0(this S0 volatile&); // { dg-bogus "" }
+ void f0(this S0 volatile&&); // { dg-bogus "" }
+ void f0(this S0 const volatile&); // { dg-bogus "" }
+ void f0(this S0 const volatile&&); // { dg-bogus "" }
+
+ void f1(); // { dg-note "previous declaration" }
+ void f1(this S0 &); // { dg-error "cannot be overloaded with" }
+ void f1(this S0 const&); // { dg-bogus "" }
+ void f1(this S0 const&&); // { dg-bogus "" }
+ void f1(this S0 volatile&); // { dg-bogus "" }
+ void f1(this S0 volatile&&); // { dg-bogus "" }
+ void f1(this S0 const volatile&); // { dg-bogus "" }
+ void f1(this S0 const volatile&&); // { dg-bogus "" }
+
+ void fc0() const; // { dg-note "previous declaration" }
+ void fc0(this S0 &); // { dg-bogus "" }
+ void fc0(this S0 &&); // { dg-bogus "" }
+ void fc0(this S0 const&&); // { dg-error "cannot be overloaded with" }
+ void fc0(this S0 volatile&); // { dg-bogus "" }
+ void fc0(this S0 volatile&&); // { dg-bogus "" }
+ void fc0(this S0 const volatile&); // { dg-bogus "" }
+ void fc0(this S0 const volatile&&); // { dg-bogus "" }
+
+ void fc1() const; // { dg-note "previous declaration" }
+ void fc1(this S0 &); // { dg-bogus "" }
+ void fc1(this S0 &&); // { dg-bogus "" }
+ void fc1(this S0 const&); // { dg-error "cannot be overloaded with" }
+ void fc1(this S0 volatile&); // { dg-bogus "" }
+ void fc1(this S0 volatile&&); // { dg-bogus "" }
+ void fc1(this S0 const volatile&); // { dg-bogus "" }
+ void fc1(this S0 const volatile&&); // { dg-bogus "" }
+
+ void fv0() volatile; // { dg-note "previous declaration" }
+ void fv0(this S0 &); // { dg-bogus "" }
+ void fv0(this S0 &&); // { dg-bogus "" }
+ void fv0(this S0 const&); // { dg-bogus "" }
+ void fv0(this S0 const&&); // { dg-bogus "" }
+ void fv0(this S0 volatile&&); // { dg-error "cannot be overloaded with" }
+ void fv0(this S0 const volatile&); // { dg-bogus "" }
+ void fv0(this S0 const volatile&&); // { dg-bogus "" }
+
+ void fv1() volatile; // { dg-note "previous declaration" }
+ void fv1(this S0 &); // { dg-bogus "" }
+ void fv1(this S0 &&); // { dg-bogus "" }
+ void fv1(this S0 const&); // { dg-bogus "" }
+ void fv1(this S0 const&&); // { dg-bogus "" }
+ void fv1(this S0 volatile&); // { dg-error "cannot be overloaded with" }
+ void fv1(this S0 const volatile&); // { dg-bogus "" }
+ void fv1(this S0 const volatile&&); // { dg-bogus "" }
+
+ void fcv0() const volatile; // { dg-note "previous declaration" }
+ void fcv0(this S0 &); // { dg-bogus "" }
+ void fcv0(this S0 &&); // { dg-bogus "" }
+ void fcv0(this S0 const&); // { dg-bogus "" }
+ void fcv0(this S0 const&&); // { dg-bogus "" }
+ void fcv0(this S0 volatile&); // { dg-bogus "" }
+ void fcv0(this S0 volatile&&); // { dg-bogus "" }
+ void fcv0(this S0 const volatile&&); // { dg-error "cannot be overloaded with" }
+
+ void fcv1() const volatile; // { dg-note "previous declaration" }
+ void fcv1(this S0 &); // { dg-bogus "" }
+ void fcv1(this S0 &&); // { dg-bogus "" }
+ void fcv1(this S0 const&); // { dg-bogus "" }
+ void fcv1(this S0 const&&); // { dg-bogus "" }
+ void fcv1(this S0 volatile&); // { dg-bogus "" }
+ void fcv1(this S0 volatile&&); // { dg-bogus "" }
+ void fcv1(this S0 const volatile&); // { dg-error "cannot be overloaded with" }
+};
+
+// iobj last
+
+struct S1 {
+ void f0(this S1 &&); // { dg-note "previous declaration" }
+ void f0(this S1 const&); // { dg-bogus "" }
+ void f0(this S1 const&&); // { dg-bogus "" }
+ void f0(this S1 volatile&); // { dg-bogus "" }
+ void f0(this S1 volatile&&); // { dg-bogus "" }
+ void f0(this S1 const volatile&); // { dg-bogus "" }
+ void f0(this S1 const volatile&&); // { dg-bogus "" }
+ void f0(); // { dg-error "cannot be overloaded with" }
+
+ void f1(this S1 &); // { dg-note "previous declaration" }
+ void f1(this S1 const&); // { dg-bogus "" }
+ void f1(this S1 const&&); // { dg-bogus "" }
+ void f1(this S1 volatile&); // { dg-bogus "" }
+ void f1(this S1 volatile&&); // { dg-bogus "" }
+ void f1(this S1 const volatile&); // { dg-bogus "" }
+ void f1(this S1 const volatile&&); // { dg-bogus "" }
+ void f1(); // { dg-error "cannot be overloaded with" }
+
+ void fc0(this S1 &); // { dg-bogus "" }
+ void fc0(this S1 &&); // { dg-bogus "" }
+ void fc0(this S1 const&&); // { dg-note "previous declaration" }
+ void fc0(this S1 volatile&); // { dg-bogus "" }
+ void fc0(this S1 volatile&&); // { dg-bogus "" }
+ void fc0(this S1 const volatile&); // { dg-bogus "" }
+ void fc0(this S1 const volatile&&); // { dg-bogus "" }
+ void fc0() const; // { dg-error "cannot be overloaded with" }
+
+ void fc1(this S1 &); // { dg-bogus "" }
+ void fc1(this S1 &&); // { dg-bogus "" }
+ void fc1(this S1 const&); // { dg-note "previous declaration" }
+ void fc1(this S1 volatile&); // { dg-bogus "" }
+ void fc1(this S1 volatile&&); // { dg-bogus "" }
+ void fc1(this S1 const volatile&); // { dg-bogus "" }
+ void fc1(this S1 const volatile&&); // { dg-bogus "" }
+ void fc1() const; // { dg-error "cannot be overloaded with" }
+
+ void fv0(this S1 &); // { dg-bogus "" }
+ void fv0(this S1 &&); // { dg-bogus "" }
+ void fv0(this S1 const&); // { dg-bogus "" }
+ void fv0(this S1 const&&); // { dg-bogus "" }
+ void fv0(this S1 volatile&&); // { dg-note "previous declaration" }
+ void fv0(this S1 const volatile&); // { dg-bogus "" }
+ void fv0(this S1 const volatile&&); // { dg-bogus "" }
+ void fv0() volatile; // { dg-error "cannot be overloaded with" }
+
+ void fv1(this S1 &); // { dg-bogus "" }
+ void fv1(this S1 &&); // { dg-bogus "" }
+ void fv1(this S1 const&); // { dg-bogus "" }
+ void fv1(this S1 const&&); // { dg-bogus "" }
+ void fv1(this S1 volatile&); // { dg-note "previous declaration" }
+ void fv1(this S1 const volatile&); // { dg-bogus "" }
+ void fv1(this S1 const volatile&&); // { dg-bogus "" }
+ void fv1() volatile; // { dg-error "cannot be overloaded with" }
+
+ void fcv0(this S1 &); // { dg-bogus "" }
+ void fcv0(this S1 &&); // { dg-bogus "" }
+ void fcv0(this S1 const&); // { dg-bogus "" }
+ void fcv0(this S1 const&&); // { dg-bogus "" }
+ void fcv0(this S1 volatile&); // { dg-bogus "" }
+ void fcv0(this S1 volatile&&); // { dg-bogus "" }
+ void fcv0(this S1 const volatile&&); // { dg-note "previous declaration" }
+ void fcv0() const volatile; // { dg-error "cannot be overloaded with" }
+
+ void fcv1(this S1 &); // { dg-bogus "" }
+ void fcv1(this S1 &&); // { dg-bogus "" }
+ void fcv1(this S1 const&); // { dg-bogus "" }
+ void fcv1(this S1 const&&); // { dg-bogus "" }
+ void fcv1(this S1 volatile&); // { dg-bogus "" }
+ void fcv1(this S1 volatile&&); // { dg-bogus "" }
+ void fcv1(this S1 const volatile&); // { dg-note "previous declaration" }
+ void fcv1() const volatile; // { dg-error "cannot be overloaded with" }
+};
+
+// in order (iobj replacing one of the following in each group)
+// lvalue ref to S
+// rvalue ref to S
+// lvalue c ref to S
+// rvalue c ref to S
+// lvalue v ref to S
+// rvalue v ref to S
+// lvalue cv ref to S
+// rvalue cv ref to S
+
+struct S2 {
+ void f0(); // { dg-note "previous declaration" }
+ void f0(this S2 &&); // { dg-error "cannot be overloaded with" }
+ void f0(this S2 const&); // { dg-bogus "" }
+ void f0(this S2 const&&); // { dg-bogus "" }
+ void f0(this S2 volatile&); // { dg-bogus "" }
+ void f0(this S2 volatile&&); // { dg-bogus "" }
+ void f0(this S2 const volatile&); // { dg-bogus "" }
+ void f0(this S2 const volatile&&); // { dg-bogus "" }
+
+ void f1(this S2 &); // { dg-note "previous declaration" }
+ void f1(); // { dg-error "cannot be overloaded with" }
+ void f1(this S2 const&); // { dg-bogus "" }
+ void f1(this S2 const&&); // { dg-bogus "" }
+ void f1(this S2 volatile&); // { dg-bogus "" }
+ void f1(this S2 volatile&&); // { dg-bogus "" }
+ void f1(this S2 const volatile&); // { dg-bogus "" }
+ void f1(this S2 const volatile&&); // { dg-bogus "" }
+
+ void fc0(this S2 &); // { dg-bogus "" }
+ void fc0(this S2 &&); // { dg-bogus "" }
+ void fc0() const; // { dg-note "previous declaration" }
+ void fc0(this S2 const&&); // { dg-error "cannot be overloaded with" }
+ void fc0(this S2 volatile&); // { dg-bogus "" }
+ void fc0(this S2 volatile&&); // { dg-bogus "" }
+ void fc0(this S2 const volatile&); // { dg-bogus "" }
+ void fc0(this S2 const volatile&&); // { dg-bogus "" }
+
+ void fc1(this S2 &); // { dg-bogus "" }
+ void fc1(this S2 &&); // { dg-bogus "" }
+ void fc1(this S2 const&); // { dg-note "previous declaration" }
+ void fc1() const; // { dg-error "cannot be overloaded with" }
+ void fc1(this S2 volatile&); // { dg-bogus "" }
+ void fc1(this S2 volatile&&); // { dg-bogus "" }
+ void fc1(this S2 const volatile&); // { dg-bogus "" }
+ void fc1(this S2 const volatile&&); // { dg-bogus "" }
+
+ void fv0(this S2 &); // { dg-bogus "" }
+ void fv0(this S2 &&); // { dg-bogus "" }
+ void fv0(this S2 const&); // { dg-bogus "" }
+ void fv0(this S2 const&&); // { dg-bogus "" }
+ void fv0() volatile; // { dg-note "previous declaration" }
+ void fv0(this S2 volatile&&); // { dg-error "cannot be overloaded with" }
+ void fv0(this S2 const volatile&); // { dg-bogus "" }
+ void fv0(this S2 const volatile&&); // { dg-bogus "" }
+
+ void fv1(this S2 &); // { dg-bogus "" }
+ void fv1(this S2 &&); // { dg-bogus "" }
+ void fv1(this S2 const&); // { dg-bogus "" }
+ void fv1(this S2 const&&); // { dg-bogus "" }
+ void fv1(this S2 volatile&); // { dg-note "previous declaration" }
+ void fv1() volatile; // { dg-error "cannot be overloaded with" }
+ void fv1(this S2 const volatile&); // { dg-bogus "" }
+ void fv1(this S2 const volatile&&); // { dg-bogus "" }
+
+ void fcv0(this S2 &); // { dg-bogus "" }
+ void fcv0(this S2 &&); // { dg-bogus "" }
+ void fcv0(this S2 const&); // { dg-bogus "" }
+ void fcv0(this S2 const&&); // { dg-bogus "" }
+ void fcv0(this S2 volatile&); // { dg-bogus "" }
+ void fcv0(this S2 volatile&&); // { dg-bogus "" }
+ void fcv0() const volatile; // { dg-note "previous declaration" }
+ void fcv0(this S2 const volatile&&); // { dg-error "cannot be overloaded with" }
+
+ void fcv1(this S2 &); // { dg-bogus "" }
+ void fcv1(this S2 &&); // { dg-bogus "" }
+ void fcv1(this S2 const&); // { dg-bogus "" }
+ void fcv1(this S2 const&&); // { dg-bogus "" }
+ void fcv1(this S2 volatile&); // { dg-bogus "" }
+ void fcv1(this S2 volatile&&); // { dg-bogus "" }
+ void fcv1(this S2 const volatile&); // { dg-note "previous declaration" }
+ void fcv1() const volatile; // { dg-error "cannot be overloaded with" }
+};
+
--- /dev/null
+// P0847R7
+// { dg-do compile { target c++23 } }
+// { dg-options "-pedantic-errors -Wno-volatile" }
+
+// rejecting redeclarations of by-value xobj member functions
+// as iobj member functions that are not ref qualified (and vice-versa)
+// also check that valid overloads are accepted without diagnostic
+
+// iobj | xobj | MSVC | clang | ISOC++
+// none | none | X | X | X
+// none | c | X | O | X
+// none | v | X | O | X
+// none | cv | X | O | X
+// c | none | O | O | O
+// c | c | O | X | O
+// c | v | O | O | O
+// c | cv | O | O | O
+// v | none | O | O | O
+// v | c | O | O | O
+// v | v | O | X | O
+// v | cv | O | O | O
+// cv | none | O | O | O
+// cv | c | O | O | O
+// cv | v | O | O | O
+// cv | cv | O | X | O
+
+/* Top-level cv qualifiers are supposed to be discarded from
+ the parameters of a function declaration.
+
+ [dcl.fct.5]
+ After producing the list of parameter types, any top-level
+ cv-qualifiers modifying a parameter type are deleted when forming
+ the function type.
+
+ According to the standard, the type of an implicit object parameter
+ is always a reference. This isn't reflected in GCC but we still need
+ to take this rule into account here.
+
+ [over.match.funcs.general.4]
+ For implicit object member functions, the type of the implicit
+ object parameter is
+ -- “lvalue reference to cv X” for functions declared
+ without a ref-qualifier or with the & ref-qualifier
+ -- “rvalue reference to cv X” for functions declared with
+ the && ref-qualifier
+
+ When comparing an iobj and xobj member function to see if they are
+ redeclarations we treat them differently depending on if the iobj
+ member function has a ref qualifier or not.
+ If the iobj member function does not have a ref qualifier, we need to
+ strip the top-level references before comparing them.
+
+ [basic.scope.scope.3]
+ Two non-static member functions have corresponding object
+ parameters if:
+ -- exactly one is an implicit object member function with no
+ ref-qualifier and the types of their object parameters
+ ([dcl.fct]), after removing top-level references, are the
+ same, or
+ -- their object parameters have the same type. */
+
+struct S {
+ void f0(); // { dg-note "previous declaration" }
+ void f0(this S); // { dg-error "cannot be overloaded with" }
+
+ void f1(); // { dg-note "previous declaration" }
+ void f1(this S const); // { dg-error "cannot be overloaded with" }
+
+ void f2(); // { dg-note "previous declaration" }
+ void f2(this S volatile); // { dg-error "cannot be overloaded with" }
+
+ void f3(); // { dg-note "previous declaration" }
+ void f3(this S const volatile); // { dg-error "cannot be overloaded with" }
+
+ void fc0() const; // { dg-bogus "" }
+ void fc0(this S); // { dg-bogus "" }
+
+ void fc1() const; // { dg-bogus "" }
+ void fc1(this S const); // { dg-bogus "" }
+
+ void fc2() const; // { dg-bogus "" }
+ void fc2(this S volatile); // { dg-bogus "" }
+
+ void fc3() const; // { dg-bogus "" }
+ void fc3(this S const volatile); // { dg-bogus "" }
+
+ void fv0() volatile; // { dg-bogus "" }
+ void fv0(this S); // { dg-bogus "" }
+
+ void fv1() volatile; // { dg-bogus "" }
+ void fv1(this S const); // { dg-bogus "" }
+
+ void fv2() volatile; // { dg-bogus "" }
+ void fv2(this S volatile); // { dg-bogus "" }
+
+ void fv3() volatile; // { dg-bogus "" }
+ void fv3(this S const volatile); // { dg-bogus "" }
+
+ void fcv0() const volatile; // { dg-bogus "" }
+ void fcv0(this S); // { dg-bogus "" }
+
+ void fcv1() const volatile; // { dg-bogus "" }
+ void fcv1(this S const); // { dg-bogus "" }
+
+ void fcv2() const volatile; // { dg-bogus "" }
+ void fcv2(this S volatile); // { dg-bogus "" }
+
+ void fcv3() const volatile; // { dg-bogus "" }
+ void fcv3(this S const volatile); // { dg-bogus "" }
+
+ // same as the above f cases except reversed
+
+ void g0(this S); // { dg-note "previous declaration" }
+ void g0(); // { dg-error "cannot be overloaded with" }
+
+ void g1(this S const); // { dg-note "previous declaration" }
+ void g1(); // { dg-error "cannot be overloaded with" }
+
+ void g2(this S volatile); // { dg-note "previous declaration" }
+ void g2(); // { dg-error "cannot be overloaded with" }
+
+ void g3(this S const volatile); // { dg-note "previous declaration" }
+ void g3(); // { dg-error "cannot be overloaded with" }
+
+ void gc0(this S); // { dg-bogus "" }
+ void gc0() const; // { dg-bogus "" }
+
+ void gc1(this S const); // { dg-bogus "" }
+ void gc1() const; // { dg-bogus "" }
+
+ void gc2(this S volatile); // { dg-bogus "" }
+ void gc2() const; // { dg-bogus "" }
+
+ void gc3(this S const volatile); // { dg-bogus "" }
+ void gc3() const; // { dg-bogus "" }
+
+ void gv0(this S); // { dg-bogus "" }
+ void gv0() volatile; // { dg-bogus "" }
+
+ void gv1(this S const); // { dg-bogus "" }
+ void gv1() volatile; // { dg-bogus "" }
+
+ void gv2(this S volatile); // { dg-bogus "" }
+ void gv2() volatile; // { dg-bogus "" }
+
+ void gv3(this S const volatile); // { dg-bogus "" }
+ void gv3() volatile; // { dg-bogus "" }
+
+ void gcv0(this S); // { dg-bogus "" }
+ void gcv0() const volatile; // { dg-bogus "" }
+
+ void gcv1(this S const); // { dg-bogus "" }
+ void gcv1() const volatile; // { dg-bogus "" }
+
+ void gcv2(this S volatile); // { dg-bogus "" }
+ void gcv2() const volatile; // { dg-bogus "" }
+
+ void gcv3(this S const volatile); // { dg-bogus "" }
+ void gcv3() const volatile; // { dg-bogus "" }
+};
+
--- /dev/null
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// redeclarations of xobj member functions as static member functions and vice versa
+
+struct S {
+// no additional params
+ void f_xs_v(this S) {}; // { dg-note {previous declaration} }
+ static void f_xs_v() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref(this S&) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref(this S const&) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref(this S&&) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref(this S const&&) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref() {}; // { dg-error {cannot be overloaded with} }
+
+// reversed
+ static void f_sx_v() {}; // { dg-note {previous declaration} }
+ void f_sx_v(this S) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref() {}; // { dg-note {previous declaration} }
+ void f_sx_ref(this S&) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref() {}; // { dg-note {previous declaration} }
+ void f_sx_cref(this S const&) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref() {}; // { dg-note {previous declaration} }
+ void f_sx_rref(this S&&) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref() {}; // { dg-note {previous declaration} }
+ void f_sx_crref(this S const&&) {}; // { dg-error {cannot be overloaded with} }
+
+// one additional param
+ void f_xs_v_int(this S, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_v_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref_int(this S&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref_int(this S const&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref_int(this S&&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref_int(this S const&&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+// reversed
+ static void f_sx_v_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_v_int(this S, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_ref_int(this S&, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_cref_int(this S const&, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_rref_int(this S&&, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_crref_int(this S const&&, int) {}; // { dg-error {cannot be overloaded with} }
+
+// two additional params
+ void f_xs_v_int2(this S, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_v_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref_int2(this S&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref_int2(this S const&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref_int2(this S&&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref_int2(this S const&&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+// reversed
+ static void f_sx_v_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_v_int2(this S, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_ref_int2(this S&, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_cref_int2(this S const&, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_rref_int2(this S&&, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_crref_int2(this S const&&, int, int) {}; // { dg-error {cannot be overloaded with} }
+};
+
+// unrelated explicit object parameter type
+
+struct A {};
+
+struct S1
+{
+// no additional params
+ void f_xs_v(this A) {}; // { dg-note {previous declaration} }
+ static void f_xs_v() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref(this A&) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref(this A const&) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref(this A&&) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref(this A const&&) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref() {}; // { dg-error {cannot be overloaded with} }
+
+// reversed
+ static void f_sx_v() {}; // { dg-note {previous declaration} }
+ void f_sx_v(this A) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref() {}; // { dg-note {previous declaration} }
+ void f_sx_ref(this A&) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref() {}; // { dg-note {previous declaration} }
+ void f_sx_cref(this A const&) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref() {}; // { dg-note {previous declaration} }
+ void f_sx_rref(this A&&) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref() {}; // { dg-note {previous declaration} }
+ void f_sx_crref(this A const&&) {}; // { dg-error {cannot be overloaded with} }
+
+// one additional param
+ void f_xs_v_int(this A, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_v_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref_int(this A&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref_int(this A const&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref_int(this A&&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref_int(this A const&&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+// reversed
+ static void f_sx_v_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_v_int(this A, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_ref_int(this A&, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_cref_int(this A const&, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_rref_int(this A&&, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_crref_int(this A const&&, int) {}; // { dg-error {cannot be overloaded with} }
+
+// two additional params
+ void f_xs_v_int2(this A, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_v_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref_int2(this A&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref_int2(this A const&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref_int2(this A&&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref_int2(this A const&&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+// reversed
+ static void f_sx_v_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_v_int2(this A, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_ref_int2(this A&, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_cref_int2(this A const&, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_rref_int2(this A&&, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_crref_int2(this A const&&, int, int) {}; // { dg-error {cannot be overloaded with} }
+};
+
--- /dev/null
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// redeclarations with equivalent constraints
+
+template<typename T>
+concept Constrain = true;
+
+
+struct S {
+// xobj/static
+ void f_xs_v(this S, Constrain auto) {}; // { dg-note {previous declaration} }
+ static void f_xs_v(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref(this S&, Constrain auto) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref(this S const&, Constrain auto) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref(this S&&, Constrain auto) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref(this S const&&, Constrain auto) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+// static/xobj
+ static void f_sx_v(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_sx_v(this S, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_sx_ref(this S&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_sx_cref(this S const&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_sx_rref(this S&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_sx_crref(this S const&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ // iobj/xobj
+ void f_ix_lref(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_ix_lref(this S&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_ix_rref(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_ix_rref(this S&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_ix_const_lref(Constrain auto) const {}; // { dg-note {previous declaration} }
+ void f_ix_const_lref(this S const&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_ix_const_rref(Constrain auto) const {}; // { dg-note {previous declaration} }
+ void f_ix_const_rref(this S const&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ // xobj/iobj
+ void f_xi_lref(this S&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_lref(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xi_rref(this S&&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_rref(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xi_const_lref(this S const&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_const_lref(Constrain auto) const {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xi_const_rref(this S const&&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_const_rref(Constrain auto) const {}; // { dg-error {cannot be overloaded with} }
+
+ // with ref qualifier
+
+ // iobj/xobj
+ void f_ix_lref_refqual(Constrain auto) & {}; // { dg-note {previous declaration} }
+ void f_ix_lref_refqual(this S&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_ix_rref_refqual(Constrain auto) && {}; // { dg-note {previous declaration} }
+ void f_ix_rref_refqual(this S&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_ix_const_lref_refqual(Constrain auto) const& {}; // { dg-note {previous declaration} }
+ void f_ix_const_lref_refqual(this S const&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_ix_const_rref_refqual(Constrain auto) const&& {}; // { dg-note {previous declaration} }
+ void f_ix_const_rref_refqual(this S const&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ // xobj/iobj
+ void f_xi_lref_refqual(this S&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_lref_refqual(Constrain auto) & {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xi_rref_refqual(this S&&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_rref_refqual(Constrain auto) && {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xi_const_lref_refqual(this S const&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_const_lref_refqual(Constrain auto) const& {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xi_const_rref_refqual(this S const&&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_const_rref_refqual(Constrain auto) const&& {}; // { dg-error {cannot be overloaded with} }
+};
+