public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* C++ PATCH for c++/49107 (excessive instantiation due to noexcept)
@ 2011-06-08 22:09 Jason Merrill
  2011-06-09  8:55 ` Paolo Carlini
  2011-06-15  8:09 ` Jason Merrill
  0 siblings, 2 replies; 3+ messages in thread
From: Jason Merrill @ 2011-06-08 22:09 UTC (permalink / raw)
  To: gcc-patches List; +Cc: Paolo Carlini

[-- Attachment #1: Type: text/plain, Size: 454 bytes --]

49107 is an issue with the addition of noexcept to the standard library 
causing extra instantiations, to the point that code that worked before 
doesn't work now due to circular dependency.  Someone suggested that to 
avoid this problem we could defer instantiation of noexcept-specifiers 
until they are actually needed.  This patch implements that suggestion, 
and does in fact seem to fix the problem.

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

[-- Attachment #2: 49107.patch --]
[-- Type: text/x-patch, Size: 20670 bytes --]

commit 03a304001f8fdb776a96648786fe667ad416ebeb
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Jun 8 09:06:07 2011 -0400

    	PR c++/49107
    	* cp-tree.def (DEFERRED_NOEXCEPT): New.
    	* cp-tree.h (struct tree_deferred_noexcept): New.
    	(DEFERRED_NOEXCEPT_PATTERN, DEFERRED_NOEXCEPT_ARGS): New.
    	(DEFERRED_NOEXCEPT_SPEC_P): New.
    	(enum cp_tree_node_structure_enum): Add TS_CP_DEFERRED_NOEXCEPT.
    	(union lang_tree_node): Add tree_deferred_noexcept.
    	(maybe_instantiate_noexcept): Declare.
    	* cp-objcp-common.c (cp_tree_size): Handle DEFERRED_NOEXCEPT.
    	* error.c (dump_exception_spec): Likewise.
    	* cxx-pretty-print.c (pp_cxx_exception_specification): Likewise.
    	* ptree.c (cxx_print_xnode): Likewise.
    	* tree.c (cp_tree_equal): Likewise.
    	* decl.c (cp_tree_node_structure): Likewise.
    	(duplicate_decls): Call maybe_instantiate_noexcept.
    	* except.c (build_noexcept_spec): Handle DEFERRED_NOEXCEPT.
    	(nothrow_spec_p, type_noexcept_p, type_throw_all_p): Check
    	DEFERRED_NOEXCEPT_SPEC_P.
    	* typeck2.c (merge_exception_specifiers): Likewise.
    	* decl2.c (mark_used): Call maybe_instantiate_noexcept.
    	* method.c (process_subob_fn, defaulted_late_check): Likewise.
    	* pt.c (tsubst_exception_specification): Add defer_ok parm.
    	Build DEFERRED_NOEXCEPT.
    	(maybe_instantiate_noexcept): New.
    	(tsubst, regenerate_decl_from_template, instantiate_decl): Adjust.
    	* search.c (check_final_overrider): Call maybe_instantiate_noexcept.

diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c
index df6b1dd..1866b81 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -79,6 +79,7 @@ cp_tree_size (enum tree_code code)
     case BASELINK:		return sizeof (struct tree_baselink);
     case TEMPLATE_PARM_INDEX:	return sizeof (template_parm_index);
     case DEFAULT_ARG:		return sizeof (struct tree_default_arg);
+    case DEFERRED_NOEXCEPT:	return sizeof (struct tree_deferred_noexcept);
     case OVERLOAD:		return sizeof (struct tree_overload);
     case STATIC_ASSERT:         return sizeof (struct tree_static_assert);
     case TYPE_ARGUMENT_PACK:
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 001ef10..ce11417 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -214,6 +214,11 @@ DEFTREECODE (USING_STMT, "using_directive", tcc_statement, 1)
    parsing had occurred.  */
 DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0)
 
+/* An uninstantiated noexcept-specification.  DEFERRED_NOEXCEPT_PATTERN is
+   the pattern from the template, and DEFERRED_NOEXCEPT_ARGS are the
+   template arguments to substitute into the pattern when needed.  */
+DEFTREECODE (DEFERRED_NOEXCEPT, "deferred_noexcept", tcc_exceptional, 0)
+
 /* A template-id, like foo<int>.  The first operand is the template.
    The second is NULL if there are no explicit arguments, or a
    TREE_VEC of arguments.  The template will be a FUNCTION_DECL,
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0395db7..71f7dd4 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -507,6 +507,22 @@ struct GTY (()) tree_default_arg {
   VEC(tree,gc) *instantiations;
 };
 
+
+#define DEFERRED_NOEXCEPT_PATTERN(NODE) \
+  (((struct tree_deferred_noexcept *)DEFERRED_NOEXCEPT_CHECK (NODE))->pattern)
+#define DEFERRED_NOEXCEPT_ARGS(NODE) \
+  (((struct tree_deferred_noexcept *)DEFERRED_NOEXCEPT_CHECK (NODE))->args)
+#define DEFERRED_NOEXCEPT_SPEC_P(NODE)				\
+  ((NODE) && (TREE_PURPOSE (NODE))				\
+   && TREE_CODE (TREE_PURPOSE (NODE)) == DEFERRED_NOEXCEPT)
+
+struct GTY (()) tree_deferred_noexcept {
+  struct tree_base base;
+  tree pattern;
+  tree args;
+};
+
+
 /* The condition associated with the static assertion.  This must be
    an integral constant expression.  */
 #define STATIC_ASSERT_CONDITION(NODE) \
@@ -693,6 +709,7 @@ enum cp_tree_node_structure_enum {
   TS_CP_BASELINK,
   TS_CP_WRAPPER,
   TS_CP_DEFAULT_ARG,
+  TS_CP_DEFERRED_NOEXCEPT,
   TS_CP_STATIC_ASSERT,
   TS_CP_ARGUMENT_PACK_SELECT,
   TS_CP_TRAIT_EXPR,
@@ -711,6 +728,7 @@ union GTY((desc ("cp_tree_node_structure (&%h)"),
   struct tree_overload GTY ((tag ("TS_CP_OVERLOAD"))) overload;
   struct tree_baselink GTY ((tag ("TS_CP_BASELINK"))) baselink;
   struct tree_default_arg GTY ((tag ("TS_CP_DEFAULT_ARG"))) default_arg;
+  struct tree_deferred_noexcept GTY ((tag ("TS_CP_DEFERRED_NOEXCEPT"))) deferred_noexcept;
   struct lang_identifier GTY ((tag ("TS_CP_IDENTIFIER"))) identifier;
   struct tree_static_assert GTY ((tag ("TS_CP_STATIC_ASSERT"))) 
     static_assertion;
@@ -5130,6 +5148,7 @@ extern int more_specialized_fn			(tree, tree, int);
 extern void do_decl_instantiation		(tree, tree);
 extern void do_type_instantiation		(tree, tree, tsubst_flags_t);
 extern bool always_instantiate_p		(tree);
+extern void maybe_instantiate_noexcept		(tree);
 extern tree instantiate_decl			(tree, int, bool);
 extern int comp_template_parms			(const_tree, const_tree);
 extern bool uses_parameter_packs                (tree);
diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c
index 2f392de..c5f1ac0 100644
--- a/gcc/cp/cxx-pretty-print.c
+++ b/gcc/cp/cxx-pretty-print.c
@@ -1446,7 +1446,10 @@ pp_cxx_exception_specification (cxx_pretty_printer *pp, tree t)
       pp_cxx_ws_string (pp, "noexcept");
       pp_cxx_whitespace (pp);
       pp_cxx_left_paren (pp);
-      pp_cxx_expression (pp, TREE_PURPOSE (ex_spec));
+      if (DEFERRED_NOEXCEPT_SPEC_P (ex_spec))
+	pp_cxx_ws_string (pp, "<uninstantiated>");
+      else
+	pp_cxx_expression (pp, TREE_PURPOSE (ex_spec));
       pp_cxx_right_paren (pp);
       return;
     }
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 30f70d9..d7da390 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -1784,6 +1784,9 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
       tree oldtype = TREE_TYPE (olddecl);
       tree newtype;
 
+      if (TREE_CODE (newdecl) == FUNCTION_DECL)
+	maybe_instantiate_noexcept (olddecl);
+
       /* Merge the data types specified in the two decls.  */
       newtype = merge_types (TREE_TYPE (newdecl), TREE_TYPE (olddecl));
 
@@ -13665,6 +13668,7 @@ cp_tree_node_structure (union lang_tree_node * t)
   switch (TREE_CODE (&t->generic))
     {
     case DEFAULT_ARG:		return TS_CP_DEFAULT_ARG;
+    case DEFERRED_NOEXCEPT:	return TS_CP_DEFERRED_NOEXCEPT;
     case IDENTIFIER_NODE:	return TS_CP_IDENTIFIER;
     case OVERLOAD:		return TS_CP_OVERLOAD;
     case TEMPLATE_PARM_INDEX:	return TS_CP_TPI;
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 9005f7e..a0512cd 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -4228,6 +4228,9 @@ mark_used (tree decl)
       return;
     }
 
+  if (TREE_CODE (decl) == FUNCTION_DECL)
+    maybe_instantiate_noexcept (decl);
+
   /* Normally, we can wait until instantiation-time to synthesize DECL.
      However, if DECL is a static data member initialized with a constant
      or a constexpr function, we need it right now because a reference to
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index d9652dc..96796c2 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -1438,7 +1438,10 @@ dump_exception_spec (tree t, int flags)
       pp_cxx_ws_string (cxx_pp, "noexcept");
       pp_cxx_whitespace (cxx_pp);
       pp_cxx_left_paren (cxx_pp);
-      dump_expr (TREE_PURPOSE (t), flags);
+      if (DEFERRED_NOEXCEPT_SPEC_P (t))
+	pp_cxx_ws_string (cxx_pp, "<uninstantiated>");
+      else
+	dump_expr (TREE_PURPOSE (t), flags);
       pp_cxx_right_paren (cxx_pp);
     }
   else if (t)
diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index 874f111..3399652 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -1160,6 +1160,7 @@ finish_noexcept_expr (tree expr, tsubst_flags_t complain)
 bool
 nothrow_spec_p (const_tree spec)
 {
+  gcc_assert (!DEFERRED_NOEXCEPT_SPEC_P (spec));
   if (spec == NULL_TREE
       || TREE_VALUE (spec) != NULL_TREE
       || spec == noexcept_false_spec)
@@ -1180,6 +1181,7 @@ bool
 type_noexcept_p (const_tree type)
 {
   tree spec = TYPE_RAISES_EXCEPTIONS (type);
+  gcc_assert (!DEFERRED_NOEXCEPT_SPEC_P (spec));
   if (flag_nothrow_opt)
     return nothrow_spec_p (spec);
   else
@@ -1193,6 +1195,7 @@ bool
 type_throw_all_p (const_tree type)
 {
   tree spec = TYPE_RAISES_EXCEPTIONS (type);
+  gcc_assert (!DEFERRED_NOEXCEPT_SPEC_P (spec));
   return spec == NULL_TREE || spec == noexcept_false_spec;
 }
 
@@ -1204,7 +1207,7 @@ build_noexcept_spec (tree expr, int complain)
 {
   /* This isn't part of the signature, so don't bother trying to evaluate
      it until instantiation.  */
-  if (!processing_template_decl)
+  if (!processing_template_decl && TREE_CODE (expr) != DEFERRED_NOEXCEPT)
     {
       expr = perform_implicit_conversion_flags (boolean_type_node, expr,
 						complain,
@@ -1219,7 +1222,8 @@ build_noexcept_spec (tree expr, int complain)
     return error_mark_node;
   else
     {
-      gcc_assert (processing_template_decl || expr == error_mark_node);
+      gcc_assert (processing_template_decl || expr == error_mark_node
+		  || TREE_CODE (expr) == DEFERRED_NOEXCEPT);
       return build_tree_list (expr, NULL_TREE);
     }
 }
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index cf35b4a..9188700 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -923,7 +923,9 @@ process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p,
 
   if (spec_p)
     {
-      tree raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
+      tree raises;
+      maybe_instantiate_noexcept (fn);
+      raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
       *spec_p = merge_exception_specifiers (*spec_p, raises);
     }
 
@@ -1558,7 +1560,9 @@ defaulted_late_check (tree fn)
      it had been implicitly declared.  */
   if (DECL_DEFAULTED_IN_CLASS_P (fn))
     {
-      tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn));
+      tree eh_spec;
+      maybe_instantiate_noexcept (fn);
+      eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn));
       if (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn))
 	  && !comp_except_specs (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)),
 				 eh_spec, ce_normal))
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 61ca31c..51d590e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -10342,7 +10342,8 @@ static tree
 tsubst_exception_specification (tree fntype,
 				tree args,
 				tsubst_flags_t complain,
-				tree in_decl)
+				tree in_decl,
+				bool defer_ok)
 {
   tree specs;
   tree new_specs;
@@ -10352,9 +10353,33 @@ tsubst_exception_specification (tree fntype,
   if (specs && TREE_PURPOSE (specs))
     {
       /* A noexcept-specifier.  */
-      new_specs = tsubst_copy_and_build
-	(TREE_PURPOSE (specs), args, complain, in_decl, /*function_p=*/false,
-	 /*integral_constant_expression_p=*/true);
+      tree expr = TREE_PURPOSE (specs);
+      if (expr == boolean_true_node || expr == boolean_false_node)
+	new_specs = expr;
+      else if (defer_ok)
+	{
+	  /* Defer instantiation of noexcept-specifiers to avoid
+	     excessive instantiations (c++/49107).  */
+	  new_specs = make_node (DEFERRED_NOEXCEPT);
+	  if (DEFERRED_NOEXCEPT_SPEC_P (specs))
+	    {
+	      /* We already partially instantiated this member template,
+		 so combine the new args with the old.  */
+	      DEFERRED_NOEXCEPT_PATTERN (new_specs)
+		= DEFERRED_NOEXCEPT_PATTERN (expr);
+	      DEFERRED_NOEXCEPT_ARGS (new_specs)
+		= add_to_template_args (DEFERRED_NOEXCEPT_ARGS (expr), args);
+	    }
+	  else
+	    {
+	      DEFERRED_NOEXCEPT_PATTERN (new_specs) = expr;
+	      DEFERRED_NOEXCEPT_ARGS (new_specs) = args;
+	    }
+	}
+      else
+	new_specs = tsubst_copy_and_build
+	  (expr, args, complain, in_decl, /*function_p=*/false,
+	   /*integral_constant_expression_p=*/true);
       new_specs = build_noexcept_spec (new_specs, complain);
     }
   else if (specs)
@@ -10879,7 +10904,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 
 	/* Substitute the exception specification.  */
 	specs = tsubst_exception_specification (t, args, complain,
-						in_decl);
+						in_decl, /*defer_ok*/true);
 	if (specs == error_mark_node)
 	  return error_mark_node;
 	if (specs)
@@ -17159,7 +17184,8 @@ regenerate_decl_from_template (tree decl, tree tmpl)
 	args = get_innermost_template_args (args, parms_depth);
 
       specs = tsubst_exception_specification (TREE_TYPE (code_pattern),
-					      args, tf_error, NULL_TREE);
+					      args, tf_error, NULL_TREE,
+					      /*defer_ok*/false);
       if (specs)
 	TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl),
 						    specs);
