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.
commit 47a2b859af4d34f00c46c0811830455b5aef3d15
Author: Jason Merrill <ja...@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;
+}
commit b0a459bd4e40cddea002782afac4f25441b027ab
Author: Jason Merrill <ja...@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);

Reply via email to