This was one of those PRs where the more you poke, the more ICEs turn up.
This patch fixes the ones I could find.  The original problem was that
maybe_instantiate_noexcept got a TEMPLATE_DECL created for the member
friend template in do_friend.  Its noexcept-specification was deferred,
so we went to the block with push_access_scope, but that crashes on a
TEMPLATE_DECL.  One approach could be to somehow not defer noexcept-specs
for friend templates, I guess, but I didn't want to do that.

So the approach I did take in the end was to handle TEMPLATE_DECLs in
maybe_instantiate_noexcept.

That broke in register_parameter_specializations but we don't need this
code anyway, so let's do away with it -- the current_class_{ref,ptr}
code is enough to fix the PR that register_parameter_specializations was
introduced for.

Another issue was that since here we are instantiating a deferred noexcept,
in a instantiate_class_template context, processing_template_decl was 0.
Let's pretend it's on for the tsubst purposes here, and for build_noexcept_spec
too, so that the *_dependent_expression_p functions work.

Lastly, I found an invalid testcase that was breaking because a template code
leaked to constexpr functions.  This I fixed similarly to the recent explicit
PR fix (r269131).

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2019-03-07  Marek Polacek  <pola...@redhat.com>

        PR c++/89612 - ICE with member friend template with noexcept.
        * except.c (build_noexcept_spec): Call instantiate_non_dependent_expr
        before perform_implicit_conversion_flags.  Add processing_template_decl
        sentinel.
        * pt.c (maybe_instantiate_noexcept): For function templates, use their
        template result (function decl).  Don't set up local specializations.
        Temporarily turn on processing_template_decl.  Update the template type
        too.

        * g++.dg/cpp0x/noexcept36.C: New test.
        * g++.dg/cpp1y/noexcept1.C: New test.
        * g++.dg/cpp1z/noexcept-type21.C: New test.

diff --git gcc/cp/except.c gcc/cp/except.c
index 139e871d7a7..d97b8d40542 100644
--- gcc/cp/except.c
+++ gcc/cp/except.c
@@ -1285,10 +1285,13 @@ build_noexcept_spec (tree expr, tsubst_flags_t complain)
   if (TREE_CODE (expr) != DEFERRED_NOEXCEPT
       && !value_dependent_expression_p (expr))
     {
+      expr = instantiate_non_dependent_expr_sfinae (expr, complain);
+      /* Don't let perform_implicit_conversion_flags create more template
+        codes.  */
+      processing_template_decl_sentinel s;
       expr = perform_implicit_conversion_flags (boolean_type_node, expr,
                                                complain,
                                                LOOKUP_NORMAL);
-      expr = instantiate_non_dependent_expr (expr);
       expr = cxx_constant_value (expr);
     }
   if (TREE_CODE (expr) == INTEGER_CST)
diff --git gcc/cp/pt.c gcc/cp/pt.c
index 906cfe0a58c..44a2c4606f8 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -24174,6 +24174,17 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t 
complain)
 
   if (DECL_CLONED_FUNCTION_P (fn))
     fn = DECL_CLONED_FUNCTION (fn);
+
+  tree orig_fn = NULL_TREE;
+  /* For a member friend template we can get a TEMPLATE_DECL.  Let's use
+     its FUNCTION_DECL for the rest of this function -- push_access_scope
+     doesn't accept TEMPLATE_DECLs.  */
+  if (DECL_FUNCTION_TEMPLATE_P (fn))
+    {
+      orig_fn = fn;
+      fn = DECL_TEMPLATE_RESULT (fn);
+    }
+
   fntype = TREE_TYPE (fn);
   spec = TYPE_RAISES_EXCEPTIONS (fntype);
 
@@ -24204,37 +24215,41 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t 
complain)
          push_deferring_access_checks (dk_no_deferred);
          input_location = DECL_SOURCE_LOCATION (fn);
 
-         /* A new stack interferes with pop_access_scope.  */
-         {
-           /* Set up the list of local specializations.  */
-           local_specialization_stack lss (lss_copy);
-
-           tree save_ccp = current_class_ptr;
-           tree save_ccr = current_class_ref;
-           /* If needed, set current_class_ptr for the benefit of
-              tsubst_copy/PARM_DECL.  */
-           tree tdecl = DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (fn));
-           if (DECL_NONSTATIC_MEMBER_FUNCTION_P (tdecl))
-             {
-               tree this_parm = DECL_ARGUMENTS (tdecl);
-               current_class_ptr = NULL_TREE;
-               current_class_ref = cp_build_fold_indirect_ref (this_parm);
-               current_class_ptr = this_parm;
-             }
+         tree save_ccp = current_class_ptr;
+         tree save_ccr = current_class_ref;
+         /* If needed, set current_class_ptr for the benefit of
+            tsubst_copy/PARM_DECL.  */
+         tree tdecl = DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (fn));
+         if (DECL_NONSTATIC_MEMBER_FUNCTION_P (tdecl))
+           {
+             tree this_parm = DECL_ARGUMENTS (tdecl);
+             current_class_ptr = NULL_TREE;
+             current_class_ref = cp_build_fold_indirect_ref (this_parm);
+             current_class_ptr = this_parm;
+           }
 
