aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorwaffl3x <waffl3x@protonmail.com>2024-01-07 00:03:19 +0000
committerJason Merrill <jason@redhat.com>2024-01-09 15:59:04 -0500
commitf8bf6a69e260a5f1aa0dbf89a6e4bcdf1a24af5d (patch)
tree6b7dd5366d64daaebc9cd68b91ee40de3fd6e537 /gcc/cp
parentfbc980d85149409ce62c22f48d3693113803929e (diff)
downloadgcc-f8bf6a69e260a5f1aa0dbf89a6e4bcdf1a24af5d.zip
gcc-f8bf6a69e260a5f1aa0dbf89a6e4bcdf1a24af5d.tar.gz
gcc-f8bf6a69e260a5f1aa0dbf89a6e4bcdf1a24af5d.tar.bz2
c++: P0847R7 (deducing this) - diagnostics. [PR102609]
Diagnostics for xobj member functions. Also includes some diagnostics for xobj lambdas which are not implemented here. CWG2554 is also implemented here, we explicitly error when an xobj member function overrides a virtual function. PR c++/102609 gcc/c-family/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - diagnostics. * c-cppbuiltin.cc (c_cpp_builtins): Define __cpp_explicit_this_parameter=202110L feature test macro. gcc/cp/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - diagnostics. * class.cc (resolve_address_of_overloaded_function): Diagnostics. * cp-tree.h (TFF_XOBJ_FUNC): Define. * decl.cc (grokfndecl): Diagnostics. (grokdeclarator): Diagnostics. * error.cc (dump_aggr_type): Pass TFF_XOBJ_FUNC. (dump_lambda_function): Formatting for xobj lambda. (dump_function_decl): Pass TFF_XOBJ_FUNC. (dump_parameters): Formatting for xobj member functions. (function_category): Formatting for xobj member functions. * parser.cc (cp_parser_decl_specifier_seq): Diagnostics. (cp_parser_parameter_declaration): Diagnostics. * search.cc (look_for_overrides_here): Make xobj member functions override. (look_for_overrides_r): Reject an overriding xobj member function and diagnose it. * semantics.cc (finish_this_expr): Diagnostics. * typeck.cc (cp_build_addr_expr_1): Diagnostics. gcc/testsuite/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - diagnostics. * g++.dg/cpp23/feat-cxx2b.C: Test existance and value of __cpp_explicit_this_parameter feature test macro. * g++.dg/cpp26/feat-cxx26.C: Likewise. * g++.dg/cpp23/explicit-obj-cxx-dialect-A.C: New test. * g++.dg/cpp23/explicit-obj-cxx-dialect-B.C: New test. * g++.dg/cpp23/explicit-obj-cxx-dialect-C.C: New test. * g++.dg/cpp23/explicit-obj-cxx-dialect-D.C: New test. * g++.dg/cpp23/explicit-obj-cxx-dialect-E.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics1.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics2.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics3.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics4.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics5.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics6.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics7.C: New test. Signed-off-by: Waffl3x <waffl3x@protonmail.com>
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/class.cc55
-rw-r--r--gcc/cp/cp-tree.h5
-rw-r--r--gcc/cp/decl.cc138
-rw-r--r--gcc/cp/error.cc24
-rw-r--r--gcc/cp/parser.cc38
-rw-r--r--gcc/cp/search.cc14
-rw-r--r--gcc/cp/semantics.cc25
-rw-r--r--gcc/cp/typeck.cc45
8 files changed, 294 insertions, 50 deletions
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6f924d5..f3cfa9f 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -8968,20 +8968,51 @@ resolve_address_of_overloaded_function (tree target_type,
fn = TREE_PURPOSE (matches);
if (DECL_OBJECT_MEMBER_FUNCTION_P (fn)
- && !(complain & tf_ptrmem_ok) && !flag_ms_extensions)
- {
- static int explained;
-
- if (!(complain & tf_error))
+ && !(complain & tf_ptrmem_ok))
+ {
+ /* Previously we allowed this behavior for iobj member functions when the
+ -fms-extensions flag is passed as MSVC allows this as a language
+ extension. MSVC also allows this for xobj member functions, but the
+ documentation for -fms-extensions states it's purpose is to support
+ the use of microsoft headers. Until otherwise demonstrated, we should
+ assume xobj member functions are not used in this manner in microsoft
+ headers and indiscriminately forbid the incorrect syntax instead of
+ supporting it for non-legacy uses. This should hopefully encourage
+ conformance going forward.
+ This comment is referred to in typeck.cc:cp_build_addr_expr_1. */
+ if (DECL_IOBJ_MEMBER_FUNCTION_P (fn) && flag_ms_extensions)
+ /* Early escape. */;
+ else if (!(complain & tf_error))
return error_mark_node;
-
- auto_diagnostic_group d;
- if (permerror (input_location, "assuming pointer to member %qD", fn)
- && !explained)
+ else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ {
+ auto_diagnostic_group d;
+ /* Should match the error in typeck.cc:cp_build_addr_expr_1.
+ We seem to lack the details here to match that diagnostic exactly,
+ perhaps this could be fixed in the future? See PR113075 bug 2. */
+ error_at (input_location,
+ "ISO C++ forbids taking the address of an unqualified"
+ " or parenthesized non-static member function to form"
+ " a pointer to explicit object member function.");
+ /* This is incorrect, see PR113075 bug 3. */
+ inform (input_location,
+ "a pointer to explicit object member function can only be "
+ "formed with %<&%E%>", fn);
+ }
+ else
{
- inform (input_location, "(a pointer to member can only be "
- "formed with %<&%E%>)", fn);
- explained = 1;
+ static int explained;
+ gcc_assert (DECL_IOBJ_MEMBER_FUNCTION_P (fn) && !flag_ms_extensions);
+ /* Is there a reason this error message doesn't match the one in
+ typeck.cc:cp_build_addr_expr_1? */
+ auto_diagnostic_group d;
+ if (permerror (input_location, "assuming pointer to member %qD", fn)
+ && !explained)
+ {
+ inform (input_location, "(a pointer to member can only be "
+ "formed with %<&%E%>)", fn);
+ explained = 1;
+ }
}
}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index ce57716..dbc7177 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6181,7 +6181,9 @@ enum auto_deduction_context
identical to their defaults.
TFF_NO_TEMPLATE_BINDINGS: do not print information about the template
arguments for a function template specialization.
- TFF_POINTER: we are printing a pointer type. */
+ TFF_POINTER: we are printing a pointer type.
+ TFF_XOBJ_FUNC: we are printing an explicit object member function's
+ parameters. */
#define TFF_PLAIN_IDENTIFIER (0)
#define TFF_SCOPE (1)
@@ -6199,6 +6201,7 @@ enum auto_deduction_context
#define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS (1 << 12)
#define TFF_NO_TEMPLATE_BINDINGS (1 << 13)
#define TFF_POINTER (1 << 14)
+#define TFF_XOBJ_FUNC (1 << 15)
/* These constants can be used as bit flags to control strip_typedefs.
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 16ccdb3..7f26705 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -10805,24 +10805,30 @@ grokfndecl (tree ctype,
TREE_TYPE (decl) = apply_memfn_quals (TREE_TYPE (decl),
TYPE_UNQUALIFIED,
REF_QUAL_NONE);
-
+ auto_diagnostic_group d;
if (quals)
- {
- error (ctype
+ error (!ctype
+ ? G_("non-member function %qD cannot have cv-qualifier")
+ : !xobj_func_p
? G_("static member function %qD cannot have cv-qualifier")
- : G_("non-member function %qD cannot have cv-qualifier"),
- decl);
- quals = TYPE_UNQUALIFIED;
- }
-
+ : G_("explicit object member function "
+ "%qD cannot have cv-qualifier"),
+ decl);
if (rqual)
- {
- error (ctype
+ error (!ctype
+ ? G_("non-member function %qD cannot have ref-qualifier")
+ : !xobj_func_p
? G_("static member function %qD cannot have ref-qualifier")
- : G_("non-member function %qD cannot have ref-qualifier"),
+ : G_("explicit object member function "
+ "%qD cannot have ref-qualifier"),
decl);
- rqual = REF_QUAL_NONE;
- }
+
+ if (xobj_func_p && (quals || rqual))
+ inform (DECL_SOURCE_LOCATION (DECL_ARGUMENTS (decl)),
+ "explicit object parameter declared here");
+ quals = TYPE_UNQUALIFIED;
+ rqual = REF_QUAL_NONE;
+
}
if (deduction_guide_p (decl))
@@ -13317,17 +13323,85 @@ grokdeclarator (const cp_declarator *declarator,
/* There is no need to iterate over the list,
only the first parm can be a valid xobj parm. */
if (!parm_list || TREE_PURPOSE (parm_list) != this_identifier)
- return false;
+ return NULL_TREE;
/* If we make it here, we are looking at an xobj parm.
Non-null 'purpose' usually means the parm has a default
argument, we don't want to violate this assumption. */
TREE_PURPOSE (parm_list) = NULL_TREE;
- return true;
+ return TREE_VALUE (parm_list);
};
- is_xobj_member_function
+ tree xobj_parm
= find_xobj_parm (declarator->u.function.parameters);
+ is_xobj_member_function = xobj_parm;
+
+ if (xobj_parm && cxx_dialect < cxx23)
+ pedwarn (DECL_SOURCE_LOCATION (xobj_parm), OPT_Wc__23_extensions,
+ "explicit object member function only available "
+ "with %<-std=c++23%> or %<-std=gnu++23%>");
+
+ if (xobj_parm && decl_context == TYPENAME)
+ {
+ /* We inform in every case, just differently depending on what
+ case it is. */
+ auto_diagnostic_group d;
+ bool ptr_type = true;
+ /* If declarator->kind is cdk_function and we are at the end of
+ the declarator chain, we are looking at a function type. */
+ if (!declarator->declarator)
+ {
+ error_at (DECL_SOURCE_LOCATION (xobj_parm),
+ "a function type cannot "
+ "have an explicit object parameter");
+ ptr_type = false;
+ }
+ else if (declarator->declarator->kind == cdk_pointer)
+ error_at (DECL_SOURCE_LOCATION (xobj_parm),
+ "a pointer to function type cannot "
+ "have an explicit object parameter");
+ else if (declarator->declarator->kind == cdk_ptrmem)
+ error_at (DECL_SOURCE_LOCATION (xobj_parm),
+ "a pointer to member function type "
+ "cannot have an explicit object parameter");
+ else
+ gcc_unreachable ();
+
+ /* The locations being used here are probably not correct. */
+ if (ptr_type)
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "the type of a pointer to explicit object member "
+ "function is a regular pointer to function type");
+ else
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "the type of an explicit object "
+ "member function is a regular function type");
+ /* Ideally we should synthesize the correct syntax
+ for the user, perhaps this could be added later. */
+ }
+ /* Since a valid xobj parm has its purpose cleared in find_xobj_parm
+ the first parm node will never erroneously be detected here. */
+ {
+ auto_diagnostic_group d;
+ bool bad_xobj_parm_encountered = false;
+ for (tree parm = declarator->u.function.parameters;
+ parm && parm != void_list_node;
+ parm = TREE_CHAIN (parm))
+ {
+ if (TREE_PURPOSE (parm) != this_identifier)
+ continue;
+ bad_xobj_parm_encountered = true;
+ gcc_rich_location bad_xobj_parm
+ (DECL_SOURCE_LOCATION (TREE_VALUE (parm)));
+ error_at (&bad_xobj_parm,
+ "Only the first parameter of a member function "
+ "can be declared as an explicit object parameter");
+ }
+ if (bad_xobj_parm_encountered && xobj_parm)
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "Valid explicit object parameter declared here");
+ }
+
if (reqs)
error_at (location_of (reqs), "requires-clause on return type");
reqs = declarator->u.function.requires_clause;
@@ -13615,6 +13689,38 @@ grokdeclarator (const cp_declarator *declarator,
explicitp = 2;
}
+ if (xobj_parm)
+ {
+ if (!ctype
+ && decl_context == NORMAL
+ && (in_namespace
+ || !declarator->declarator->u.id.qualifying_scope))
+ error_at (DECL_SOURCE_LOCATION (xobj_parm),
+ "a non-member function cannot have "
+ "an explicit object parameter");
+ else
+ {
+ if (virtualp)
+ {
+ auto_diagnostic_group d;
+ error_at (declspecs->locations[ds_virtual],
+ "an explicit object member function cannot "
+ "be %<virtual%>");
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "explicit object parameter declared here");
+ virtualp = false;
+ }
+ if (staticp >= 2)
+ {
+ auto_diagnostic_group d;
+ error_at (declspecs->locations[ds_storage_class],
+ "an explicit object member function cannot "
+ "be %<static%>");
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "explicit object parameter declared here");
+ }
+ }
+ }
tree pushed_scope = NULL_TREE;
if (funcdecl_p
&& decl_context != FIELD
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index a384f62..52e24fb 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -840,10 +840,14 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
{
/* A lambda's "type" is essentially its signature. */
pp_string (pp, M_("<lambda"));
- if (lambda_function (t))
- dump_parameters (pp,
- FUNCTION_FIRST_USER_PARMTYPE (lambda_function (t)),
- flags);
+ tree const fn = lambda_function (t);
+ if (fn)
+ {
+ int const parm_flags
+ = DECL_XOBJ_MEMBER_FUNCTION_P (fn) ? TFF_XOBJ_FUNC | flags
+ : flags;
+ dump_parameters (pp, FUNCTION_FIRST_USER_PARMTYPE (fn), parm_flags);
+ }
pp_greater (pp);
}
else if (!decl || IDENTIFIER_ANON_P (DECL_NAME (decl)))
@@ -1712,7 +1716,9 @@ dump_lambda_function (cxx_pretty_printer *pp,
{
/* A lambda's signature is essentially its "type". */
dump_type (pp, DECL_CONTEXT (fn), flags);
- if (TREE_CODE (TREE_TYPE (fn)) == FUNCTION_TYPE)
+ if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ /* Early escape. */;
+ else if (TREE_CODE (TREE_TYPE (fn)) == FUNCTION_TYPE)
{
pp->padding = pp_before;
pp_c_ws_string (pp, "static");
@@ -1833,7 +1839,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
{
- dump_parameters (pp, parmtypes, flags);
+ int const parm_flags
+ = DECL_XOBJ_MEMBER_FUNCTION_P (t) ? TFF_XOBJ_FUNC | flags : flags;
+ dump_parameters (pp, parmtypes, parm_flags);
if (TREE_CODE (fntype) == METHOD_TYPE)
{
@@ -1912,6 +1920,8 @@ dump_parameters (cxx_pretty_printer *pp, tree parmtypes, int flags)
for (first = 1; parmtypes != void_list_node;
parmtypes = TREE_CHAIN (parmtypes))
{
+ if (first && flags & TFF_XOBJ_FUNC)
+ pp_string (pp, "this ");
if (!first)
pp_separate_with_comma (pp);
first = 0;
@@ -3696,6 +3706,8 @@ function_category (tree fn)
return _("In destructor %qD");
else if (LAMBDA_FUNCTION_P (fn))
return _("In lambda function");
+ else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ return _("In explicit object member function %qD");
else
return _("In member function %qD");
}
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 046c48d..b0c08cf 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -16188,6 +16188,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
/* Assume no class or enumeration type is declared. */
*declares_class_or_enum = 0;
+ /* Keep a token that additionally will be used for diagnostics. */
+ cp_token *first_specifier = NULL;
/* Keep reading specifiers until there are no more to read. */
while (true)
{
@@ -16260,12 +16262,32 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
decl_specs->locations[ds_attribute] = token->location;
continue;
}
+ /* We know by this point that the token is not part of an attribute. */
+ if (!first_specifier)
+ first_specifier = token;
/* Special case for "this" specifier, indicating a parm is an xobj parm.
The "this" specifier must be the first specifier in the declaration,
after any attributes. */
if (token->keyword == RID_THIS)
{
cp_lexer_consume_token (parser->lexer);
+ if (token != first_specifier)
+ {
+ /* Don't emit diagnostics if we have already seen "this",
+ leave it for set_and_check_decl_spec_loc. */
+ if (decl_specs->locations[ds_this] == 0)
+ {
+ auto_diagnostic_group d;
+ gcc_rich_location richloc (token->location);
+ /* Works, need to add tests for it though. */
+ richloc.add_fixit_remove ();
+ richloc.add_fixit_insert_before (first_specifier->location,
+ "this ");
+ error_at (&richloc,
+ "%<this%> must be the first specifier "
+ "in a parameter declaration");
+ }
+ }
set_and_check_decl_spec_loc (decl_specs, ds_this, token);
continue;
}
@@ -25657,12 +25679,14 @@ cp_parser_parameter_declaration (cp_parser *parser,
/* The restriction on defining new types applies only to the type
of the parameter, not to the default argument. */
parser->type_definition_forbidden_message = saved_message;
-
+ cp_token *eq_token = NULL;
/* If the next token is `=', then process a default argument. */
if (cp_lexer_next_token_is (parser->lexer, CPP_EQ))
{
tree type = decl_specifiers.type;
token = cp_lexer_peek_token (parser->lexer);
+ /* Used for diagnostics with an xobj parameter. */
+ eq_token = token;
if (declarator)
declarator->init_loc = token->location;
/* If we are defining a class, then the tokens that make up the
@@ -25733,6 +25757,18 @@ cp_parser_parameter_declaration (cp_parser *parser,
if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_this))
{
+ if (default_argument)
+ {
+ /* If there is a default_argument, eq_token should always be set. */
+ gcc_assert (eq_token);
+ location_t param_with_init_loc
+ = make_location (eq_token->location,
+ decl_spec_token_start->location,
+ input_location);
+ error_at (param_with_init_loc,
+ "an explicit object parameter "
+ "may not have a default argument");
+ }
/* Xobj parameters can not have default arguments, thus
we can reuse the default argument field to flag the param as such. */
default_argument = this_identifier;
diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc
index f7be4a1..dc76714 100644
--- a/gcc/cp/search.cc
+++ b/gcc/cp/search.cc
@@ -2224,10 +2224,13 @@ look_for_overrides_here (tree type, tree fndecl)
/* Not a virtual. */;
else if (DECL_CONTEXT (fn) != type)
/* Introduced with a using declaration. */;
- else if (DECL_STATIC_FUNCTION_P (fndecl))
+ else if (DECL_STATIC_FUNCTION_P (fndecl)
+ || DECL_XOBJ_MEMBER_FUNCTION_P (fndecl))
{
tree btypes = TYPE_ARG_TYPES (TREE_TYPE (fn));
tree dtypes = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+ dtypes = DECL_XOBJ_MEMBER_FUNCTION_P (fndecl) ? TREE_CHAIN (dtypes)
+ : dtypes;
if (compparms (TREE_CHAIN (btypes), dtypes))
return fn;
}
@@ -2255,6 +2258,15 @@ look_for_overrides_r (tree type, tree fndecl)
error ("%q+#D cannot be declared", fndecl);
error (" since %q+#D declared in base class", fn);
}
+ else if (DECL_XOBJ_MEMBER_FUNCTION_P (fndecl))
+ {
+ auto_diagnostic_group d;
+ error_at (DECL_SOURCE_LOCATION (fndecl),
+ "explicit object member function "
+ "overrides virtual function");
+ inform (DECL_SOURCE_LOCATION (fn),
+ "virtual function declared here");
+ }
else
{
/* It's definitely virtual, even if not explicitly set. */
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 8ff6962..3253cee 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -3169,7 +3169,30 @@ finish_this_expr (void)
return rvalue (result);
tree fn = current_nonlambda_function ();
- if (fn && DECL_STATIC_FUNCTION_P (fn))
+ if (fn && DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ {
+ auto_diagnostic_group d;
+ error ("%<this%> is unavailable for explicit object member "
+ "functions");
+ tree xobj_parm = DECL_ARGUMENTS (fn);
+ gcc_assert (xobj_parm);
+ tree parm_name = DECL_NAME (xobj_parm);
+
+ static tree remembered_fn = NULL_TREE;
+ /* Only output this diagnostic once per function. */
+ if (remembered_fn == fn)
+ /* Early escape. */;
+ else if (parm_name)
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "use explicit object parameter %qs instead",
+ IDENTIFIER_POINTER (parm_name));
+ else
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "name the explicit object parameter");
+
+ remembered_fn = fn;
+ }
+ else if (fn && DECL_STATIC_FUNCTION_P (fn))
error ("%<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");
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 7e66913..a15eda3 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -7129,26 +7129,47 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
tree fn = get_first_fn (TREE_OPERAND (arg, 1));
if (!mark_used (fn, complain) && !(complain & tf_error))
return error_mark_node;
-
- if (! flag_ms_extensions)
+ /* Until microsoft headers are known to incorrectly take the address of
+ unqualified xobj member functions we should not support this
+ extension.
+ See comment in class.cc:resolve_address_of_overloaded_function for
+ the extended reasoning. */
+ if (!flag_ms_extensions || DECL_XOBJ_MEMBER_FUNCTION_P (fn))
{
+ auto_diagnostic_group d;
tree name = DECL_NAME (fn);
if (!(complain & tf_error))
return error_mark_node;
else if (current_class_type
&& TREE_OPERAND (arg, 0) == current_class_ref)
/* An expression like &memfn. */
- permerror (loc,
- "ISO C++ forbids taking the address of an unqualified"
- " or parenthesized non-static member function to form"
- " a pointer to member function. Say %<&%T::%D%>",
- base, name);
+ if (!DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ permerror (loc,
+ "ISO C++ forbids taking the address of an unqualified"
+ " or parenthesized non-static member function to form"
+ " a pointer to member function. Say %<&%T::%D%>",
+ base, name);
+ else
+ error_at (loc,
+ "ISO C++ forbids taking the address of an unqualified"
+ " or parenthesized non-static member function to form"
+ " a pointer to explicit object member function");
else
- permerror (loc,
- "ISO C++ forbids taking the address of a bound member"
- " function to form a pointer to member function."
- " Say %<&%T::%D%>",
- base, name);
+ if (!DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ permerror (loc,
+ "ISO C++ forbids taking the address of a bound member"
+ " function to form a pointer to member function."
+ " Say %<&%T::%D%>",
+ base, name);
+ else
+ error_at (loc,
+ "ISO C++ forbids taking the address of a bound member"
+ " function to form a pointer to explicit object member"
+ " function");
+ if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ inform (loc,
+ "a pointer to explicit object member function can only be "
+ "formed with %<&%T::%D%>", base, name);
}
arg = build_offset_ref (base, fn, /*address_p=*/true, complain);
}