On 06/07/2012 04:29 PM, Jason Merrill wrote:
The problem here was that when we instantiate G::bar, we want to
instantiate F, and end up improperly pushing the declaration inside G,
which leads to the ICE. Adjusting the call to pushclass is enough to fix
this, which is what I propose doing for the 4.7 branch. For 4.8 I'm
going to do more.

What we really want is to get local classes declared at the same place in a template that they are in a non-template. This patch achieves that by adding a DECL_EXPR to make the class declaration explicit. This confused finish_cond, which expected a DECL_EXPR to be for a condition variable, so I needed to adjust it and tsubst_expr accordingly.

The second patch unconditionally instantiates local classes when we encounter their definition, so that we don't need to deal with trying to push back to the right scope later on--and because this seems to be what the language requires.

Tested x86_64-pc-linux-gnu, applied to trunk.
commit c9e4cdb59a541fd5006983923adfcbb406533f3a
Author: jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Tue Jun 12 18:32:04 2012 +0000

    	PR c++/53599
    	* name-lookup.c (pushtag_1): Add a DECL_EXPR for a local class.
    	* semantics.c (finish_cond): Build a COMPOUND_EXPR.
    	* pt.c (tsubst_expr) [COMPOUND_EXPR]: Handle.
    	[DECL_EXPR]: Don't call cp_finish_decl for an implicit typedef.
    	Don't return the decl.
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@188473 138bc75d-0d04-0410-961f-82ee72b054a4

diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 9cc6d39..0f28820 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -5796,7 +5796,16 @@ pushtag_1 (tree name, tree type, tag_scope scope)
 	 class.)  */
       if (TYPE_CONTEXT (type)
 	  && TREE_CODE (TYPE_CONTEXT (type)) == FUNCTION_DECL)
-	VEC_safe_push (tree, gc, local_classes, type);
+	{
+	  if (processing_template_decl)
+	    {
+	      /* Push a DECL_EXPR so we call pushtag at the right time in
+		 template instantiation rather than in some nested context.  */
+	      add_decl_expr (decl);
+	    }
+	  else
+	    VEC_safe_push (tree, gc, local_classes, type);
+	}
     }
   if (b->kind == sk_class
       && !COMPLETE_TYPE_P (current_class_type))
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index df80159..04f7be8 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -12887,6 +12887,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
 		    DECL_CONTEXT (decl) = current_function_decl;
 		    insert_capture_proxy (decl);
 		  }
+		else if (DECL_IMPLICIT_TYPEDEF_P (t))
+		  /* We already did a pushtag.  */;
 		else
 		  {
 		    int const_init = false;
@@ -12930,9 +12932,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
 	      }
 	  }
 
-	/* A DECL_EXPR can also be used as an expression, in the condition
-	   clause of an if/for/while construct.  */
-	return decl;
+	break;
       }
 
     case FOR_STMT:
@@ -13341,6 +13341,15 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       error ("use %<...%> to expand argument pack");
       return error_mark_node;
 
+    case COMPOUND_EXPR:
+      tmp = RECUR (TREE_OPERAND (t, 0));
+      if (tmp == NULL_TREE)
+	/* If the first operand was a statement, we're done with it.  */
+	return RECUR (TREE_OPERAND (t, 1));
+      return build_x_compound_expr (EXPR_LOCATION (t), tmp,
+				    RECUR (TREE_OPERAND (t, 1)),
+				    complain);
+
     default:
       gcc_assert (!STATEMENT_CODE_P (TREE_CODE (t)));
 
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 7769bba..f8ad2a5 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -509,11 +509,14 @@ finish_cond (tree *cond_p, tree expr)
   if (processing_template_decl)
     {
       tree cond = pop_stmt_list (*cond_p);
-      if (TREE_CODE (cond) == DECL_EXPR)
-	expr = cond;
 
-      if (check_for_bare_parameter_packs (expr))
-        *cond_p = error_mark_node;
+      if (expr == NULL_TREE)
+	/* Empty condition in 'for'.  */
+	gcc_assert (empty_expr_stmt_p (cond));
+      else if (check_for_bare_parameter_packs (expr))
+        expr = error_mark_node;
+      else if (!empty_expr_stmt_p (cond))
+	expr = build2 (COMPOUND_EXPR, TREE_TYPE (expr), cond, expr);
     }
   *cond_p = expr;
 }
diff --git a/gcc/testsuite/g++.dg/template/local7.C b/gcc/testsuite/g++.dg/template/local7.C
new file mode 100644
index 0000000..3045534
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/local7.C
@@ -0,0 +1,15 @@
+// PR c++/53599
+
+template <typename T>
+int foo ()
+{
+  struct F;
+  struct G
+  {
+    static int F::* bar();
+  };
+
+  return sizeof(G);
+}
+
+int z = foo <int> ();
commit 978586407742e3f208d6f76ee0789b83c760e468
Author: jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Tue Jun 12 18:32:13 2012 +0000

    	* pt.c (tsubst_expr) [TAG_DEFN]: Instantiate local class.
    	* class.c (finish_struct): Don't add a TAG_DEFN for a lambda.
    	* decl2.c (finish_static_data_member_decl): Avoid redundant error.
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@188474 138bc75d-0d04-0410-961f-82ee72b054a4

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 4bb9dd2..021344b 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -6337,7 +6337,9 @@ finish_struct (tree t, tree attributes)
   else
     error ("trying to finish struct, but kicked out due to previous parse errors");
 
-  if (processing_template_decl && at_function_scope_p ())
+  if (processing_template_decl && at_function_scope_p ()
+      /* Lambdas are defined by the LAMBDA_EXPR.  */
+      && !LAMBDA_TYPE_P (t))
     add_stmt (build_min (TAG_DEFN, t));
 
   return t;
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 78e17af..2e3c9a6 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -770,7 +770,9 @@ finish_static_data_member_decl (tree decl,
   if (! processing_template_decl)
     VEC_safe_push (tree, gc, pending_statics, decl);
 
-  if (LOCAL_CLASS_P (current_class_type))
+  if (LOCAL_CLASS_P (current_class_type)
+      /* We already complained about the template definition.  */
+      && !DECL_TEMPLATE_INSTANTIATION (decl))
     permerror (input_location, "local class %q#T shall not have static data member %q#D",
 	       current_class_type, decl);
 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 04f7be8..5e02c8c 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -13123,7 +13123,21 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       break;
 
     case TAG_DEFN:
-      tsubst (TREE_TYPE (t), args, complain, NULL_TREE);
+      tmp = tsubst (TREE_TYPE (t), args, complain, NULL_TREE);
+      if (CLASS_TYPE_P (tmp))
+	{
+	  /* Local classes are not independent templates; they are
+	     instantiated along with their containing function.  And this
+	     way we don't have to deal with pushing out of one local class
+	     to instantiate a member of another local class.  */
+	  tree fn;
+	  /* Closures are handled by the LAMBDA_EXPR.  */
+	  gcc_assert (!LAMBDA_TYPE_P (TREE_TYPE (t)));
+	  complete_type (tmp);
+	  for (fn = TYPE_METHODS (tmp); fn; fn = DECL_CHAIN (fn))
+	    if (!DECL_ARTIFICIAL (fn))
+	      instantiate_decl (fn, /*defer_ok*/0, /*expl_inst_class*/false);
+	}
       break;
 
     case STATIC_ASSERT:

Reply via email to