Patchwork Fix PR c++/60573

login
register
mail settings
Submitter Adam Butcher
Date March 27, 2014, 8:45 p.m.
Message ID <1395953117-27334-1-git-send-email-adam@jessamine.co.uk>
Download mbox | patch
Permalink /patch/334462/
State New
Headers show

Comments

Adam Butcher - March 27, 2014, 8:45 p.m.
PR c++/60573
	* name-lookup.h (cp_binding_level): New field scope_defines_class_p.
	* semantics.c (begin_class_definition): Set scope_defines_class_p.
	* pt.c (instantiate_class_template_1): Likewise.
	* parser.c (synthesize_implicit_template_parm): Use cp_binding_level::
	scope_defines_class_p rather than TYPE_BEING_DEFINED as the predicate
	for unwinding to class-defining scope to handle the erroneous definition
	of a generic function of an arbitrarily nested class within an enclosing
	class.

	PR c++/60573
	* g++.dg/cpp1y/pr60573.C: New testcase.
---
 gcc/cp/name-lookup.h                 |  6 +++++-
 gcc/cp/parser.c                      | 23 +++++++++++++++++------
 gcc/cp/pt.c                          |  5 ++++-
 gcc/cp/semantics.c                   |  1 +
 gcc/testsuite/g++.dg/cpp1y/pr60573.C | 28 ++++++++++++++++++++++++++++
 5 files changed, 55 insertions(+), 8 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr60573.C
Adam Butcher - March 27, 2014, 8:56 p.m.
On 2014-03-27 20:45, Adam Butcher wrote:
> @@ -8905,9 +8905,12 @@ instantiate_class_template_1 (tree type)
>      return type;
>
>    /* Now we're really doing the instantiation.  Mark the type as in
> -     the process of being defined.  */
> +     the process of being defined...  */
>    TYPE_BEING_DEFINED (type) = 1;
>
> +  /* ... and the scope defining it.  */
> +  class_binding_level->scope_defines_class_p = 1;
>

I meant current_binding_level here; but I'm not sure it's necessary 
here at all.
Adam Butcher - March 27, 2014, 9:16 p.m.
On 2014-03-27 20:45, Adam Butcher wrote:
> PR c++/60573
> 	* name-lookup.h (cp_binding_level): New field scope_defines_class_p.
> 	* semantics.c (begin_class_definition): Set scope_defines_class_p.
> 	* pt.c (instantiate_class_template_1): Likewise.
> 	* parser.c (synthesize_implicit_template_parm): Use 
> cp_binding_level::
> 	scope_defines_class_p rather than TYPE_BEING_DEFINED as the 
> predicate
> 	for unwinding to class-defining scope to handle the erroneous 
> definition
> 	of a generic function of an arbitrarily nested class within an 
> enclosing
> 	class.
>
Still got issues with this.  It fails on out-of-line defs.  I'll have 
another look.

Patch

diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index a63442f..9e5d812 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -255,7 +255,11 @@  struct GTY(()) cp_binding_level {
   unsigned more_cleanups_ok : 1;
   unsigned have_cleanups : 1;
 
-  /* 24 bits left to fill a 32-bit word.  */
+  /* Set if this scope is of sk_class kind and is the defining
+     scope for this_entity.  */
+  unsigned scope_defines_class_p : 1;
+
+  /* 23 bits left to fill a 32-bit word.  */
 };
 
 /* The binding level currently in effect.  */
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index e729d65..4919a67 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -32000,7 +32000,7 @@  synthesize_implicit_template_parm  (cp_parser *parser)
 	{
 	  /* If not defining a class, then any class scope is a scope level in
 	     an out-of-line member definition.  In this case simply wind back
-	     beyond the first such scope to inject the template argument list.
+	     beyond the first such scope to inject the template parameter list.
 	     Otherwise wind back to the class being defined.  The latter can
 	     occur in class member friend declarations such as:
 
@@ -32011,12 +32011,23 @@  synthesize_implicit_template_parm  (cp_parser *parser)
 		 friend void A::foo (auto);
 	       };
 
-	    The template argument list synthesized for the friend declaration
-	    must be injected in the scope of 'B', just beyond the scope of 'A'
-	    introduced by 'A::'.  */
+	    The template parameter list synthesized for the friend declaration
+	    must be injected in the scope of 'B'.  This can also occur in
+	    erroneous cases such as:
 
-	  while (scope->kind == sk_class
-		 && !TYPE_BEING_DEFINED (scope->this_entity))
+	       struct A {
+	         struct B {
+		   void foo (auto);
+		 };
+		 void B::foo (auto) {}
+	       };
+
+	    Here the attempted definition of 'B::foo' within 'A' is ill-formed
+	    but, nevertheless, the template parameter list synthesized for the
+	    declarator should be injected into the scope of 'A' as if the
+	    ill-formed template was specified explicitly.  */
+
+	  while (scope->kind == sk_class && !scope->scope_defines_class_p)
 	    {
 	      parent_scope = scope;
 	      scope = scope->level_chain;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index c791d03..90faeec 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -8905,9 +8905,12 @@  instantiate_class_template_1 (tree type)
     return type;
 
   /* Now we're really doing the instantiation.  Mark the type as in
-     the process of being defined.  */
+     the process of being defined...  */
   TYPE_BEING_DEFINED (type) = 1;
 
+  /* ... and the scope defining it.  */
+  class_binding_level->scope_defines_class_p = 1;
+
   /* We may be in the middle of deferred access check.  Disable
      it now.  */
   push_deferring_access_checks (dk_no_deferred);
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 886fbb8..deba2ab 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2777,6 +2777,7 @@  begin_class_definition (tree t)
   maybe_process_partial_specialization (t);
   pushclass (t);
   TYPE_BEING_DEFINED (t) = 1;
+  class_binding_level->scope_defines_class_p = 1;
 
   if (flag_pack_struct)
     {
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr60573.C b/gcc/testsuite/g++.dg/cpp1y/pr60573.C
new file mode 100644
index 0000000..2f60707
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr60573.C
@@ -0,0 +1,28 @@ 
+// PR c++/60573
+// { dg-do compile { target c++1y } }
+// { dg-options "" }
+
+struct A
+{
+  struct B
+  {
+    void foo(auto);
+  };
+
+  void B::foo(auto) {}  // { dg-error "cannot define" }
+
+  struct X
+  {
+    struct Y
+    {
+      struct Z
+      {
+        void foo(auto);
+      };
+    };
+
+    void Y::Z::foo(auto) {}  // { dg-error "cannot define" }
+  };
+
+  void X::Y::Z::foo(auto) {}  // { dg-error "cannot define" }
+};