[PR c++/59930] template friend injection
authorNathan Sidwell <nathan@acm.org>
Mon, 18 Dec 2017 16:30:58 +0000 (16:30 +0000)
committerNathan Sidwell <nathan@gcc.gnu.org>
Mon, 18 Dec 2017 16:30:58 +0000 (16:30 +0000)
https://gcc.gnu.org/ml/gcc-patches/2017-12/msg01168.html
PR c++/59930
* name-lookup.c (name_lookup::search_unqualified): Don't search
parent namespace when looking for hidden things.
* pt.c (tsubst_friend_class): Always push to friend scope, drop
unneeded self-friend check. Inject new hidden friend into correct
scope.

PR c++/59930
* g++.dg/parse/pr81247-c.C: Adjust.
* g++.dg/template/pr59930-[123].C: New.

From-SVN: r255780

gcc/cp/ChangeLog
gcc/cp/name-lookup.c
gcc/cp/pt.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/parse/pr81247-c.C
gcc/testsuite/g++.dg/template/pr59930-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/pr59930-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/pr59930-3.C [new file with mode: 0644]

index 5c281004ef63e51a2536337fd8183154536fb0a4..3885e566d0c7ddf3991c6eb0c6692e1d9a25dced 100644 (file)
@@ -1,3 +1,12 @@
+2017-12-18  Nathan Sidwell  <nathan@acm.org>
+
+       PR c++/59930
+       * name-lookup.c (name_lookup::search_unqualified): Don't search
+       parent namespace when looking for hidden things.
+       * pt.c (tsubst_friend_class): Always push to friend scope, drop
+       unneeded self-friend check. Inject new hidden friend into correct
+       scope.
+
 2017-12-15  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/83205
index 97d0632301d210b3f877d37732cf73cfdf6832e0..978ff77ae751ab700ee3e9935d7779c542c40762 100644 (file)
@@ -711,6 +711,15 @@ name_lookup::search_unqualified (tree scope, cp_binding_level *level)
     done:;
       if (scope == global_namespace)
        break;
+
+      /* If looking for hidden names, we only look in the innermost
+        namespace scope.  [namespace.memdef]/3 If a friend
+        declaration in a non-local class first declares a class,
+        function, class template or function template the friend is a
+        member of the innermost enclosing namespace.  See also
+        [basic.lookup.unqual]/7 */
+      if (flags & LOOKUP_HIDDEN)
+       break;
     }
 
   vec_safe_truncate (queue, length);
