On Thu, Feb 21, 2019 at 02:57:28PM -1000, Jason Merrill wrote:
> On 2/21/19 1:34 PM, Marek Polacek wrote:
> > On Thu, Feb 21, 2019 at 11:22:41AM -1000, Jason Merrill wrote:
> > > On 2/21/19 10:56 AM, Marek Polacek wrote:
> > > > On Wed, Feb 20, 2019 at 01:53:18PM -1000, Jason Merrill wrote:
> > > > > On 2/20/19 10:31 AM, Marek Polacek wrote:
> > > > > > Here we ICE when substituting a deferred noexcept-specifier, 
> > > > > > because it
> > > > > > contains 'this', a PARM_DECL, in an evaluated context.  This is 
> > > > > > different
> > > > > > from "noexcept(noexcept(this))" where the noexcept operator's 
> > > > > > operand is
> > > > > > an unevaluated operand.  We crash within tsubst_copy's PARM_DECL 
> > > > > > handling
> > > > > > of a 'this' PARM_DECL:
> > > > > > 15488           gcc_assert (cp_unevaluated_operand != 0)
> > > > > > It'd be wrong to mess with cp_unevaluated_operand (or 
> > > > > > current_class_*), and
> > > > > > since we only check the expression's constness after substituting in
> > > > > > maybe_instantiate_noexcept, one fix would be the following.
> > > > > > 
> > > > > > This is not just about 'this', as the 87844 test shows: here we 
> > > > > > also have
> > > > > > a parameter whose value we're trying to determine -- it's a 
> > > > > > template arg
> > > > > > of a late-specified return type.  Returning the original value in 
> > > > > > tsubst_copy
> > > > > > and leaving the later code to complain seems to work here as well.  
> > > > > > Just
> > > > > > removing the assert should work as well.
> > > > > 
> > > > > I'm reluctant to mess with this assert, as it catches a lot of lambda 
> > > > > bugs.
> > > > 
> > > > Makes sense -- I wasn't aware of that.
> > > > 
> > > > > I think we want to register_parameter_specializations when 
> > > > > instantiating
> > > > > deferred noexcept, so that tsubst_copy can find the right decls.
> > > > 
> > > > Thanks for the suggestion -- that works with one catch: we need to 
> > > > replace the
> > > > fake 'this' in the noexcept- specifier with a real 'this' (the template 
> > > > one),
> > > > one that has DECL_CONTEXT set.  The fake one comes from 
> > > > inject_this_parameter,
> > > > when we were parsing the noexcept-specifier.  The real one was set in 
> > > > grokfndecl.
> > > 
> > > If you set current_class_ptr appropriately tsubst_copy will use it, so you
> > > shouldn't need to do a walk_tree.
> > 
> > Unfortunately that broke a lot of libstdc++ tests.  I can poke at it more if
> > you feel stronly about it.
> 
> Please do poke a bit more, that surprises me.

Apparently *someone* needs to learn how to properly restore c_c_{ptr,ref}...

noexcept35.C is a new test reduced from something from libstdc++.  Since it
exercises codepaths that nothing in dg.exp does, I think it's worth adding.

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

2019-02-22  Marek Polacek  <pola...@redhat.com>

        PR c++/88294 - ICE with non-constant noexcept-specifier.
        * pt.c (maybe_instantiate_noexcept): Set up the list of local
        specializations.  Set current_class_{ptr,ref}.

        * g++.dg/cpp0x/noexcept34.C: New test.
        * g++.dg/cpp0x/noexcept35.C: New test.

diff --git gcc/cp/pt.c gcc/cp/pt.c
index 42dd095a6b0..d678e278078 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -24203,12 +24203,39 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t 
complain)
          push_access_scope (fn);
          push_deferring_access_checks (dk_no_deferred);
          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);
-         spec = build_noexcept_spec (noex, tf_warning_or_error);
+
+         /* 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;
+             }
+
+           /* Create substitution entries for the parameters.  */
+           register_parameter_specializations (tdecl, fn);
+
+           /* 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);
+         }
+
          pop_deferring_access_checks ();
          pop_access_scope (fn);
          pop_tinst_level ();
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept34.C 
gcc/testsuite/g++.dg/cpp0x/noexcept34.C
new file mode 100644
index 00000000000..dce35652ef5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept34.C
@@ -0,0 +1,29 @@
+// PR c++/88294
+// { dg-do compile { target c++11 } }
+
+constexpr int foo (bool b) { return b; }
+
+template<typename> struct A
+{
+  constexpr int f () { return 0; }
+  bool b = true;
+  void g () noexcept (f()) { } // { dg-error "use of parameter" }
+  void g2 () noexcept (this->f()) { } // { dg-error "use of parameter" }
+  void g3 () noexcept (b) { } // { dg-error "use of .this. in a constant 
expression|use of parameter" }
+  void g4 (int i) noexcept (i) { } // { dg-error "use of parameter" }
+  void g5 () noexcept (A::f()) { } // { dg-error "use of parameter" }
+  void g6 () noexcept (foo(b)) { } // { dg-error "use of .this. in a constant 
expression|use of parameter" }
+  void g7 () noexcept (int{f()}) { } // { dg-error "use of parameter" }
+};
+
+int main ()
+{
+  A<int> a;
+  a.g ();
+  a.g2 ();
+  a.g3 ();
+  a.g4 (1);
+  a.g5 ();
+  a.g6 ();
+  a.g7 ();
+}
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept35.C 
gcc/testsuite/g++.dg/cpp0x/noexcept35.C
new file mode 100644
index 00000000000..8606b1ad28c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept35.C
@@ -0,0 +1,21 @@
+// { dg-do compile { target c++11 } }
+
+template <typename _Tp, _Tp __v> struct A { static constexpr _Tp value = __v; 
};
+typedef A<bool, false> false_type;
+struct is_same : false_type {};
+template <bool> struct enable_if;
+template <typename> using __remove_cvref_t = int;
+template <typename _Tp> class reference_wrapper {
+  static _Tp _S_fun();
+  template <typename _Up, typename = __remove_cvref_t<_Up>>
+  using __not_same = enable_if<is_same::value>;
+
+public:
+  template <typename _Up, typename = __not_same<_Up>>
+  reference_wrapper(_Up) noexcept(noexcept(reference_wrapper::_S_fun));
+};
+
+reference_wrapper<int> fn1() {
+  int __t = 10;
+  return __t;
+}

Reply via email to