From c8e8155a635fab7f326d0ad32326da352d7c323e Mon Sep 17 00:00:00 2001
From: waffl3x <waffl3x@protonmail.com>
Date: Sun, 5 Nov 2023 05:17:18 -0700
Subject: [PATCH 2/2] c++: Diagnostics for C++23 P0847R7 (Deducing this)
[PR102609]
This patch adds diagnostics for various ill-formed code related to xobj member
functions. Some of the code in here leaves something to be desired, but the
majority of cases should be handled. I opted to add a new TFF flag despite only
using it in a single place, other solutions seemed non ideal and there are
plenty of bits left. Some of the diagnostics are more scattered around than I
would like, perhaps this could be refactored in the future, especially those in
grokfndecl.
PR c++/102609
gcc/cp/ChangeLog:
PR c++/102609
Diagnostics for C++23 P0847R7 - Deducing this.
* cp-tree.h (TFF_XOBJ_FUNC): Define.
* decl.cc (grokfndecl): Diagnose cvref-qualifiers on an xobj member
function.
(grokdeclarator): Diagnostics
* error.cc (dump_function_decl): For xobj member function add
TFF_XOBJ_FUNC bit to dump_parameters flags argument.
(dump_parameters): When printing xobj member function's params add
"this" to the first param.
(function_category): Say so when in an xobj member function.
* parser.cc (cp_parser_decl_specifier_seq): Diagnose incorrectly
positioned "this" specifier.
(cp_parser_parameter_declaration): Diagnose default argument on
xobj params.
* semantics.cc (finish_this_expr): Diagnose uses of "this" in body
of xobj member function.
gcc/testsuite/ChangeLog:
PR c++/102609
Diagnostics for C++23 P0847R7 - Deducing this.
* g++.dg/cpp23/explicit-obj-cxx-dialect-A.C: New test.
* g++.dg/cpp23/explicit-obj-cxx-dialect-B.C: New test.
* g++.dg/cpp23/explicit-obj-cxx-dialect-C.C: New test.
* g++.dg/cpp23/explicit-obj-cxx-dialect-D.C: New test.
* g++.dg/cpp23/explicit-obj-cxx-dialect-E.C: New test.
* g++.dg/cpp23/explicit-obj-diagnostics1.C: New test.
* g++.dg/cpp23/explicit-obj-diagnostics2.C: New test.
* g++.dg/cpp23/explicit-obj-diagnostics3.C: New test.
* g++.dg/cpp23/explicit-obj-diagnostics4.C: New test.
* g++.dg/cpp23/explicit-obj-diagnostics5.C: New test.
* g++.dg/cpp23/explicit-obj-diagnostics6.C: New test.
* g++.dg/cpp23/explicit-obj-diagnostics7.C: New test.
Signed-off-by: waffl3x <waffl3x@protonmail.com>
---
gcc/cp/cp-tree.h | 5 +-
gcc/cp/decl.cc | 133 ++++++++++++++---
gcc/cp/error.cc | 8 +-
gcc/cp/parser.cc | 45 ++++++
gcc/cp/semantics.cc | 28 +++-
.../g++.dg/cpp23/explicit-obj-cxx-dialect-A.C | 6 +
.../g++.dg/cpp23/explicit-obj-cxx-dialect-B.C | 7 +
.../g++.dg/cpp23/explicit-obj-cxx-dialect-C.C | 7 +
.../g++.dg/cpp23/explicit-obj-cxx-dialect-D.C | 7 +
.../g++.dg/cpp23/explicit-obj-cxx-dialect-E.C | 7 +
.../g++.dg/cpp23/explicit-obj-diagnostics1.C | 138 ++++++++++++++++++
.../g++.dg/cpp23/explicit-obj-diagnostics2.C | 25 ++++
.../g++.dg/cpp23/explicit-obj-diagnostics3.C | 84 +++++++++++
.../g++.dg/cpp23/explicit-obj-diagnostics4.C | 19 +++
.../g++.dg/cpp23/explicit-obj-diagnostics5.C | 15 ++
.../g++.dg/cpp23/explicit-obj-diagnostics6.C | 22 +++
.../g++.dg/cpp23/explicit-obj-diagnostics7.C | 24 +++
17 files changed, 560 insertions(+), 20 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C
@@ -6116,7 +6116,9 @@ enum auto_deduction_context
identical to their defaults.
TFF_NO_TEMPLATE_BINDINGS: do not print information about the template
arguments for a function template specialization.
- TFF_POINTER: we are printing a pointer type. */
+ TFF_POINTER: we are printing a pointer type.
+ TFF_XOBJ_FUNC: we are printing an explicit object member function's
+ parameters. */
#define TFF_PLAIN_IDENTIFIER (0)
#define TFF_SCOPE (1)
@@ -6134,6 +6136,7 @@ enum auto_deduction_context
#define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS (1 << 12)
#define TFF_NO_TEMPLATE_BINDINGS (1 << 13)
#define TFF_POINTER (1 << 14)
+#define TFF_XOBJ_FUNC (1 << 15)
/* These constants can be used as bit flags to control strip_typedefs.
@@ -10607,24 +10607,29 @@ grokfndecl (tree ctype,
TREE_TYPE (decl) = apply_memfn_quals (TREE_TYPE (decl),
TYPE_UNQUALIFIED,
REF_QUAL_NONE);
-
if (quals)
- {
- error (ctype
+ error (!ctype
+ ? G_("non-member function %qD cannot have cv-qualifier")
+ : !xobj_func_p
? G_("static member function %qD cannot have cv-qualifier")
- : G_("non-member function %qD cannot have cv-qualifier"),
- decl);
- quals = TYPE_UNQUALIFIED;
- }
-
+ : G_("explicit object member function "
+ "%qD cannot have cv-qualifier"),
+ decl);
if (rqual)
- {
- error (ctype
+ error (!ctype
+ ? G_("non-member function %qD cannot have ref-qualifier")
+ : !xobj_func_p
? G_("static member function %qD cannot have ref-qualifier")
- : G_("non-member function %qD cannot have ref-qualifier"),
+ : G_("explicit object member function "
+ "%qD cannot have ref-qualifier"),
decl);
- rqual = REF_QUAL_NONE;
- }
+
+ if (xobj_func_p && (quals || rqual))
+ inform (DECL_SOURCE_LOCATION (DECL_ARGUMENTS (decl)),
+ "explicit object parameter declared here");
+ quals = TYPE_UNQUALIFIED;
+ rqual = REF_QUAL_NONE;
+
}
if (deduction_guide_p (decl))
@@ -13117,19 +13122,113 @@ grokdeclarator (const cp_declarator *declarator,
/* There is no need to iterate over the list,
only the first parm can be a valid xobj parm. */
if (!parm_list || parm_list == void_list_node)
- return false;
+ return NULL_TREE;
if (TREE_PURPOSE (parm_list) != this_identifier)
- return false;
+ return NULL_TREE;
/* If we make it here, we are looking at an xobj parm.
Non-null 'purpose' usually means the parm has a default
argument, we don't want to violate this assumption. */
TREE_PURPOSE (parm_list) = NULL_TREE;
- return true;
+ return TREE_VALUE (parm_list);
};
- is_xobj_member_function
+ tree xobj_parm
= find_xobj_parm (declarator->u.function.parameters);
+ is_xobj_member_function = xobj_parm;
+
+ if (xobj_parm && cxx_dialect < cxx23)
+ pedwarn(DECL_SOURCE_LOCATION (xobj_parm), OPT_Wc__23_extensions,
+ "explicit object member function only available "
+ "with %<-std=c++23%> or %<-std=gnu++23%>");
+
+ /* Error handling for explicit object member functions. */
+ if (!xobj_parm)
+ /* Early escape. */;
+ else if (decl_context == TYPENAME)
+ {
+ bool ptr_type = true;
+ /* If */
+ if (!declarator->declarator)
+ {
+ error_at (DECL_SOURCE_LOCATION (xobj_parm),
+ "a function type cannot "
+ "have an explicit object parameter");
+ ptr_type = false;
+ }
+ else if (declarator->declarator->kind == cdk_pointer)
+ error_at (DECL_SOURCE_LOCATION (xobj_parm),
+ "a function pointer type cannot "
+ "have an explicit object parameter");
+ else if (declarator->declarator->kind == cdk_ptrmem)
+ error_at (DECL_SOURCE_LOCATION (xobj_parm),
+ "a member function pointer type "
+ "cannot have an explicit object parameter");
+ else
+ gcc_unreachable ();
+
+ if (ptr_type)
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "the type of a pointer to explicit object member "
+ "function is a regular pointer to function type");
+ else
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "the type of an explicit object "
+ "member function is a regular function type");
+ /* Ideally we should synthesize the correct syntax
+ for the user, perhaps this could be added later. */
+ }
+ /* Free function case,
+ surely there is a better way to identify it? */
+ else if (decl_context == NORMAL
+ && (in_namespace
+ || !declarator->declarator->u.id.qualifying_scope))
+ error_at (DECL_SOURCE_LOCATION (xobj_parm),
+ "a free function cannot have "
+ "an explicit object parameter");
+ else /* if (xobj_parm) */
+ {
+ if (virtualp)
+ {
+ error_at (declspecs->locations[ds_virtual],
+ "an explicit object member function cannot be "
+ "%<virtual%>");
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "explicit object parameter declared here");
+ virtualp = false;
+ }
+ if (staticp >= 2)
+ {
+ error_at (declspecs->locations[ds_storage_class],
+ "an explicit object member function cannot be "
+ "%<static%>");
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "explicit object parameter declared here");
+ }
+ }
+ /* We don't need to skip over the first node even if it is an xobj
+ parm, find_xobj_parm clears the flag so it will be treated as a
+ normal parm, thus immediately going to the next node without
+ emitting an errant diagnosis. */
+ bool bad_xobj_parm_encountered = false;
+ for (tree parm = declarator->u.function.parameters;
+ parm && parm != void_list_node;
+ parm = TREE_CHAIN (parm))
+ {
+ if (TREE_PURPOSE (parm) != this_identifier)
+ continue;
+ bad_xobj_parm_encountered = true;
+ gcc_rich_location bad_xobj_parm
+ (DECL_SOURCE_LOCATION (TREE_VALUE (parm)));
+ /* I'm keeping it more basic for now. */
+ error_at (&bad_xobj_parm,
+ "Only the first parameter of a member function "
+ "can be declared as an explicit object parameter");
+ }
+ if (bad_xobj_parm_encountered && xobj_parm)
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "Valid explicit object parameter declared here");
+ /* End of explicit object member function diagnostics. */
if (reqs)
error_at (location_of (reqs), "requires-clause on return type");
@@ -1831,7 +1831,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
{
- dump_parameters (pp, parmtypes, flags);
+ dump_parameters (pp, parmtypes,
+ DECL_XOBJ_MEMBER_FUNC_P (t) ? TFF_XOBJ_FUNC | flags
+ : flags);
if (TREE_CODE (fntype) == METHOD_TYPE)
{
@@ -1910,6 +1912,8 @@ dump_parameters (cxx_pretty_printer *pp, tree parmtypes, int flags)
for (first = 1; parmtypes != void_list_node;
parmtypes = TREE_CHAIN (parmtypes))
{
+ if (first && flags & TFF_XOBJ_FUNC)
+ pp_string (pp, "this ");
if (!first)
pp_separate_with_comma (pp);
first = 0;
@@ -3685,6 +3689,8 @@ function_category (tree fn)
return _("In destructor %qD");
else if (LAMBDA_FUNCTION_P (fn))
return _("In lambda function");
+ else if (DECL_XOBJ_MEMBER_FUNC_P (fn))
+ return _("In explicit object member function %qD");
else
return _("In member function %qD");
}
@@ -16019,6 +16019,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
/* Assume no class or enumeration type is declared. */
*declares_class_or_enum = 0;
+ /* Keep a token that additionally will be used for diagnostics. */
+ cp_token *first_specifier = NULL;
/* Keep reading specifiers until there are no more to read. */
while (true)
{
@@ -16091,12 +16093,42 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
decl_specs->locations[ds_attribute] = token->location;
continue;
}
+ /* We know by this point that the token is not part of an attribute. */
+ if (!first_specifier)
+ first_specifier = token;
/* Special case for "this" specifier, indicating a parm is an xobj parm.
The "this" specifier must be the first specifier in the declaration,
after any attributes. */
if (token->keyword == RID_THIS)
{
cp_lexer_consume_token (parser->lexer);
+ if (token != first_specifier)
+ {
+ /* Don't emit diagnostics if we have already seen "this",
+ leave it for set_and_check_decl_spec_loc. */
+ if (decl_specs->locations[ds_this] == 0)
+ {
+ gcc_rich_location richloc (token->location);
+ /* Ideally we synthesize a full rewrite, at the moment
+ there are issues with it though.
+ It rewrites "f(S this & s)" correctly,
+ but fails to rewrite "f(const this S s)" correctly.
+ It also does not handle "f(S& this s)" correctly at all.
+
+ It's also possible we want to wait and see if the parm
+ could even be a valid xobj parm as it might be confusing
+ to the user to see an error, fix it, and then see another
+ error for something new.
+
+ In short, this area needs improvement. */
+ richloc.add_fixit_insert_before
+ (first_specifier->location, "this ");
+ richloc.add_fixit_remove ();
+ error_at (&richloc,
+ "%<this%> must be the first specifier "
+ "in a parameter declaration");
+ }
+ }
set_and_check_decl_spec_loc (decl_specs, ds_this, token);
continue;
}
@@ -25507,6 +25539,19 @@ cp_parser_parameter_declaration (cp_parser *parser,
if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_this))
{
+ if (default_argument)
+ {
+ /* If default_argument is non-null token should always be the
+ the location of the `=' token, this is brittle code though
+ and should be rectified in the future. */
+ location_t param_with_init_loc
+ = make_location (token->location,
+ decl_spec_token_start->location,
+ input_location);
+ error_at (param_with_init_loc,
+ "an explicit object parameter "
+ "may not have a default argument");
+ }
/* Xobj parameters can not have default arguments, thus
we can reuse the default argument field to flag the param as such. */
default_argument = this_identifier;
@@ -3085,7 +3085,33 @@ finish_this_expr (void)
return rvalue (result);
tree fn = current_nonlambda_function ();
- if (fn && DECL_STATIC_FUNCTION_P (fn))
+ if (fn && DECL_XOBJ_MEMBER_FUNC_P (fn))
+ {
+ /* I can imagine doing a fixit here, suggesting replacing
+ this / *this / this-> with &name / name / "name." but it would be
+ very difficult to get it perfect and I've been advised against
+ making imperfect fixits.
+ Perhaps it would be as simple as the replacements listed,
+ even if one is move'ing/forward'ing, if the replacement is just
+ done in the same place, it will be exactly what the user wants?
+ Even if this is true though, there's still a problem of getting the
+ context of the expression to find which tokens to replace.
+ I would really like for this to be possible though.
+ I will decide whether or not to persue this after review. */
+ tree xobj_parm = FUNCTION_DECL_CHECK (fn)->function_decl.arguments;
+ tree parm_name = DECL_MINIMAL_CHECK (xobj_parm)->decl_minimal.name;
+ error ("%<this%> is unavailable for explicit object member "
+ "functions");
+ if (parm_name)
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "use explicit object parameter %qD instead",
+ parm_name);
+ else
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "name and use the explicit object parameter instead");
+ /* Maybe suggest self as a name here? */
+ }
+ else if (fn && DECL_STATIC_FUNCTION_P (fn))
error ("%<this%> is unavailable for static member functions");
else if (fn && processing_contract_condition && DECL_CONSTRUCTOR_P (fn))
error ("invalid use of %<this%> before it is valid");
new file mode 100644
@@ -0,0 +1,6 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+struct S {
+ void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
new file mode 100644
@@ -0,0 +1,7 @@
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// { dg-options "-pedantic-errors" }
+
+struct S {
+ void f(this S); // { dg-error {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
new file mode 100644
@@ -0,0 +1,7 @@
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// { dg-options "-Wno-error=pedantic" }
+
+struct S {
+ void f(this S); // { dg-warning {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
new file mode 100644
@@ -0,0 +1,7 @@
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// { dg-options "-Wno-c++23-extensions" }
+
+struct S {
+ void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
new file mode 100644
@@ -0,0 +1,7 @@
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// { dg-options "-Wno-c++23-extensions -pedantic-errors" }
+
+struct S {
+ void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
new file mode 100644
@@ -0,0 +1,138 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of xobj member functions that have member function qualifiers.
+
+struct S {
+ void f_value_0(this S) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_value_1(this S) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_value_2(this S) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_value_3(this S) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_value_4(this S) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_value_5(this S) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_value_6(this S) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_value_7(this S) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_value_8(this S) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_value_9(this S) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_value_A(this S) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+ void f_ref_0(this S&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_ref_1(this S&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_ref_2(this S&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_ref_3(this S&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_ref_4(this S&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_ref_5(this S&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_ref_6(this S&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_ref_7(this S&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_ref_8(this S&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_ref_9(this S&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_ref_A(this S&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+ void f_refref_0(this S&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_refref_1(this S&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_refref_2(this S&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_refref_3(this S&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_refref_4(this S&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_refref_5(this S&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_refref_6(this S&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_refref_7(this S&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_refref_8(this S&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_refref_9(this S&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_refref_A(this S&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+ void f_cref_0(this S const&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_cref_1(this S const&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_cref_2(this S const&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_cref_3(this S const&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_cref_4(this S const&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_cref_5(this S const&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cref_6(this S const&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cref_7(this S const&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cref_8(this S const&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cref_9(this S const&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cref_A(this S const&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+ void f_crefref_0(this S const&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_crefref_1(this S const&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_crefref_2(this S const&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_crefref_3(this S const&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_crefref_4(this S const&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_crefref_5(this S const&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_crefref_6(this S const&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_crefref_7(this S const&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_crefref_8(this S const&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_crefref_9(this S const&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_crefref_A(this S const&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+ void f_vref_0(this S volatile&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_vref_1(this S volatile&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_vref_2(this S volatile&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_vref_3(this S volatile&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_vref_4(this S volatile&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_vref_5(this S volatile&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_vref_6(this S volatile&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_vref_7(this S volatile&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_vref_8(this S volatile&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_vref_9(this S volatile&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_vref_A(this S volatile&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+ void f_vrefref_0(this S volatile&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_vrefref_1(this S volatile&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_vrefref_2(this S volatile&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_vrefref_3(this S volatile&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_vrefref_4(this S volatile&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_vrefref_5(this S volatile&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_vrefref_6(this S volatile&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_vrefref_7(this S volatile&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_vrefref_8(this S volatile&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_vrefref_9(this S volatile&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_vrefref_A(this S volatile&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+ void f_cvref_0(this S const volatile&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_cvref_1(this S const volatile&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_cvref_2(this S const volatile&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_cvref_3(this S const volatile&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_cvref_4(this S const volatile&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_cvref_5(this S const volatile&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cvref_6(this S const volatile&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cvref_7(this S const volatile&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cvref_8(this S const volatile&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cvref_9(this S const volatile&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cvref_A(this S const volatile&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+ void f_cvrefref_0(this S const volatile&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_cvrefref_1(this S const volatile&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_cvrefref_2(this S const volatile&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void f_cvrefref_3(this S const volatile&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_cvrefref_4(this S const volatile&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void f_cvrefref_5(this S const volatile&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cvrefref_6(this S const volatile&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cvrefref_7(this S const volatile&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cvrefref_8(this S const volatile&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cvrefref_9(this S const volatile&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void f_cvrefref_A(this S const volatile&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+ template<typename Self> void d_templ_0(this Self&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ template<typename Self> void d_templ_1(this Self&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ template<typename Self> void d_templ_2(this Self&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ template<typename Self> void d_templ_3(this Self&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ template<typename Self> void d_templ_4(this Self&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ template<typename Self> void d_templ_5(this Self&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ template<typename Self> void d_templ_6(this Self&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ template<typename Self> void d_templ_7(this Self&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ template<typename Self> void d_templ_8(this Self&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ template<typename Self> void d_templ_9(this Self&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ template<typename Self> void d_templ_A(this Self&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+ void d_auto_0(this auto&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void d_auto_1(this auto&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void d_auto_2(this auto&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ void d_auto_3(this auto&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void d_auto_4(this auto&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ void d_auto_5(this auto&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void d_auto_6(this auto&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void d_auto_7(this auto&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void d_auto_8(this auto&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void d_auto_9(this auto&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ void d_auto_A(this auto&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+};
new file mode 100644
@@ -0,0 +1,25 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of incorrect uses of 'this' in declarations and definitions
+
+using func_type = void(this int); // { dg-line func_type_line }
+// { dg-error "a function type cannot have an explicit object parameter" "" { target *-*-* } func_type_line }
+// { dg-note "the type of an explicit object member function is a regular function type" "" { target *-*-* } func_type_line }
+
+using func_ptr_type = void(*)(this int); // { dg-line func_ptr_type_line }
+// { dg-error "a function pointer type cannot have an explicit object parameter" "" { target *-*-* } func_ptr_type_line }
+// { dg-note "the type of a pointer to explicit object member function is a regular pointer to function type" "" { target *-*-* } func_ptr_type_line }
+
+struct S {
+ static void f(this S) {} // { dg-line static_member_func_line }
+};
+// { dg-error "an explicit object member function cannot be 'static'" "" { target *-*-* } static_member_func_line }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } static_member_func_line }
+
+using mem_func_type = void (S::*)(this S&); // { dg-line mem_func_type_line }
+// { dg-error "a member function pointer type cannot have an explicit object parameter" "" { target *-*-* } mem_func_type_line }
+// { dg-note "the type of a pointer to explicit object member function is a regular pointer to function type" "" { target *-*-* } mem_func_type_line }
+
+void f(this int); // { dg-error "a free function cannot have an explicit object parameter" }
+void f(this int) {} // { dg-error "a free function cannot have an explicit object parameter" }
new file mode 100644
@@ -0,0 +1,84 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of equivalent redeclarations of member functions
+
+// xobj redeclared as iobj
+
+struct S0 {
+ void f0(this S0&); // { dg-note "previous declaration" }
+ void f0(); // { dg-error "cannot be overloaded with" }
+
+ void f1(this S0 const&); // { dg-note "previous declaration" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } }
+ void f1() const; // { dg-error "cannot be overloaded with" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } }
+
+ void f2(this S0 volatile&); // { dg-note "previous declaration" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } }
+ void f2() volatile; // { dg-error "cannot be overloaded with" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } }
+
+ void f3(this S0 const volatile&); // { dg-note "previous declaration" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } }
+ void f3() const volatile; // { dg-error "cannot be overloaded with" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } }
+
+ void f4(this S0&); // { dg-note "previous declaration" }
+ void f4() &; // { dg-error "cannot be overloaded with" }
+
+ void f5(this S0&&); // { dg-note "previous declaration" }
+ void f5() &&; // { dg-error "cannot be overloaded with" }
+
+ void f6(this S0 const&); // { dg-note "previous declaration" }
+ void f6() const&; // { dg-error "cannot be overloaded with" }
+
+ void f7(this S0 const&&); // { dg-note "previous declaration" }
+ void f7() const&&; // { dg-error "cannot be overloaded with" }
+
+ void f8(this S0 volatile&); // { dg-note "previous declaration" }
+ void f8() volatile&; // { dg-error "cannot be overloaded with" }
+
+ void f9(this S0 volatile&&); // { dg-note "previous declaration" }
+ void f9() volatile&&; // { dg-error "cannot be overloaded with" }
+
+ void fA(this S0 const volatile&); // { dg-note "previous declaration" }
+ void fA() const volatile&; // { dg-error "cannot be overloaded with" }
+
+ void fB(this S0 const volatile&&); // { dg-note "previous declaration" }
+ void fB() const volatile&&; // { dg-error "cannot be overloaded with" }
+};
+
+// iobj redeclared as xobj
+
+struct S1 {
+ void f0(); // { dg-note "previous declaration" }
+ void f0(this S1&); // { dg-error "cannot be overloaded with" }
+
+ void f1() const; // { dg-note "previous declaration" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }
+ void f1(this S1 const&); // { dg-error "cannot be overloaded with" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }
+
+ void f2() volatile; // { dg-note "previous declaration" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }
+ void f2(this S1 volatile&); // { dg-error "cannot be overloaded with" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }
+
+ void f3() const volatile; // { dg-note "previous declaration" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }
+ void f3(this S1 const volatile&); // { dg-error "cannot be overloaded with" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }
+
+ void f4() &; // { dg-note "previous declaration" }
+ void f4(this S1&); // { dg-error "cannot be overloaded with" }
+
+ void f5() &&; // { dg-note "previous declaration" }
+ void f5(this S1&&); // { dg-error "cannot be overloaded with" }
+
+ void f6() const&; // { dg-note "previous declaration" }
+ void f6(this S1 const&); // { dg-error "cannot be overloaded with" }
+
+ void f7() const&&; // { dg-note "previous declaration" }
+ void f7(this S1 const&&); // { dg-error "cannot be overloaded with" }
+
+ void f8() volatile&; // { dg-note "previous declaration" }
+ void f8(this S1 volatile&); // { dg-error "cannot be overloaded with" }
+
+ void f9() volatile&&; // { dg-note "previous declaration" }
+ void f9(this S1 volatile&&); // { dg-error "cannot be overloaded with" }
+
+ void fA() const volatile&; // { dg-note "previous declaration" }
+ void fA(this S1 const volatile&); // { dg-error "cannot be overloaded with" }
+
+ void fB() const volatile&&; // { dg-note "previous declaration" }
+ void fB(this S1 const volatile&&); // { dg-error "cannot be overloaded with" }
+};
new file mode 100644
@@ -0,0 +1,19 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of an xobj parameter declared with a default argument
+
+struct S {
+ void f0(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+ void f1(this S = {}); // { dg-error "an explicit object parameter may not have a default argument" }
+ void f2(this S);
+ void f10(this S s = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+ void f11(this S s = {}); // { dg-error "an explicit object parameter may not have a default argument" }
+ void f12(this S s);
+};
+
+void S::f1(this S) {}
+void S::f2(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+
+void S::f11(this S s) {}
+void S::f12(this S s = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
new file mode 100644
@@ -0,0 +1,15 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// location diagnostic text when an error is emitted from an xobj member function
+// this does not test for specific ill-formed code, just the additional diagnostic message
+
+// { dg-message "In explicit object member function" "" { target *-*-* } 0 }
+
+struct S {
+ void f(this S s) {
+ // The specific diagnosis issued here does not matter
+ // we just need to force an error to be emitted
+ +s; // { dg-error "" }
+ }
+};
new file mode 100644
@@ -0,0 +1,22 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of invalid uses of 'this' in body of xobj member functions
+
+// { dg-message "In explicit object member function" "" { target *-*-* } 0 }
+
+struct S0 {
+ int _n;
+ void f(this S0& s) { // { dg-note "use explicit object parameter 's' instead" }
+ this->_n = 10; // { dg-error "'this' is unavailable for explicit object member functions" }
+ // suppress unused variable warning
+ static_cast<void>(s);
+ }
+};
+
+struct S1 {
+ int _n;
+ void f(this S1&) { // { dg-note "name and use the explicit object parameter instead" }
+ this->_n = 10; // { dg-error "'this' is unavailable for explicit object member functions" }
+ }
+};
new file mode 100644
@@ -0,0 +1,24 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis when taking address of an unqualified xobj member function
+
+struct S {
+ static void static_f(S&) {}
+ void iobj_member_f() {}
+ void xobj_member_f(this S&) {}
+
+ void test() {
+ using func_ptr_type = void(*)(S&);
+ // using mem_func_ptr_type = void (S::*)();
+
+ // allowed (not testing for this)
+ // func_ptr_type static_f_ptr = &static_f;
+
+ // not allowed (also not testing for this)
+ // mem_func_ptr_type iobj_mem_f_ptr = &iobj_member_f;
+
+ // not allowed (this is what we are testing for)
+ func_ptr_type xobj_mem_f_ptr = &xobj_member_f; // { dg-error "undetermined error" "disallowing address of unqualified explicit object member function is not implemented yet" { xfail *-*-* } }
+ }
+};
--
2.42.0