c++: 'this' injection and static member functions [PR97399]
authorPatrick Palka <ppalka@redhat.com>
Sat, 23 Jan 2021 05:24:17 +0000 (00:24 -0500)
committerPatrick Palka <ppalka@redhat.com>
Sat, 23 Jan 2021 05:24:17 +0000 (00:24 -0500)
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.

gcc/cp/cp-tree.h
gcc/cp/parser.c
gcc/cp/search.c
gcc/cp/semantics.c
gcc/testsuite/g++.dg/cpp0x/this2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/pr97399.C [new file with mode: 0644]

index 51139f4a4be8c11fa4d6f3883e34c05b74480098..3046c8386305924522a3b6bd3c29930a11b0207a 100644 (file)
@@ -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);
 
index e0208d02bdc2c0f0a1f583b6aa771adc2cfe97ae..e196db141139bbd661c62379cfe0d00d793438de 100644 (file)
@@ -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);
 
index dd3773da4f79ee44557368c97d324ea4d28c9474..81bdd45e40ddc5541304e840df56ca757b9ebc7a 100644 (file)
@@ -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
index 067095276afdd3c9220cd000f62598efacfcded4..51841dcf24a0a83400a7e1c5095005e0ec6ffe74 100644 (file)
@@ -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 (file)
index 0000000..ccc2608
--- /dev/null
@@ -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 (file)
index 0000000..4bb8189
--- /dev/null
@@ -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);
+}