Patchwork C++ PATCH for c++/44609 (printing an error for each step in infinite template recursion)

login
register
mail settings
Submitter Jason Merrill
Date July 11, 2011, 7:03 p.m.
Message ID <4E1B48E5.70203@redhat.com>
Download mbox | patch
Permalink /patch/104271/
State New
Headers show

Comments

Jason Merrill - July 11, 2011, 7:03 p.m.
The PR complained about G++ getting into an infinite loop, but it isn't 
really infinite; the problem is that in the testcase a function template 
has an error and then depends on another instance of itself.

I've fixed this for many cases by refusing to instantiate a declaration 
if there have been errors since beginning to instantiate the nearest 
enclosing declaration.  This doesn't affect classes and constexpr 
variables/functions, because we can't just decide not to instantiate 
them without producing other errors.

Tested x86_64-pc-linux-gnu, applying to trunk.

Patch

commit af8514f2c47162f32f56d0ee3f18e9040d756b1f
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Jul 11 09:38:11 2011 -0400

    	PR c++/44609
    	* cp-tree.h (struct tinst_level): Add errors field.
    	* pt.c (neglectable_inst_p, limit_bad_template_recurson): New.
    	(push_tinst_level): Don't start another decl in that case.
    	(reopen_tinst_level): Adjust errors field.
    	* decl2.c (cp_write_global_declarations): Don't complain about
    	undefined inline if its template was defined.
    	* mangle.c (mangle_decl_string): Handle failure from push_tinst_level.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 357295c..cc08640 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4679,6 +4679,9 @@  struct GTY((chain_next ("%h.next"))) tinst_level {
   /* The location where the template is instantiated.  */
   location_t locus;
 
+  /* errorcount+sorrycount when we pushed this level.  */
+  int errors;
+
   /* True if the location is in a system header.  */
   bool in_system_header_p;
 };
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 8cd51c2..d90d4b5 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -3950,10 +3950,10 @@  cp_write_global_declarations (void)
 	     #pragma interface, etc.) we decided not to emit the
 	     definition here.  */
 	  && !DECL_INITIAL (decl)
-	  /* An explicit instantiation can be used to specify
-	     that the body is in another unit. It will have
-	     already verified there was a definition.  */
-	  && !DECL_EXPLICIT_INSTANTIATION (decl))
+	  /* Don't complain if the template was defined.  */
+	  && !(DECL_TEMPLATE_INSTANTIATION (decl)
+	       && DECL_INITIAL (DECL_TEMPLATE_RESULT
+				(template_for_substitution (decl)))))
 	{
 	  warning (0, "inline function %q+D used but never defined", decl);
 	  /* Avoid a duplicate warning from check_global_declaration_1.  */
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index 81b772f..4a83c9a 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -3106,11 +3106,11 @@  mangle_decl_string (const tree decl)
   if (DECL_LANG_SPECIFIC (decl) && DECL_USE_TEMPLATE (decl))
     {
       struct tinst_level *tl = current_instantiation ();
-      if (!tl || tl->decl != decl)
+      if ((!tl || tl->decl != decl)
+	  && push_tinst_level (decl))
 	{
 	  template_p = true;
 	  saved_fn = current_function_decl;
-	  push_tinst_level (decl);
 	  current_function_decl = NULL_TREE;
 	}
     }
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 2c64dd4..7c735ef 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -7499,6 +7499,36 @@  uses_template_parms_level (tree t, int level)
 				 /*include_nondeduced_p=*/true);
 }
 
+/* Returns TRUE iff INST is an instantiation we don't need to do in an
+   ill-formed translation unit, i.e. a variable or function that isn't
+   usable in a constant expression.  */
+
+static inline bool
+neglectable_inst_p (tree d)
+{
+  return (DECL_P (d)
+	  && !(TREE_CODE (d) == FUNCTION_DECL ? DECL_DECLARED_CONSTEXPR_P (d)
+	       : decl_maybe_constant_var_p (d)));
+}
+
+/* Returns TRUE iff we should refuse to instantiate DECL because it's
+   neglectable and instantiated from within an erroneous instantiation.  */
+
+static bool
+limit_bad_template_recurson (tree decl)
+{
+  struct tinst_level *lev = current_tinst_level;
+  int errs = errorcount + sorrycount;
+  if (lev == NULL || errs == 0 || !neglectable_inst_p (decl))
+    return false;
+
+  for (; lev; lev = lev->next)
+    if (neglectable_inst_p (lev->decl))
+      break;
+
+  return (lev && errs > lev->errors);
+}
+
 static int tinst_depth;
 extern int max_tinst_depth;
 #ifdef GATHER_STATISTICS
@@ -7532,9 +7562,16 @@  push_tinst_level (tree d)
       return 0;
     }
 
+  /* If the current instantiation caused problems, don't let it instantiate
+     anything else.  Do allow deduction substitution and decls usable in
+     constant expressions.  */
+  if (limit_bad_template_recurson (d))
+    return 0;
+
   new_level = ggc_alloc_tinst_level ();
   new_level->decl = d;
   new_level->locus = input_location;
+  new_level->errors = errorcount+sorrycount;
   new_level->in_system_header_p = in_system_header;
   new_level->next = current_tinst_level;
   current_tinst_level = new_level;
@@ -7578,6 +7615,8 @@  reopen_tinst_level (struct tinst_level *level)
 
   current_tinst_level = level;
   pop_tinst_level ();
+  if (current_tinst_level)
+    current_tinst_level->errors = errorcount+sorrycount;
   return level->decl;
 }
 
diff --git a/gcc/testsuite/g++.dg/template/recurse3.C b/gcc/testsuite/g++.dg/template/recurse3.C
new file mode 100644
index 0000000..f1db7c5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/recurse3.C
@@ -0,0 +1,14 @@ 
+// PR c++/44609
+// { dg-options -ftemplate-depth=10 }
+
+template<int N>
+void f()
+{
+  0 = 0;			// { dg-error "lvalue required" }
+  f<N+1>();			// { dg-bogus "instantiation depth" }
+}
+
+int main()
+{
+  f<0>();
+}

commit 6d77d383471e8be7119cb001f985be1ea9ce97d9
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Jul 11 11:04:41 2011 -0400

    	* decl2.c (decl_constant_var_p): Use decl_maybe_constant_var_p.

diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index d90d4b5..e1f9562 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -3574,26 +3574,16 @@  decl_defined_p (tree decl)
 bool
 decl_constant_var_p (tree decl)
 {
-  bool ret;
-  tree type = TREE_TYPE (decl);
-  if (TREE_CODE (decl) != VAR_DECL)
+  if (!decl_maybe_constant_var_p (decl))
     return false;
-  if (DECL_DECLARED_CONSTEXPR_P (decl)
-      || (CP_TYPE_CONST_NON_VOLATILE_P (type)
-	  && INTEGRAL_OR_ENUMERATION_TYPE_P (type)))
-    {
-      /* We don't know if a template static data member is initialized with
-	 a constant expression until we instantiate its initializer.  Even
-	 in the case of a constexpr variable, we can't treat it as a
-	 constant until its initializer is complete in case it's used in
-	 its own initializer.  */
-      mark_used (decl);
-      ret = DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl);
-    }
-  else
-    ret = false;
 
-  return ret;
+  /* We don't know if a template static data member is initialized with
+     a constant expression until we instantiate its initializer.  Even
+     in the case of a constexpr variable, we can't treat it as a
+     constant until its initializer is complete in case it's used in
+     its own initializer.  */
+  mark_used (decl);
+  return DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl);
 }
 
 /* Returns true if DECL could be a symbolic constant variable, depending on