diff options
author | Patrick Palka <ppalka@redhat.com> | 2021-01-23 00:24:17 -0500 |
---|---|---|
committer | Patrick Palka <ppalka@redhat.com> | 2021-01-23 00:24:17 -0500 |
commit | a8cef3cba6945730c69e15dcdad726e74b50fe58 (patch) | |
tree | 3917c7c5a471207cb6f2deeec15c3e4a4608fdc3 | |
parent | eb9883c1312c3801e5c25e763729d011343b22c3 (diff) | |
download | gcc-a8cef3cba6945730c69e15dcdad726e74b50fe58.zip gcc-a8cef3cba6945730c69e15dcdad726e74b50fe58.tar.gz gcc-a8cef3cba6945730c69e15dcdad726e74b50fe58.tar.bz2 |
c++: 'this' injection and static member functions [PR97399]
In the testcase pr97399.C below, finish_qualified_id_expr at parse time
adds an implicit 'this->' to the expression tmp::integral<T> (because
it's type-dependent, and also current_class_ptr is set at this point)
within the trailing return type. Later when substituting into this
trailing return type we crash because we can't resolve the 'this', since
tsubst_function_type does inject_this_parm only for non-static member
functions, which tmp::func is not.
This patch fixes this issue by removing the type-dependence check
in finish_qualified_id_expr added by r9-5972, and instead relaxes
shared_member_p to handle dependent USING_DECLs:
> I think I was wrong in my assertion around Alex's patch that
> shared_member_p should abort on a dependent USING_DECL; it now seems
> appropriate for it to return false if we don't know, we just need to
> adjust the comment to say that.
And when parsing a friend function declaration, we shouldn't be setting
current_class_ptr at all, so this patch additionally suppresses
inject_this_parm in this case.
Finally, the self-contained change to cp_parser_init_declarator is so
that we properly communicate static-ness to cp_parser_direct_declarator
when parsing a member function template. This lets us reject the
explicit use of 'this' in the testcase this2.C below.
gcc/cp/ChangeLog:
PR c++/97399
* cp-tree.h (shared_member_p): Adjust declaration.
* parser.c (cp_parser_init_declarator): If the storage class
specifier is sc_static, pass true for static_p to
cp_parser_declarator.
(cp_parser_direct_declarator): Don't do inject_this_parm when
the declarator is a friend.
* search.c (shared_member_p): Change return type to bool and
adjust function body accordingly. Return false for a dependent
USING_DECL instead of aborting.
* semantics.c (finish_qualified_id_expr): Rely on shared_member_p
even when type-dependent.
gcc/testsuite/ChangeLog:
PR c++/88548
PR c++/97399
* g++.dg/cpp0x/this2.C: New test.
* g++.dg/template/pr97399.C: New test.
-rw-r--r-- | gcc/cp/cp-tree.h | 2 | ||||
-rw-r--r-- | gcc/cp/parser.c | 5 | ||||
-rw-r--r-- | gcc/cp/search.c | 20 | ||||
-rw-r--r-- | gcc/cp/semantics.c | 3 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp0x/this2.C | 8 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/template/pr97399.C | 23 |
6 files changed, 47 insertions, 14 deletions
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 51139f4..3046c83 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7312,7 +7312,7 @@ extern tree adjust_result_of_qualified_name_lookup (tree, tree, tree); extern tree copied_binfo (tree, tree); extern tree original_binfo (tree, tree); -extern int shared_member_p (tree); +extern bool shared_member_p (tree); extern bool any_dependent_bases_p (tree = current_nonlambda_class_type ()); extern bool maybe_check_overriding_exception_spec (tree, tree); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index e0208d0..e196db1 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -21413,6 +21413,7 @@ cp_parser_init_declarator (cp_parser* parser, bool is_non_constant_init; int ctor_dtor_or_conv_p; bool friend_p = cp_parser_friend_p (decl_specifiers); + bool static_p = decl_specifiers->storage_class == sc_static; tree pushed_scope = NULL_TREE; bool range_for_decl_p = false; bool saved_default_arg_ok_p = parser->default_arg_ok_p; @@ -21446,7 +21447,7 @@ cp_parser_init_declarator (cp_parser* parser, = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED, flags, &ctor_dtor_or_conv_p, /*parenthesized_p=*/NULL, - member_p, friend_p, /*static_p=*/false); + member_p, friend_p, static_p); /* Gather up the deferred checks. */ stop_deferring_access_checks (); @@ -22122,7 +22123,7 @@ cp_parser_direct_declarator (cp_parser* parser, tree save_ccp = current_class_ptr; tree save_ccr = current_class_ref; - if (memfn) + if (memfn && !friend_p) /* DR 1207: 'this' is in scope after the cv-quals. */ inject_this_parameter (current_class_type, cv_quals); diff --git a/gcc/cp/search.c b/gcc/cp/search.c index dd3773d..81bdd45 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -910,7 +910,7 @@ struct lookup_field_info { const char *errstr; }; -/* Nonzero for a class member means that it is shared between all objects +/* True for a class member means that it is shared between all objects of that class. [class.member.lookup]:If the resulting set of declarations are not all @@ -920,25 +920,27 @@ struct lookup_field_info { This function checks that T contains no non-static members. */ -int +bool shared_member_p (tree t) { - if (VAR_P (t) || TREE_CODE (t) == TYPE_DECL \ + if (VAR_P (t) || TREE_CODE (t) == TYPE_DECL || TREE_CODE (t) == CONST_DECL) - return 1; + return true; if (is_overloaded_fn (t)) { for (ovl_iterator iter (get_fns (t)); iter; ++iter) { tree decl = strip_using_decl (*iter); - /* We don't expect or support dependent decls. */ - gcc_assert (TREE_CODE (decl) != USING_DECL); + if (TREE_CODE (decl) == USING_DECL) + /* Conservatively assume a dependent using-declaration + might resolve to a non-static member. */ + return false; if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)) - return 0; + return false; } - return 1; + return true; } - return 0; + return false; } /* Routine to see if the sub-object denoted by the binfo PARENT can be diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 0670952..51841dc 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -2214,8 +2214,7 @@ finish_qualified_id_expr (tree qualifying_class, { /* See if any of the functions are non-static members. */ /* If so, the expression may be relative to 'this'. */ - if ((type_dependent_expression_p (expr) - || !shared_member_p (expr)) + if (!shared_member_p (expr) && current_class_ptr && DERIVED_FROM_P (qualifying_class, current_nonlambda_class_type ())) diff --git a/gcc/testsuite/g++.dg/cpp0x/this2.C b/gcc/testsuite/g++.dg/cpp0x/this2.C new file mode 100644 index 0000000..ccc2608 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/this2.C @@ -0,0 +1,8 @@ +// PR c++/88548 +// { dg-do compile { target c++11 } } + +struct S { + int a; + template <class> static auto m1 () + -> decltype(this->a) { return 0; } // { dg-error "'this'" } +}; diff --git a/gcc/testsuite/g++.dg/template/pr97399.C b/gcc/testsuite/g++.dg/template/pr97399.C new file mode 100644 index 0000000..4bb8189 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/pr97399.C @@ -0,0 +1,23 @@ +// PR c++/97399 +// { dg-do compile { target c++11 } } + +template <bool> struct enable_if_t {}; + +struct tmp { + template <class> static constexpr bool is_integral(); + template <class T> static auto f() + -> enable_if_t<tmp::is_integral<T>()>; + template <class T> friend auto g(tmp, T) + -> enable_if_t<!tmp::is_integral<T>()>; +}; + +template <class> constexpr bool tmp::is_integral() { return true; } + +template <class T> auto tmp::f() + -> enable_if_t<tmp::is_integral<T>()> { return {}; } + +int main() +{ + tmp::f<int>(); + g(tmp{}, 0); +} |