@@ -17324,6 +17350,46 @@ always_instantiate_p (tree decl)
 	      && decl_maybe_constant_var_p (decl)));
 }
 
+/* If FN has a noexcept-specifier that hasn't been instantiated yet,
+   instantiate it now, modifying TREE_TYPE (fn).  */
+
+void
+maybe_instantiate_noexcept (tree fn)
+{
+  tree fntype = TREE_TYPE (fn);
+  tree spec = TYPE_RAISES_EXCEPTIONS (fntype);
+  tree noex = NULL_TREE;
+  location_t saved_loc = input_location;
+  tree clone;
+
+  if (!DEFERRED_NOEXCEPT_SPEC_P (spec))
+    return;
+  noex = TREE_PURPOSE (spec);
+
+  push_tinst_level (fn);
+  push_access_scope (fn);
+  input_location = DECL_SOURCE_LOCATION (fn);
+  noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex),
+				DEFERRED_NOEXCEPT_ARGS (noex),
+				tf_warning_or_error, fn, /*function_p=*/false,
+				/*integral_constant_expression_p=*/true);
+  input_location = saved_loc;
+  pop_access_scope (fn);
+  pop_tinst_level ();
+  spec = build_noexcept_spec (noex, tf_warning_or_error);
+  if (spec == error_mark_node)
+    spec = noexcept_false_spec;
+  TREE_TYPE (fn) = build_exception_variant (fntype, spec);
+
+  FOR_EACH_CLONE (clone, fn)
+    {
+      if (TREE_TYPE (clone) == fntype)
+	TREE_TYPE (clone) = TREE_TYPE (fn);
+      else
+	TREE_TYPE (clone) = build_exception_variant (TREE_TYPE (clone), spec);
+    }
+}
+
 /* Produce the definition of D, a _DECL generated from a template.  If
    DEFER_OK is nonzero, then we don't have to actually do the
    instantiation now; we just have to do it sometime.  Normally it is
@@ -17460,6 +17526,9 @@ instantiate_decl (tree d, int defer_ok,
       SET_DECL_IMPLICIT_INSTANTIATION (d);
     }
 
+  if (TREE_CODE (d) == FUNCTION_DECL)
+    maybe_instantiate_noexcept (d);
+
   /* Recheck the substitutions to obtain any warning messages
      about ignoring cv qualifiers.  Don't do this for artificial decls,
      as it breaks the context-sensitive substitution for lambda op(). */
