diff mbox

Fix PR c++/60573

Message ID 1395197167-25367-1-git-send-email-adam@jessamine.co.uk
State New
Headers show

Commit Message

Adam Butcher March 19, 2014, 2:46 a.m. UTC
PR c++/60573
	* parser.c (synthesize_implicit_template_parm): Handle the fact that
	nested class member declarations erroneously appearing in an enclosing
	class contain an addition scope level for the class being defined.

	PR c++/60573
	* g++.dg/cpp1y/pr60573.C: New testcase.
---
 gcc/cp/parser.c                      | 18 ++++++++++++++++--
 gcc/testsuite/g++.dg/cpp1y/pr60573.C | 13 +++++++++++++
 2 files changed, 29 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr60573.C

Comments

Jason Merrill March 24, 2014, 5:23 p.m. UTC | #1
On 03/18/2014 10:46 PM, Adam Butcher wrote:
> -	  while (scope->kind == sk_class
> -		 && !TYPE_BEING_DEFINED (scope->this_entity))

Does it work to just change TYPE_BEING_DEFINED to currently_open_class?

Jason
Adam Butcher March 24, 2014, 8:13 p.m. UTC | #2
On 2014-03-24 17:23, Jason Merrill wrote:
> On 03/18/2014 10:46 PM, Adam Butcher wrote:
>> -	  while (scope->kind == sk_class
>> -		 && !TYPE_BEING_DEFINED (scope->this_entity))
>
> Does it work to just change TYPE_BEING_DEFINED to 
> currently_open_class?
>
No.  The object referred to by 'scope->this_entity' is the same for 
each of the two levels, so unless there's something else I can pass to 
'currently_open_class', the result will be equivalent; the issue is that 
the class being referred to *is* being defined, but I need to skip over 
the second class-scope for that type to arrive at the defining scope 
level.

Adam
Jason Merrill March 25, 2014, 3:48 p.m. UTC | #3
On 03/18/2014 10:46 PM, Adam Butcher wrote:
> +	      if (TYPE_BEING_DEFINED (scope->this_entity))
> +		if (scope->level_chain == 0
> +		    || scope->this_entity != scope->level_chain->this_entity)
> +		  break;

I don't think this is an adequate test; if you have another class 
wrapping B, you'll have two levels of context pushed for the declarator, 
so the this_entities will compare unequal.  I think we need some way to 
designate a scope that actually corresponds to a class-specifier.

Jason
Adam Butcher March 25, 2014, 7:48 p.m. UTC | #4
On 2014-03-25 15:48, Jason Merrill wrote:
> On 03/18/2014 10:46 PM, Adam Butcher wrote:
>> +	      if (TYPE_BEING_DEFINED (scope->this_entity))
>> +		if (scope->level_chain == 0
>> +		    || scope->this_entity != scope->level_chain->this_entity)
>> +		  break;
>
> I don't think this is an adequate test; if you have another class
> wrapping B, you'll have two levels of context pushed for the
> declarator, so the this_entities will compare unequal.  I think we
> need some way to designate a scope that actually corresponds to a
> class-specifier.
>
I don't follow.  Are you suggesting a case like the following?

   struct A
   {
     struct X
     {
       struct B
       {
         void foo(auto);
       };
     };

     void X::B::foo(auto) {}  // { dg-error "cannot define" }
   };

If so, it is handled.  The code you are referring to is within a 
level_chain traversal loop so handles arbitrary levels of pushed 
context.  The end result is the same though; we arrive at an 'A::' scope 
that is not the defining class scope but has the same 'this_entity' as 
it (so TYPE_BEING_DEFINED returns true).  The scope chain is as if the 
following was used to declare it:

   struct A
   {
     ...
     void A::X::B::foo(auto);
   };

It is the two levels of 'A' entity scopes that caused the ICE due the 
previous version of the loop stopping to inject the template parameter 
list between the 'A::' and the 'X::', rather than unwinding back to the 
scope defining 'A'.

Apologies if I've completely misunderstood your point here.  I've got a 
feeling that I may have.

Adam
Jason Merrill March 26, 2014, 3:17 p.m. UTC | #5
On 03/25/2014 03:48 PM, Adam Butcher wrote:
> I don't follow.  Are you suggesting a case like the following?
>
>    struct A
>    {
>      struct X
>      {
>        struct B
>        {
>          void foo(auto);
>        };
>      };
>
>      void X::B::foo(auto) {}  // { dg-error "cannot define" }
>    };

I meant

   struct A
   {
     struct X
     {
       struct B
       {
         void foo(auto);
       };

       void B::foo(auto) {}  // { dg-error "cannot define" }
     };
   };

Here we push both A and X for the declarator.  When we get to the pushed 
X, we see that the enclosing scope is A, so we break out of the loop and 
don't pop either of the pushed scopes.

Jason
Adam Butcher March 26, 2014, 6:36 p.m. UTC | #6
On 2014-03-26 15:17, Jason Merrill wrote:
>
> I meant
>
>   struct A
>   {
>     struct X
>     {
>       struct B
>       {
>         void foo(auto);
>       };
>
>       void B::foo(auto) {}  // { dg-error "cannot define" }
>     };
>   };
>
> Here we push both A and X for the declarator.  When we get to the
> pushed X, we see that the enclosing scope is A, so we break out of 
> the
> loop and don't pop either of the pushed scopes.
>
Thought I was probably being dense!  :)  Yes, that will be broken with 
the current patch.  Continuing the loop based on TYPE_BEING_DEFINED 
might do the trick.  I'll try to have a look later.

Cheers,
Adam
Adam Butcher March 26, 2014, 8:29 p.m. UTC | #7
On 2014-03-25 15:48, Jason Merrill wrote:
>
> I think we need some way to designate a scope that actually
> corresponds to a class-specifier.
>
Agreed.  I'll look into it.

Adam
diff mbox

Patch

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 46e2453..36872c9 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -32006,9 +32006,23 @@  synthesize_implicit_template_parm  (cp_parser *parser)
 	    must be injected in the scope of 'B', just beyond the scope of 'A'
 	    introduced by 'A::'.  */
 
-	  while (scope->kind == sk_class
-		 && !TYPE_BEING_DEFINED (scope->this_entity))
+	  while (scope->kind == sk_class)
 	    {
+	      /* In valid cases where the class being defined is reached, we're
+		 at the point where the template argument list should be
+		 injected for a generic member function.  In the erroneous case
+		 of generic member function of a nested class being declared in
+		 the enclosing class, an additional class scope for the
+		 enclosing class has been pushed by push_nested_class via
+		 push_scope in cp_parser_direct_declarator.  This additional
+		 scope needs to be skipped to reach the class definition scope
+		 where the template argument list should be injected.  */
+
+	      if (TYPE_BEING_DEFINED (scope->this_entity))
+		if (scope->level_chain == 0
+		    || scope->this_entity != scope->level_chain->this_entity)
+		  break;
+
 	      parent_scope = scope;
 	      scope = scope->level_chain;
 	    }
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr60573.C b/gcc/testsuite/g++.dg/cpp1y/pr60573.C
new file mode 100644
index 0000000..7f56ff4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/pr60573.C
@@ -0,0 +1,13 @@ 
+// 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" }
+};