index dc50f31034b3015ebc1941612b8f632fad54e5af..4b42f03c27f9fcf2d2ef186842e95691d165068b 100644 (file)
@@ -10005,57 +10005,23 @@ tsubst_friend_function (tree decl, tree args)
 static tree
 tsubst_friend_class (tree friend_tmpl, tree args)
 {
-  tree friend_type;
   tree tmpl;
-  tree context;
 
   if (DECL_TEMPLATE_TEMPLATE_PARM_P (friend_tmpl))
     {
-      tree t = tsubst (TREE_TYPE (friend_tmpl), args, tf_none, NULL_TREE);
-      return TREE_TYPE (t);
-    }
-
-  context = CP_DECL_CONTEXT (friend_tmpl);
-
-  if (context != global_namespace)
-    {
-      if (TREE_CODE (context) == NAMESPACE_DECL)
-       push_nested_namespace (context);
-      else
-       push_nested_class (tsubst (context, args, tf_none, NULL_TREE));
+      tmpl = tsubst (TREE_TYPE (friend_tmpl), args, tf_none, NULL_TREE);
+      return TREE_TYPE (tmpl);
     }
 
-  /* Look for a class template declaration.  We look for hidden names
-     because two friend declarations of the same template are the
-     same.  For example, in:
-
-       struct A { 
-         template <typename> friend class F;
-       };
-       template <typename> struct B { 
-         template <typename> friend class F;
-       };
-
-     both F templates are the same.  */
-  tmpl = lookup_name_real (DECL_NAME (friend_tmpl), 0, 0,
-                          /*block_p=*/true, 0, LOOKUP_HIDDEN);
-
-  /* But, if we don't find one, it might be because we're in a
-     situation like this:
-
-       template <class T>
-       struct S {
-        template <class U>
-        friend struct S;
-       };
+  tree context = CP_DECL_CONTEXT (friend_tmpl);
+  if (TREE_CODE (context) == NAMESPACE_DECL)
+    push_nested_namespace (context);
+  else
+    push_nested_class (context);
 
-     Here, in the scope of (say) S<int>, `S' is bound to a TYPE_DECL
-     for `S<int>', not the TEMPLATE_DECL.  */
-  if (!tmpl || !DECL_CLASS_TEMPLATE_P (tmpl))
-    {
-      tmpl = lookup_name_prefer_type (DECL_NAME (friend_tmpl), 1);
-      tmpl = maybe_get_template_decl_from_type_decl (tmpl);
-    }
+  tmpl = lookup_name_real (DECL_NAME (friend_tmpl), /*prefer_type=*/false,
+                          /*non_class=*/false, /*block_p=*/false,
+                          /*namespaces_only=*/false, LOOKUP_HIDDEN);
 
   if (tmpl && DECL_CLASS_TEMPLATE_P (tmpl))
     {
@@ -10068,53 +10034,50 @@ tsubst_friend_class (tree friend_tmpl, tree args)
       if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl))
          > TMPL_ARGS_DEPTH (args))
        {
-         tree parms;
-          location_t saved_input_location;
-         parms = tsubst_template_parms (DECL_TEMPLATE_PARMS (friend_tmpl),
-                                        args, tf_warning_or_error);
-
-          saved_input_location = input_location;
+         tree parms = tsubst_template_parms (DECL_TEMPLATE_PARMS (friend_tmpl),
+                                             args, tf_warning_or_error);
+          location_t saved_input_location = input_location;
           input_location = DECL_SOURCE_LOCATION (friend_tmpl);
           tree cons = get_constraints (tmpl);
           redeclare_class_template (TREE_TYPE (tmpl), parms, cons);
           input_location = saved_input_location;
-          
        }
-
-      friend_type = TREE_TYPE (tmpl);
     }
   else
     {
       /* The friend template has not already been declared.  In this
         case, the instantiation of the template class will cause the
-        injection of this template into the global scope.  */
+        injection of this template into the namespace scope.  */
       tmpl = tsubst (friend_tmpl, args, tf_warning_or_error, NULL_TREE);
-      if (tmpl == error_mark_node)
-       return error_mark_node;
 
-      /* The new TMPL is not an instantiation of anything, so we
-        forget its origins.  We don't reset CLASSTYPE_TI_TEMPLATE for
-        the new type because that is supposed to be the corresponding
-        template decl, i.e., TMPL.  */
-      DECL_USE_TEMPLATE (tmpl) = 0;
-      DECL_TEMPLATE_INFO (tmpl) = NULL_TREE;
-      CLASSTYPE_USE_TEMPLATE (TREE_TYPE (tmpl)) = 0;
-      CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl))
-       = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl)));
+      if (tmpl != error_mark_node)
+       {
+         /* The new TMPL is not an instantiation of anything, so we
+            forget its origins.  We don't reset CLASSTYPE_TI_TEMPLATE
+            for the new type because that is supposed to be the
+            corresponding template decl, i.e., TMPL.  */
+         DECL_USE_TEMPLATE (tmpl) = 0;
+         DECL_TEMPLATE_INFO (tmpl) = NULL_TREE;
+         CLASSTYPE_USE_TEMPLATE (TREE_TYPE (tmpl)) = 0;
+         CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl))
+           = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl)));
+
+         /* It is hidden.  */
+         retrofit_lang_decl (DECL_TEMPLATE_RESULT (tmpl));
+         DECL_ANTICIPATED (tmpl)
+           = DECL_ANTICIPATED (DECL_TEMPLATE_RESULT (tmpl)) = true;
 