@@ -17477,7 +17546,7 @@ instantiate_decl (tree d, int defer_ok,
 	{
 	  tsubst (DECL_ARGUMENTS (gen), gen_args, tf_warning_or_error, d);
           tsubst_exception_specification (type, gen_args, tf_warning_or_error,
-                                          d);
+                                          d, /*defer_ok*/true);
 	  /* Don't simply tsubst the function type, as that will give
 	     duplicate warnings about poor parameter qualifications.
 	     The function arguments are the same as the decl_arguments
diff --git a/gcc/cp/ptree.c b/gcc/cp/ptree.c
index 5c9626e..fb05e13 100644
--- a/gcc/cp/ptree.c
+++ b/gcc/cp/ptree.c
@@ -227,6 +227,10 @@ cxx_print_xnode (FILE *file, tree node, int indent)
       indent_to (file, indent + 3);
       fprintf (file, "index %d", ARGUMENT_PACK_SELECT_INDEX (node));
       break;
+    case DEFERRED_NOEXCEPT:
+      print_node (file, "pattern", DEFERRED_NOEXCEPT_PATTERN (node), indent+4);
+      print_node (file, "args", DEFERRED_NOEXCEPT_ARGS (node), indent+4);
+      break;
     default:
       break;
     }
diff --git a/gcc/cp/search.c b/gcc/cp/search.c
index cf0b1a0..97f593c 100644
--- a/gcc/cp/search.c
+++ b/gcc/cp/search.c
@@ -1803,8 +1803,8 @@ check_final_overrider (tree overrider, tree basefn)
   tree base_type = TREE_TYPE (basefn);
   tree over_return = TREE_TYPE (over_type);
   tree base_return = TREE_TYPE (base_type);
-  tree over_throw = TYPE_RAISES_EXCEPTIONS (over_type);
-  tree base_throw = TYPE_RAISES_EXCEPTIONS (base_type);
+  tree over_throw, base_throw;
+
   int fail = 0;
 
   if (DECL_INVALID_OVERRIDER_P (overrider))
@@ -1888,6 +1888,11 @@ check_final_overrider (tree overrider, tree basefn)
     }
 
   /* Check throw specifier is at least as strict.  */
+  maybe_instantiate_noexcept (basefn);
+  maybe_instantiate_noexcept (overrider);
+  base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
+  over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
+
   if (!comp_except_specs (base_throw, over_throw, ce_derived))
     {
       error ("looser throw specifier for %q+#F", overrider);
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 025fe2d..5b988e9 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2340,6 +2340,13 @@ cp_tree_equal (tree t1, tree t2)
       /* Now compare operands as usual.  */
       break;
 
+    case DEFERRED_NOEXCEPT:
+      return (cp_tree_equal (DEFERRED_NOEXCEPT_PATTERN (t1),
+			     DEFERRED_NOEXCEPT_PATTERN (t2))
+	      && comp_template_args (DEFERRED_NOEXCEPT_ARGS (t1),
+				     DEFERRED_NOEXCEPT_ARGS (t2)));
+      break;
+
     default:
       break;
     }
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 4d5c21a..f291393 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1769,10 +1769,15 @@ merge_exception_specifiers (tree list, tree add)
     return list;
   else if (!add || add == noexcept_false_spec)
     return add;
+
+  /* We need to instantiate deferred noexcept before we get here.  */
+  gcc_assert (!DEFERRED_NOEXCEPT_SPEC_P (list)
+	      && !DEFERRED_NOEXCEPT_SPEC_P (add));
+
   /* For merging noexcept(true) and throw(), take the more recent one (LIST).
      Any other noexcept-spec should only be merged with an equivalent one.
      So the !TREE_VALUE code below is correct for all cases.  */
-  else if (!TREE_VALUE (add))
+  if (!TREE_VALUE (add))
     return list;
   else if (!TREE_VALUE (list))
     return add;
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept11.C b/gcc/testsuite/g++.dg/cpp0x/noexcept11.C
new file mode 100644
index 0000000..b7c64a6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept11.C
@@ -0,0 +1,53 @@
+// PR c++/49107
+// { dg-options -std=c++0x }
+
+template<typename _Tp>
+_Tp declval() noexcept;
+
+template<typename _Tp , typename = decltype(_Tp(declval<_Tp&&>()))>
+struct trait
+{
+  static const bool value=true;
+};
+
+template<class _T2>
+struct pair
+{
+  _T2 second;
+  void swap(pair& __p)
+    noexcept(trait<_T2>::value);
+};
+
+template < class R_ >
+struct Main
+{
+  Main() {}
+  Main(const typename R_::Sub1T& r) ;
+  Main(const typename R_::Sub2T& l) ;
+};
+
+template < class R_ >
+class Sub1
+{
+  typedef pair<typename R_::MainT> Rep;
+  Rep base;
+};
+
+template < class R_ >
+struct Sub2
+{
+  typedef pair<typename R_::MainT> Rep;
+  Rep base;
+};
+
+struct Kernel
+{
+  typedef Main<Kernel> MainT;
+  typedef Sub1<Kernel> Sub1T;
+  typedef Sub2<Kernel> Sub2T;
+};
+
+Main<Kernel> f()
+{
+  return Main<Kernel> ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept12.C b/gcc/testsuite/g++.dg/cpp0x/noexcept12.C
new file mode 100644
index 0000000..1fd1b03
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept12.C
@@ -0,0 +1,11 @@
+// Test that we handle merging with deferred noexcept.
+// { dg-options -std=c++0x }
+
+template <class U>
+struct O
+{
+  template <class T>
+  void f() noexcept(noexcept(T()));
+};
+
+template<> template<> void O<int>::f<int>() noexcept { }
diff --git a/gcc/testsuite/g++.dg/cpp0x/sfinae11.C b/gcc/testsuite/g++.dg/cpp0x/sfinae11.C
index a3ffc34..117b08b 100644
--- a/gcc/testsuite/g++.dg/cpp0x/sfinae11.C
+++ b/gcc/testsuite/g++.dg/cpp0x/sfinae11.C
@@ -6,7 +6,7 @@ template<class T>
 T&& declval() noexcept;
 
 template< class T >
-inline void f1( T& x ) noexcept( noexcept( declval<T&>().foo() ) )
+inline void f1( T& x ) noexcept( noexcept( declval<T&>().foo() ) ) // { dg-error "Z" }
 {
   x.foo();
 }
@@ -21,7 +21,7 @@ inline void f2( T& x ) noexcept( Noexcept )
 
 // a common and trivial mistake
 template< class T >
-inline void f3( T& x ) noexcept( declval<T&>().foo() )
+inline void f3( T& x ) noexcept( declval<T&>().foo() ) // { dg-error "Z" }
 {
   x.foo();
 }
@@ -50,7 +50,7 @@ int main()
   static_assert(  noexcept( f2(y) ), "OK." );
   // static_assert(  noexcept( f3(y) ), "shall be ill-formed(OK)." );
 
-  static_assert(  noexcept( f1(z) ), "shall be ill-formed." ); // { dg-error "no match" }
+  noexcept( f1(z) );		// { dg-message "required" }
   static_assert(  noexcept( f2(z) ), "shall be ill-formed." ); // { dg-error "no match" }
-  static_assert( !noexcept( f3(z) ), "shall be ill-formed." ); // { dg-error "no match" }
+  noexcept( f3(z) );		// { dg-message "required" }
 }

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: C++ PATCH for c++/49107 (excessive instantiation due to noexcept)
  2011-06-08 22:09 C++ PATCH for c++/49107 (excessive instantiation due to noexcept) Jason Merrill
@ 2011-06-09  8:55 ` Paolo Carlini
  2011-06-15  8:09 ` Jason Merrill
  1 sibling, 0 replies; 3+ messages in thread
From: Paolo Carlini @ 2011-06-09  8:55 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches List

On 06/08/2011 11:32 PM, Jason Merrill wrote:
> 49107 is an issue with the addition of noexcept to the standard 
> library causing extra instantiations, to the point that code that 
> worked before doesn't work now due to circular dependency.  Someone 
> suggested that to avoid this problem we could defer instantiation of 
> noexcept-specifiers until they are actually needed.  This patch 
> implements that suggestion, and does in fact seem to fix the problem.
Thanks a lot Jason.

I'm trying to figure out which are the implications for the library of 
the comment you added to c++/48468, I think it would be now more 
difficult to implement consolidated is_nothrow_* traits, thus not 
computing first via sfinae the corresponding is_* trait. But doesn't 
seem a serious limitation...

Thanks again,
Paolo.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: C++ PATCH for c++/49107 (excessive instantiation due to noexcept)
  2011-06-08 22:09 C++ PATCH for c++/49107 (excessive instantiation due to noexcept) Jason Merrill
  2011-06-09  8:55 ` Paolo Carlini
@ 2011-06-15  8:09 ` Jason Merrill
  1 sibling, 0 replies; 3+ messages in thread
From: Jason Merrill @ 2011-06-15  8:09 UTC (permalink / raw)
  To: gcc-patches List; +Cc: Paolo Carlini

[-- Attachment #1: Type: text/plain, Size: 514 bytes --]

Another testcase attached to 49107 shows that we can't force noexcept 
instantiation as part of implicitly declaring special member functions; 
we need to defer their noexcept-specifications as well.

While I was working on this I noticed that it's not necessary to save 
and restore input_location when doing push/pop_tinst_level, as 
pop_tinst_level will restore the previous input_location, so in a 
separate patch I remove the unnecessary code I added recently.

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

[-- Attachment #2: 49107-2.patch --]
[-- Type: text/x-patch, Size: 12614 bytes --]

commit 47a2b859af4d34f00c46c0811830455b5aef3d15
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Jun 14 17:38:07 2011 -0400

    	PR c++/49107
    	* cp-tree.h (DEFERRED_NOEXCEPT_SPEC_P): Handle overload.
    	* method.c (defaulted_late_check): Only maybe_instantiate_noexcept
    	if the declaration had an exception-specifier.
    	(process_subob_fn): Don't maybe_instantiate_noexcept.
    	* pt.c (maybe_instantiate_noexcept): Handle overload.
    	* typeck2.c (nothrow_spec_p_uninst): New.
    	(merge_exception_specifiers): Add 'fn' parm.  Build up overload.
    	* typeck.c (merge_types): Adjust.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 06b5926..ff8b2dc 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -514,7 +514,8 @@ struct GTY (()) tree_default_arg {
   (((struct tree_deferred_noexcept *)DEFERRED_NOEXCEPT_CHECK (NODE))->args)
 #define DEFERRED_NOEXCEPT_SPEC_P(NODE)				\
   ((NODE) && (TREE_PURPOSE (NODE))				\
-   && TREE_CODE (TREE_PURPOSE (NODE)) == DEFERRED_NOEXCEPT)
+  && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFERRED_NOEXCEPT	\
+      || is_overloaded_fn (TREE_PURPOSE (NODE))))
 
 struct GTY (()) tree_deferred_noexcept {
   struct tree_base base;
@@ -1792,7 +1793,10 @@ struct GTY((variable_size)) lang_type {
    this type can raise.  Each TREE_VALUE is a _TYPE.  The TREE_VALUE
    will be NULL_TREE to indicate a throw specification of `()', or
    no exceptions allowed.  For a noexcept specification, TREE_VALUE
-   is NULL_TREE and TREE_PURPOSE is the constant-expression. */
+   is NULL_TREE and TREE_PURPOSE is the constant-expression.  For
+   a deferred noexcept-specification, TREE_PURPOSE is a DEFERRED_NOEXCEPT
+   (for templates) or an OVERLOAD list of functions (for implicitly
+   declared functions).  */
 #define TYPE_RAISES_EXCEPTIONS(NODE) TYPE_LANG_SLOT_1 (NODE)
 
 /* For FUNCTION_TYPE or METHOD_TYPE, return 1 iff it is declared `throw()'
@@ -5698,7 +5702,7 @@ extern tree build_x_arrow			(tree);
 extern tree build_m_component_ref		(tree, tree);
 extern tree build_functional_cast		(tree, tree, tsubst_flags_t);
 extern tree add_exception_specifier		(tree, tree, int);
-extern tree merge_exception_specifiers		(tree, tree);
+extern tree merge_exception_specifiers		(tree, tree, tree);
 
 /* in mangle.c */
 extern void init_mangle				(void);
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 9188700..d8f7a57 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -923,10 +923,8 @@ process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p,
 
   if (spec_p)
     {
-      tree raises;
-      maybe_instantiate_noexcept (fn);
-      raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
-      *spec_p = merge_exception_specifiers (*spec_p, raises);
+      tree raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
+      *spec_p = merge_exception_specifiers (*spec_p, raises, fn);
     }
 
   if (!trivial_fn_p (fn))
@@ -1560,15 +1558,16 @@ defaulted_late_check (tree fn)
      it had been implicitly declared.  */
   if (DECL_DEFAULTED_IN_CLASS_P (fn))
     {
-      tree eh_spec;
-      maybe_instantiate_noexcept (fn);
-      eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn));
-      if (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn))
-	  && !comp_except_specs (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)),
-				 eh_spec, ce_normal))
-	error ("function %q+D defaulted on its first declaration "
-	       "with an exception-specification that differs from "
-	       "the implicit declaration %q#D", fn, implicit_fn);
+      tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn));
+      if (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)))
+	{
+	  maybe_instantiate_noexcept (fn);
+	  if (!comp_except_specs (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)),
+				  eh_spec, ce_normal))
+	    error ("function %q+D defaulted on its first declaration "
+		   "with an exception-specification that differs from "
+		   "the implicit declaration %q#D", fn, implicit_fn);
+	}
       TREE_TYPE (fn) = build_exception_variant (TREE_TYPE (fn), eh_spec);
       if (DECL_DECLARED_CONSTEXPR_P (implicit_fn))
 	/* Hmm...should we do this for out-of-class too? Should it be OK to
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 5f53ce8..ff145a2 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -17354,27 +17354,49 @@ always_instantiate_p (tree decl)
 void
 maybe_instantiate_noexcept (tree fn)
 {
-  tree fntype = TREE_TYPE (fn);
-  tree spec = TYPE_RAISES_EXCEPTIONS (fntype);
-  tree noex = NULL_TREE;
-  tree clone;
+  tree fntype, spec, noex, clone;
+
+  if (DECL_CLONED_FUNCTION_P (fn))
+    fn = DECL_CLONED_FUNCTION (fn);
+  fntype = TREE_TYPE (fn);
+  spec = TYPE_RAISES_EXCEPTIONS (fntype);
 
   if (!DEFERRED_NOEXCEPT_SPEC_P (spec))
     return;
+
   noex = TREE_PURPOSE (spec);
 
-  push_tinst_level (fn);
-  push_access_scope (fn);
-  input_location = DECL_SOURCE_LOCATION (fn);
-  noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex),
-				DEFERRED_NOEXCEPT_ARGS (noex),
-				tf_warning_or_error, fn, /*function_p=*/false,
-				/*integral_constant_expression_p=*/true);
-  pop_access_scope (fn);
-  pop_tinst_level ();
-  spec = build_noexcept_spec (noex, tf_warning_or_error);
-  if (spec == error_mark_node)
-    spec = noexcept_false_spec;
+  if (TREE_CODE (noex) == DEFERRED_NOEXCEPT)
+    {
+      push_tinst_level (fn);
+      push_access_scope (fn);
+      input_location = DECL_SOURCE_LOCATION (fn);
+      noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex),
+				    DEFERRED_NOEXCEPT_ARGS (noex),
+				    tf_warning_or_error, fn, /*function_p=*/false,
+				    /*integral_constant_expression_p=*/true);
+      pop_access_scope (fn);
+      pop_tinst_level ();
+      spec = build_noexcept_spec (noex, tf_warning_or_error);
+      if (spec == error_mark_node)
+	spec = noexcept_false_spec;
+    }
+  else
+    {
+      /* This is an implicitly declared function, so NOEX is a list of
+	 other functions to evaluate and merge.  */
+      tree elt;
+      spec = noexcept_true_spec;
+      for (elt = noex; elt; elt = OVL_NEXT (elt))
+	{
+	  tree fn = OVL_CURRENT (elt);
+	  tree subspec;
+	  maybe_instantiate_noexcept (fn);
+	  subspec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
+	  spec = merge_exception_specifiers (spec, subspec, NULL_TREE);
+	}
+    }
+
   TREE_TYPE (fn) = build_exception_variant (fntype, spec);
 
   FOR_EACH_CLONE (clone, fn)
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index f97d4dc..83c65fa 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -830,7 +830,8 @@ merge_types (tree t1, tree t2)
 	gcc_assert (type_memfn_quals (t1) == type_memfn_quals (t2));
 	rval = apply_memfn_quals (rval, type_memfn_quals (t1));
 	raises = merge_exception_specifiers (TYPE_RAISES_EXCEPTIONS (t1),
-					     TYPE_RAISES_EXCEPTIONS (t2));
+					     TYPE_RAISES_EXCEPTIONS (t2),
+					     NULL_TREE);
 	t1 = build_exception_variant (rval, raises);
 	break;
       }
