]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: ICE on invalid with inheriting constructors [PR94751]
authorMarek Polacek <polacek@redhat.com>
Fri, 5 Mar 2021 20:46:50 +0000 (15:46 -0500)
committerMarek Polacek <polacek@redhat.com>
Thu, 25 Mar 2021 20:11:29 +0000 (16:11 -0400)
This is an ICE on invalid where we crash because since r269032 we
keep error_mark_node around instead of using noexcept_false_spec
when things go wrong; see the walk_field_subobs hunk.

We crash in deduce_inheriting_ctor which calls synthesized_method_walk
to deduce the exception-specification, but fails to do so in this case,
because the testcase is invalid so get_nsdmi returns error_mark_node for
the member 'c', and per r269032 the error_mark_node propagates back to
deduce_inheriting_ctor which subsequently calls build_exception_variant
whereon we crash.  I think we should return early if the deduction fails
and I decided to call mark_used to get an error right away instead of
hoping that it would get called later.  My worry is that we could forget
that there was an error and think that we just deduced noexcept(false).

And then I noticed that the test still crashes in C++98.  Here again we
failed to deduce the exception-specification in implicitly_declare_fn,
but nothing reported an error between synthesized_method_walk and the
assert.  Well, not much we can do except calling synthesized_method_walk
again, this time in the verbose mode and making sure that we did get an
error.

gcc/cp/ChangeLog:

PR c++/94751
* call.c (build_over_call): Maybe call mark_used in case
deduce_inheriting_ctor fails and return error_mark_node.
* cp-tree.h (deduce_inheriting_ctor): Adjust declaration.
* method.c (deduce_inheriting_ctor): Return bool if the deduction
fails.
(implicitly_declare_fn): If raises is error_mark_node, call
synthesized_method_walk with diag being true.

gcc/testsuite/ChangeLog:

PR c++/94751
* g++.dg/cpp0x/inh-ctor37.C: New test.

gcc/cp/call.c
gcc/cp/cp-tree.h
gcc/cp/method.c
gcc/testsuite/g++.dg/cpp0x/inh-ctor37.C [new file with mode: 0644]

index e757e1893c77baf6345da2a0c388b53d15c40ced..4b81d0ff33311676d5f7e6b1397c0176a479a886 100644 (file)
@@ -8947,8 +8947,13 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
   /* OK, we're actually calling this inherited constructor; set its deletedness
      appropriately.  We can get away with doing this here because calling is
      the only way to refer to a constructor.  */
-  if (DECL_INHERITED_CTOR (fn))
-    deduce_inheriting_ctor (fn);
+  if (DECL_INHERITED_CTOR (fn)
+      && !deduce_inheriting_ctor (fn))
+    {
+      if (complain & tf_error)
+       mark_used (fn);
+      return error_mark_node;
+    }
 
   /* Make =delete work with SFINAE.  */
   if (DECL_DELETED_FN (fn))
index e68e3905f80a6d4d2c7202dd54ba489b17e795a7..f6470524cf8c33e891187a06bc461e42eb69b511 100644 (file)
@@ -6915,7 +6915,7 @@ extern bool is_xible                              (enum tree_code, tree, tree);
 extern tree get_defaulted_eh_spec              (tree, tsubst_flags_t = tf_warning_or_error);
 extern bool maybe_explain_implicit_delete      (tree);
 extern void explain_implicit_non_constexpr     (tree);
-extern void deduce_inheriting_ctor             (tree);
+extern bool deduce_inheriting_ctor             (tree);
 extern bool decl_remember_implicit_trigger_p   (tree);
 extern void synthesize_method                  (tree);
 extern tree lazily_declare_fn                  (special_function_kind,
index 3fe3bd82a7dc7b7c306810296a4e7b0117816325..25c1e681b996075038e4819a66580120ff2ee009 100644 (file)
@@ -2789,9 +2789,9 @@ explain_implicit_non_constexpr (tree decl)
 
 /* DECL is an instantiation of an inheriting constructor template.  Deduce
    the correct exception-specification and deletedness for this particular
-   specialization.  */
+   specialization.  Return true if the deduction succeeds; false otherwise.  */
 
-void
+bool
 deduce_inheriting_ctor (tree decl)
 {
   decl = DECL_ORIGIN (decl);
@@ -2804,6 +2804,8 @@ deduce_inheriting_ctor (tree decl)
                           /*diag*/false,
                           &inh,
                           FUNCTION_FIRST_USER_PARMTYPE (decl));
+  if (spec == error_mark_node)
+    return false;
   if (TREE_CODE (inherited_ctor_binfo (decl)) != TREE_BINFO)
     /* Inherited the same constructor from different base subobjects.  */
     deleted = true;
@@ -2818,6 +2820,8 @@ deduce_inheriting_ctor (tree decl)
       TREE_TYPE (clone) = build_exception_variant (TREE_TYPE (clone), spec);
       SET_DECL_INHERITED_CTOR (clone, inh);
     }
+
+  return true;
 }
 
 /* Implicitly declare the special function indicated by KIND, as a
@@ -2993,9 +2997,17 @@ implicitly_declare_fn (special_function_kind kind, tree type,
       if (raises != error_mark_node)
        fn_type = build_exception_variant (fn_type, raises);
       else
-       /* Can happen, eg, in C++98 mode for an ill-formed non-static data
-          member initializer (c++/89914).  */
-       gcc_assert (seen_error ());
+       {
+         /* Can happen, e.g., in C++98 mode for an ill-formed non-static data
+            member initializer (c++/89914).  Also, in C++98, we might have
+            failed to deduce RAISES, so try again but complain this time.  */
+         if (cxx_dialect < cxx11)
+           synthesized_method_walk (type, kind, const_p, nullptr, nullptr,
+                                    nullptr, nullptr, /*diag=*/true,
+                                    &inherited_ctor, inherited_parms);
+         /* We should have seen an error at this point.  */
+         gcc_assert (seen_error ());
+       }
     }
   fn = build_lang_decl (FUNCTION_DECL, name, fn_type);
   if (kind != sfk_inheriting_constructor)
diff --git a/gcc/testsuite/g++.dg/cpp0x/inh-ctor37.C b/gcc/testsuite/g++.dg/cpp0x/inh-ctor37.C
new file mode 100644 (file)
index 0000000..7d12b53
--- /dev/null
@@ -0,0 +1,26 @@
+// PR c++/94751
+// { dg-do compile }
+// { dg-options "-w" }
+
+struct A {
+  A(float);
+};
+
+template<typename T> 
+struct B : A {
+  using A::A;
+
+  struct C {
+    C(int);
+  };
+
+  C c{ "foo" }; // { dg-error "invalid conversion" }
+};
+
+struct S { S(B<A> *); };
+
+S
+fn ()
+{
+  return S(new B<A>(10.5)); // { dg-error "no matching function" "" { target c++98_only } }
+}