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.

Before calling register_parameter_specializations one apparently has to set up
a local specializations stack.

Does this look reasonable?  It no longer fixes the other PR; we'll likely have
to play some register_local_specialization games there, too.

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

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

        PR c++/88294 - ICE with non-constant noexcept-specifier.
        * pt.c (replace_this_r): New function.
        (maybe_instantiate_noexcept): Set up the list of local
        specializations.  Replace the fake 'this' with the real one.

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

diff --git gcc/cp/pt.c gcc/cp/pt.c
index a212be8c747..abbec8f53b9 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -24158,6 +24158,20 @@ always_instantiate_p (tree decl)
              && decl_maybe_constant_var_p (decl)));
 }
 
+/* Replace all fake 'this' parameters (i.e. the ones created by
+   inject_this_parameter) with the real 'this'.  */
+
+static tree
+replace_this_r (tree *t, int *, void *data_)
+{
+  tree *d = static_cast<tree *>(data_);
+  if (TREE_CODE (*t) == PARM_DECL
+      && DECL_NAME (*t) == this_identifier
+      && DECL_CONTEXT (*t) == NULL_TREE)
+    *t = *d;
+  return NULL_TREE;
+}
+
 /* If FN has a noexcept-specifier that hasn't been instantiated yet,
    instantiate it now, modifying TREE_TYPE (fn).  Returns false on
    error, true otherwise.  */
@@ -24203,12 +24217,34 @@ 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);
+
+           /* Replace the fake 'this' with the real 'this'.  */
+           tree tdecl = DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (fn));
+           if (DECL_NONSTATIC_MEMBER_FUNCTION_P (tdecl))
+             {
+               tree real_this = DECL_ARGUMENTS (tdecl);
+               tree *p = &DEFERRED_NOEXCEPT_PATTERN (noex);
+               cp_walk_tree_without_duplicates (p, replace_this_r,
+                                                &real_this);
+             }
+
+           /* 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);
+           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..495cc260d9b
--- /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 ".this. is not a constant 
expression" }
+  void g2 () noexcept (this->f()) { } // { dg-error ".this. is not a constant 
expression" }
+  void g3 () noexcept (b) { } // { dg-error ".this. is not a constant 
expression|use of .this." }
+  void g4 (int i) noexcept (i) { } // { dg-error "use of parameter outside 
function body" }
+  void g5 () noexcept (A::f()) { } // { dg-error ".this. is not a constant 
expression" }
+  void g6 () noexcept (foo(b)) { } // { dg-error ".this. is not a constant 
expression|use of .this. in a constant expression" }
+  void g7 () noexcept (int{f()}) { } // { dg-error ".this. is not a constant 
expression" }
+};
+
+int main ()
+{
+  A<int> a;
+  a.g ();
+  a.g2 ();
+  a.g3 ();
+  a.g4 (1);
+  a.g5 ();
+  a.g6 ();
+  a.g7 ();
+}

Reply via email to