@@ -841,7 +842,8 @@ merge_types (tree t1, tree t2)
 	   is just the main variant of this.  */
 	tree basetype = class_of_this_parm (t2);
 	tree raises = merge_exception_specifiers (TYPE_RAISES_EXCEPTIONS (t1),
-						  TYPE_RAISES_EXCEPTIONS (t2));
+						  TYPE_RAISES_EXCEPTIONS (t2),
+						  NULL_TREE);
 	tree t3;
 
 	/* If this was a member function type, get back to the
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index d72f57e..6d6267e 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1771,12 +1771,24 @@ add_exception_specifier (tree list, tree spec, int complain)
   return list;
 }
 
+/* Like nothrow_spec_p, but don't abort on deferred noexcept.  */
+
+static bool
+nothrow_spec_p_uninst (const_tree spec)
+{
+  if (DEFERRED_NOEXCEPT_SPEC_P (spec))
+    return false;
+  return nothrow_spec_p (spec);
+}
+
 /* Combine the two exceptions specifier lists LIST and ADD, and return
-   their union.  */
+   their union.  If FN is non-null, it's the source of ADD.  */
 
 tree
-merge_exception_specifiers (tree list, tree add)
+merge_exception_specifiers (tree list, tree add, tree fn)
 {
+  tree noex, orig_list;
+
   /* No exception-specifier or noexcept(false) are less strict than
      anything else.  Prefer the newer variant (LIST).  */
   if (!list || list == noexcept_false_spec)
@@ -1784,37 +1796,51 @@ merge_exception_specifiers (tree list, tree add)
   else if (!add || add == noexcept_false_spec)
     return add;
 
-  /* We need to instantiate deferred noexcept before we get here.  */
-  gcc_assert (!DEFERRED_NOEXCEPT_SPEC_P (list)
-	      && !DEFERRED_NOEXCEPT_SPEC_P (add));
-
-  /* For merging noexcept(true) and throw(), take the more recent one (LIST).
-     Any other noexcept-spec should only be merged with an equivalent one.
-     So the !TREE_VALUE code below is correct for all cases.  */
-  if (!TREE_VALUE (add))
+  /* noexcept(true) and throw() are stricter than anything else.
+     As above, prefer the more recent one (LIST).  */
+  if (nothrow_spec_p_uninst (add))
     return list;
-  else if (!TREE_VALUE (list))
+
+  noex = TREE_PURPOSE (list);
+  if (DEFERRED_NOEXCEPT_SPEC_P (add))
+    {
+      /* If ADD is a deferred noexcept, we must have been called from
+	 process_subob_fn.  For implicitly declared functions, we build up
+	 a list of functions to consider at instantiation time.  */
+      if (noex == boolean_true_node)
+	noex = NULL_TREE;
+      gcc_assert (fn && (!noex || is_overloaded_fn (noex)));
+      noex = build_overload (fn, noex);
+    }
+  else if (nothrow_spec_p_uninst (list))
     return add;
   else
+    gcc_checking_assert (!TREE_PURPOSE (add)
+			 || cp_tree_equal (noex, TREE_PURPOSE (add)));
+
+  /* Combine the dynamic-exception-specifiers, if any.  */
+  orig_list = list;
+  for (; add && TREE_VALUE (add); add = TREE_CHAIN (add))
     {
-      tree orig_list = list;
+      tree spec = TREE_VALUE (add);
+      tree probe;
 
-      for (; add; add = TREE_CHAIN (add))
+      for (probe = orig_list; probe && TREE_VALUE (probe);
+	   probe = TREE_CHAIN (probe))
+	if (same_type_p (TREE_VALUE (probe), spec))
+	  break;
+      if (!probe)
 	{
-	  tree spec = TREE_VALUE (add);
-	  tree probe;
-
-	  for (probe = orig_list; probe; probe = TREE_CHAIN (probe))
-	    if (same_type_p (TREE_VALUE (probe), spec))
-	      break;
-	  if (!probe)
-	    {
-	      spec = build_tree_list (NULL_TREE, spec);
-	      TREE_CHAIN (spec) = list;
-	      list = spec;
-	    }
+	  spec = build_tree_list (NULL_TREE, spec);
+	  TREE_CHAIN (spec) = list;
+	  list = spec;
 	}
     }
+
+  /* Keep the noexcept-specifier at the beginning of the list.  */
+  if (noex != TREE_PURPOSE (list))
+    list = tree_cons (noex, TREE_VALUE (list), TREE_CHAIN (list));
+
   return list;
 }
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept13.C b/gcc/testsuite/g++.dg/cpp0x/noexcept13.C
new file mode 100644
index 0000000..7d51c82
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept13.C
@@ -0,0 +1,78 @@
+// PR c++/49107
+// { dg-options -std=c++0x }
+
+namespace std
+{
+  template<typename _Tp> _Tp&& declval() noexcept;
+
+  struct true_type { static const bool value = true; };
+  struct false_type { static const bool value = false; };
+
+  template<typename _Tp, typename _Arg>
+    struct __is_direct_constructible_impl
+    {
+      template<typename _Tp2, typename _Arg2, typename
+	       = decltype(::new _Tp2(declval<_Arg2>()))>
+      static true_type __test(int);
+
+      template<typename, typename>
+      static false_type __test(...);
+
+      typedef decltype(__test<_Tp, _Arg>(0)) type;
+    };
+
+  template<typename _Tp, typename _Arg>
+    struct __is_direct_constructible_new_safe
+    : public __is_direct_constructible_impl<_Tp, _Arg>::type
+    { };
+
+  template<class _T1, class _T2>
+    struct pair
+    {
+      pair() = default;
+      constexpr pair(const pair&) = default;
+
+      pair(pair&& __p)
+      noexcept(__is_direct_constructible_new_safe<_T2,_T2&&>::value);
+    };
+}
+
+template <class R_>
+struct Vector3
+{
+  typedef typename R_::Ray_3 Ray_3;
+  Vector3() {}
+  explicit Vector3(const Ray_3& r);
+};
+
+template < class R_ > class LineC3
+{
+  typedef typename R_::Vector_3 Vector_3;
+  std::pair<int, Vector_3> x;
+};
+
+template < class R_ > class RayH3
+{
+  typedef typename R_::Vector_3 Vector_3;
+  std::pair<int, Vector_3> x;
+};
+
+template <typename Kernel >
+struct Homogeneous_base
+{
+  typedef LineC3<Kernel> Line_3;
+  typedef RayH3<Kernel> Ray_3;
+};
+
+template < typename RT_>
+struct Simple_homogeneous
+: public Homogeneous_base< Simple_homogeneous<RT_> >
+{
+  typedef Vector3<Simple_homogeneous<RT_> > Vector_3;
+};
+
+int main()
+{
+  typedef Simple_homogeneous<double> R;
+  R::Line_3 l3;
+}

