On Fri, Jun 15, 2018 at 4:21 PM, Jason Merrill <ja...@redhat.com> wrote:
> This testcase was broken by the lambda rewrite.  This turned out to be
> because the lambda in the template's default argument had namespace
> scope, while the instantiation had function scope, because of
> tsubst_default_argument calling push_access_scope and do_pushtag using
> the resulting value of current_function_decl.
>
> The fix is to teach do_pushtag to do better.  For GCC 8 I'm applying a
> focused patch that just recognizes when we're trying to give a lambda
> a function scope we aren't actually in.  This re-broke bug 59949, so
> to avoid that I needed to fix tsubst_lambda_expr to use and update the
> discriminator for global lambda scope when the template lambda has no
> extra scope.
>
> Looking at do_pushtag led me to wonder why we were relying on
> current_* for the scope in the first place; since we're going to add
> the tag to current_binding_level, we should get the scope from there
> as well.  Making that change revealed a few more issues:
>
> * With a lambda in a default argument, we would frequently be in the
> binding_level for the calling function.  Fixed by calling
> push_to_top_level.
> * Redeclarations of C++11 enums with specified underlying type were
> getting pushed into an arbitrary binding level.  Fixed by removing the
> unnecessary permissiveness around such redeclarations with underlying
> types that are not template-equivalent, so a redeclaration isn't
> pushed anywhere.
> * Lambdas in default member initializers were also getting pushed into
> an arbitrary binding level, due to the code for allowing type
> definition in a compound literal in a static data member initializer.
> Fixed by no longer looking through the binding_level for a complete
> class, but instead not pushing the tag anywhere if the class is
> complete; a lambda type isn't found by name anyway.

One more: looking through one class binding level then stopped at its
enclosing template parms level even if that's still inside another
class binding level.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit fa24a18a557cc0f7a7cb42416b0150a4847916c6
Author: Jason Merrill <ja...@redhat.com>
Date:   Wed Jun 27 12:25:07 2018 -0400

    Avoid crash on friend in nested class template.
    
            * name-lookup.c (do_pushtag): If we skip a class level, also skip
            its template level.

diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index a30c37428ad..e0500d83071 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -6509,20 +6509,30 @@ do_pushtag (tree name, tree type, tag_scope scope)
   tree decl;
 
   cp_binding_level *b = current_binding_level;
-  while (/* Cleanup scopes are not scopes from the point of view of
-	    the language.  */
-	 b->kind == sk_cleanup
-	 /* Neither are function parameter scopes.  */
-	 || b->kind == sk_function_parms
-	 /* Neither are the scopes used to hold template parameters
-	    for an explicit specialization.  For an ordinary template
-	    declaration, these scopes are not scopes from the point of
-	    view of the language.  */
-	 || (b->kind == sk_template_parms
-	     && (b->explicit_spec_p || scope == ts_global))
-	 || (b->kind == sk_class
-	     && scope != ts_current))
-    b = b->level_chain;
+  while (true)
+    {
+      if (/* Cleanup scopes are not scopes from the point of view of
+	     the language.  */
+	  b->kind == sk_cleanup
+	  /* Neither are function parameter scopes.  */
+	  || b->kind == sk_function_parms
+	  /* Neither are the scopes used to hold template parameters
+	     for an explicit specialization.  For an ordinary template
+	     declaration, these scopes are not scopes from the point of
+	     view of the language.  */
+	  || (b->kind == sk_template_parms
+	      && (b->explicit_spec_p || scope == ts_global)))
+	b = b->level_chain;
+      else if (b->kind == sk_class
+	       && scope != ts_current)
+	{
+	  b = b->level_chain;
+	  if (b->kind == sk_template_parms)
+	    b = b->level_chain;
+	}
+      else
+	break;
+    }
 
   gcc_assert (identifier_p (name));
 
diff --git a/gcc/testsuite/g++.dg/template/friend66.C b/gcc/testsuite/g++.dg/template/friend66.C
new file mode 100644
index 00000000000..f8c95f844b3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend66.C
@@ -0,0 +1,9 @@
+template <class T>
+struct A
+{
+  template <class U>
+  struct B
+  {
+    friend struct C;
+  };
+};
diff --git a/gcc/testsuite/g++.old-deja/g++.law/visibility13.C b/gcc/testsuite/g++.old-deja/g++.law/visibility13.C
index 025b0b1ef54..451ef1afaf8 100644
--- a/gcc/testsuite/g++.old-deja/g++.law/visibility13.C
+++ b/gcc/testsuite/g++.old-deja/g++.law/visibility13.C
@@ -15,9 +15,11 @@ using namespace std;
 
 const int ArraySize = 12;
 
+template <class> class Array_RC;
+
 template <class Type>
-class Array { // { dg-error "" } .struct Array_RC redecl.*
-friend class Array_RC;
+class Array {
+  friend class Array_RC<Type>;
 public:
     Array(const Type *ar, int sz) { init(ar,sz); }
     virtual ~Array() { delete [] ia; }
@@ -76,8 +78,8 @@ Array_RC<Type>::Array_RC(const Type *ar, int sz) : Array<Type>(ar, sz) {}
 
 template <class Type>
 Type &Array_RC<Type>::operator[](int ix) {
-    assert(ix >= 0 && ix < size);// { dg-error "" } member .size.*
-    return ia[ix];// { dg-error "" } member .ia.*
+    assert(ix >= 0 && ix < this->size);
+    return this->ia[ix];
 }
 
 //    -------------------   Test routine   ----------------------

Reply via email to