-      /* Inject this template into the global scope.  */
-      friend_type = TREE_TYPE (pushdecl_top_level (tmpl, true));
+         /* Inject this template into the enclosing namspace scope.  */
+         tmpl = pushdecl_namespace_level (tmpl, true);
+       }
     }
 
-  if (context != global_namespace)
-    {
-      if (TREE_CODE (context) == NAMESPACE_DECL)
-       pop_nested_namespace (context);
-      else
-       pop_nested_class ();
-    }
+  if (TREE_CODE (context) == NAMESPACE_DECL)
+    pop_nested_namespace (context);
+  else
+    pop_nested_class ();
 
-  return friend_type;
+  return TREE_TYPE (tmpl);
 }
 
 /* Returns zero if TYPE cannot be completed later due to circularity.
index c00a20f79d6e231acd247e9d2a4c15e3ea4f9b46..831a9bb11263c1596ba8450de0cb7120b7ac5594 100644 (file)
@@ -1,3 +1,9 @@
+2017-12-18  Nathan Sidwell  <nathan@acm.org>
+
+       PR c++/59930
+       * g++.dg/parse/pr81247-c.C: Adjust.
+       * g++.dg/template/pr59930-[123].C: New.
+
 2017-12-18  Claudiu Zissulescu  <claziss@synopsys.com>
 
        * gcc.target/arc/tumaddsidi4.c: New test.
index 32f41f28f4150623154466958b981c3747bb8fe3..33183c5294b2a14b7e651dae7b409a422df4ecf5 100644 (file)
@@ -1,8 +1,9 @@
-// PR c++/81247 confused error
+// PR c++/81247 rejected well-formed
 
-namespace N { // { dg-message "previous declaration" }
+namespace N {
   template < typename T > class A
-  { // { dg-error "conflicts with a previous" }
+  {
+    // injects a hidden class N::N at instantiation time
     template < T > friend class N;
   };
 }
diff --git a/gcc/testsuite/g++.dg/template/pr59930-1.C b/gcc/testsuite/g++.dg/template/pr59930-1.C
new file mode 100644 (file)
index 0000000..7a8c17d
--- /dev/null
@@ -0,0 +1,18 @@
+// PR c++/59930
+
+namespace N {
+  template<typename T> class A {
+    // The injected name is N::B, because we don;t look outside of N
+    template<typename U> friend struct B;
+  private:
+    int n; // { dg-message "declared private here" }
+  public:
+    A (int);
+  };
+}
+
+template<typename T> struct B {
+  int f(N::A<int> ai) { return ai.n; } // { dg-error "is private" }
+};
+
+int k = B<int>().f(0);
diff --git a/gcc/testsuite/g++.dg/template/pr59930-2.C b/gcc/testsuite/g++.dg/template/pr59930-2.C
new file mode 100644 (file)
index 0000000..a7e6ea4
--- /dev/null
@@ -0,0 +1,17 @@
+// PR c++/59930
+
+namespace N {
+  template < typename T > class A
+  {
+    // Injects N::N
+    template < T > friend class N;
+    // { dg-error "template parameter" "" { target *-*-* } .-1 }
+    // { dg-error "redeclared"  "" { target *-*-* } .-2 }
+  };
+}
+
+void f ()
+{
+  N::A < int > a1;
+  N::A <short > a2;
+}
diff --git a/gcc/testsuite/g++.dg/template/pr59930-3.C b/gcc/testsuite/g++.dg/template/pr59930-3.C
new file mode 100644 (file)
index 0000000..1769a3c
--- /dev/null
@@ -0,0 +1,29 @@
+// PR c++/59930
+
+namespace NS {
+  template<typename T> class Holder
+  {
+  private:
+    void func();
+
+    template<typename> friend class User;
+  };
+
+  template class Holder<long>;
+
+  template<typename T> class User
+  {
+  public:
+    void method() const
+    {
+      Holder<T> x;
+      x.func();
+    }
+  };
+} // namespace
+
+void Foo()
+{
+  NS::User<long> decl;
+  decl.method();
+}