[-- Attachment #3: no-inp-save.patch --]
[-- Type: text/x-patch, Size: 1490 bytes --]

commit b0a459bd4e40cddea002782afac4f25441b027ab
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Jun 14 15:02:32 2011 -0400

    	* pt.c (deduction_tsubst_fntype): Don't save input_location.
    	(maybe_instantiate_noexcept): Likewise.

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 814a08f..5f53ce8 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -13635,7 +13635,6 @@ deduction_tsubst_fntype (tree fn, tree targs)
 {
   static bool excessive_deduction_depth;
   static int deduction_depth;
-  location_t save_loc = input_location;
   struct pending_template *old_last_pend = last_pending_template;
 
   tree fntype = TREE_TYPE (fn);
@@ -13659,7 +13658,6 @@ deduction_tsubst_fntype (tree fn, tree targs)
   r = tsubst (fntype, targs, tf_none, NULL_TREE);
   pop_deduction_access_scope (fn);
   --deduction_depth;
-  input_location = save_loc;
 
   if (excessive_deduction_depth)
     {
@@ -17359,7 +17357,6 @@ maybe_instantiate_noexcept (tree fn)
   tree fntype = TREE_TYPE (fn);
   tree spec = TYPE_RAISES_EXCEPTIONS (fntype);
   tree noex = NULL_TREE;
-  location_t saved_loc = input_location;
   tree clone;
 
   if (!DEFERRED_NOEXCEPT_SPEC_P (spec))
@@ -17373,7 +17370,6 @@ maybe_instantiate_noexcept (tree fn)
 				DEFERRED_NOEXCEPT_ARGS (noex),
 				tf_warning_or_error, fn, /*function_p=*/false,
 				/*integral_constant_expression_p=*/true);
-  input_location = saved_loc;
   pop_access_scope (fn);
   pop_tinst_level ();
   spec = build_noexcept_spec (noex, tf_warning_or_error);

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2011-06-15  3:53 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-06-08 22:09 C++ PATCH for c++/49107 (excessive instantiation due to noexcept) Jason Merrill
2011-06-09  8:55 ` Paolo Carlini
2011-06-15  8:09 ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).