-           /* Create substitution entries for the parameters.  */
-           register_parameter_specializations (tdecl, fn);
+         /* If this function is represented by a TEMPLATE_DECL, then
+            the deferred noexcept-specification might still contain
+            dependent types, even after substitution.  And we need the
+            dependency check functions to work in build_noexcept_spec.  */
+         if (orig_fn)
+           ++processing_template_decl;
 
-           /* Do deferred instantiation of the noexcept-specifier.  */
-           noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex),
-                                         DEFERRED_NOEXCEPT_ARGS (noex),
-                                         tf_warning_or_error, fn,
-                                         /*function_p=*/false,
-                                         /*i_c_e_p=*/true);
-           current_class_ptr = save_ccp;
-           current_class_ref = save_ccr;
-           spec = build_noexcept_spec (noex, tf_warning_or_error);
-         }
+         /* Do deferred instantiation of the noexcept-specifier.  */
+         noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex),
+                                       DEFERRED_NOEXCEPT_ARGS (noex),
+                                       tf_warning_or_error, fn,
+                                       /*function_p=*/false,
+                                       /*i_c_e_p=*/true);
+
+         current_class_ptr = save_ccp;
+         current_class_ref = save_ccr;
+
+         /* Build up the noexcept-specification.  */
+         spec = build_noexcept_spec (noex, tf_warning_or_error);
+
+         if (orig_fn)
+           --processing_template_decl;
 
          pop_deferring_access_checks ();
          pop_access_scope (fn);
@@ -24250,6 +24265,8 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t 
complain)
        return false;
 
       TREE_TYPE (fn) = build_exception_variant (fntype, spec);
+      if (orig_fn)
+       TREE_TYPE (orig_fn) = TREE_TYPE (fn);
     }
 
   FOR_EACH_CLONE (clone, fn)
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept36.C 
gcc/testsuite/g++.dg/cpp0x/noexcept36.C
new file mode 100644
index 00000000000..ecab59df694
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept36.C
@@ -0,0 +1,19 @@
+// PR c++/89612
+// { dg-do compile { target c++11 } }
+
+template <typename> 
+struct C {
+  template <int N>
+  friend int foo() noexcept(N);
+
+  template <int N>
+  friend int foo2() noexcept(N); // { dg-error "different exception" }
+};
+
+template <int N>
+int foo() noexcept(N);
+
+template <int N>
+int foo2() noexcept(N + 1);
+
+C<int> c;
diff --git gcc/testsuite/g++.dg/cpp1y/noexcept1.C 
gcc/testsuite/g++.dg/cpp1y/noexcept1.C
new file mode 100644
index 00000000000..2344b1ba92c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp1y/noexcept1.C
@@ -0,0 +1,13 @@
+// PR c++/89612
+// { dg-do compile { target c++14 } }
+
+template <int> bool b;
+
+template <typename> 
+struct C {
+  template <typename> friend int foo() noexcept(b<1>); // { dg-error "not 
usable in a constant expression|different exception specifier" }
+};
+
+template <typename> int foo() noexcept(b<1>);
+
+auto a = C<int>();
diff --git gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C 
gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C
new file mode 100644
index 00000000000..d0a61d95e87
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp1z/noexcept-type21.C
@@ -0,0 +1,16 @@
+// PR c++/89612
+// { dg-do compile { target c++17 } }
+
+template <typename a> using b = typename a ::c;
+template <typename> bool d;
+template <typename, typename> struct e {
+  template <typename f, typename g> e(f, g) {}
+  template <typename h, typename i, typename j>
+  friend auto k(h &&, const j &, i &&) noexcept(d<b<h>, h> &&d<b<i>, i>);
+};
+template <typename l, typename m> e(l, m)->e<l, m>;
+template <typename l, typename m, typename j>
+auto k(l &&, const j &, m &&) noexcept(d<b<l>, l> &&d<b<m>, m>);
+int main() {
+  e(0, [] {});
+}

Reply via email to