[C++ Patch] [PR c++/88146] do not crash synthesizing inherited ctor(...)

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[C++ Patch] [PR c++/88146] do not crash synthesizing inherited ctor(...)

Alexandre Oliva-2
This patch started out from the testcase in PR88146, that attempted to
synthesize an inherited ctor without any args before a varargs
ellipsis and crashed while at that, because of the unguarded
dereferencing of the parm type list, that usually contains a
terminator.  The terminator is not there for varargs functions,
however, and without any other args, we ended up dereferencing a NULL
pointer.  Oops.

Guarding the accesses there was easy, but I missed the sorry message
we got in other testcases that passed arguments through the ellipsis
in inherited ctors.  I put a check in, and noticed the inherited ctors
were synthesized with the location assigned to the class name,
although they were initially assigned the location of the using
declaration.  I decided the latter was better, and arranged for the
better location to be retained.

Further investigation revealed the lack of a sorry message had to do
with the call being in a non-evaluated context, in this case, a
noexcept expression.  The sorry would be correctly reported in other
contexts, so I rolled back the check I'd added, but retained the
source location improvement.

I was still concerned about issuing sorry messages while instantiating
template ctors even in non-evaluated contexts, e.g., if a template
ctor had a base initializer that used an inherited ctor with enough
arguments that they'd go through an ellipsis.  I wanted to defer the
instantiation of such template ctors, but that would have been wrong
for constexpr template ctors, and already done for non-constexpr ones.
So, I just consolidated multiple test variants into a single testcase
that explores and explains various of the possibilities I thought of.

Regstrapped on x86_64- and i686-linux-gnu, mistakenly along with a patch
with a known regression, and got only that known regression.  Retesting
without it.  Ok to install?


for  gcc/cp/ChangeLog

        PR c++/88146
        * method.c (do_build_copy_constructor): Do not crash with
        ellipsis-only parm list.
        (synthesize_method): Retain location of inherited ctor.

for  gcc/testsuite/ChangeLog

        PR c++/88146
        * g++.dg/cpp0x/inh-ctor32.C: New.
---
 gcc/cp/method.c                         |    9 +
 gcc/testsuite/g++.dg/cpp0x/inh-ctor32.C |  229 +++++++++++++++++++++++++++++++
 2 files changed, 234 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/inh-ctor32.C

diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index fd023e200538..41d609fb1de6 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -643,7 +643,7 @@ do_build_copy_constructor (tree fndecl)
   bool trivial = trivial_fn_p (fndecl);
   tree inh = DECL_INHERITED_CTOR (fndecl);
 
-  if (!inh)
+  if (parm && !inh)
     parm = convert_from_reference (parm);
 
   if (trivial)
@@ -677,7 +677,7 @@ do_build_copy_constructor (tree fndecl)
     {
       tree fields = TYPE_FIELDS (current_class_type);
       tree member_init_list = NULL_TREE;
-      int cvquals = cp_type_quals (TREE_TYPE (parm));
+      int cvquals = parm ? cp_type_quals (TREE_TYPE (parm)) : 0;
       int i;
       tree binfo, base_binfo;
       tree init;
@@ -891,8 +891,9 @@ synthesize_method (tree fndecl)
 
   /* Reset the source location, we might have been previously
      deferred, and thus have saved where we were first needed.  */
-  DECL_SOURCE_LOCATION (fndecl)
-    = DECL_SOURCE_LOCATION (TYPE_NAME (DECL_CONTEXT (fndecl)));
+  if (!DECL_INHERITED_CTOR (fndecl))
+    DECL_SOURCE_LOCATION (fndecl)
+      = DECL_SOURCE_LOCATION (TYPE_NAME (DECL_CONTEXT (fndecl)));
 
   /* If we've been asked to synthesize a clone, just synthesize the
      cloned function instead.  Doing so will automatically fill in the
diff --git a/gcc/testsuite/g++.dg/cpp0x/inh-ctor32.C b/gcc/testsuite/g++.dg/cpp0x/inh-ctor32.C
new file mode 100644
index 000000000000..c40412fc5346
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/inh-ctor32.C
@@ -0,0 +1,229 @@
+// { dg-do compile { target c++11 } }
+// Minimized from the testcase for PR c++/88146,
+// then turned into multiple variants.
+
+// We issue an error when calling an inherited ctor with at least one
+// argument passed through a varargs ellipsis, if the call is in an
+// evaluated context.  Even in nonevaluated contexts, we will
+// instantiate constexpr templates (unlike non-constexpr templates),
+// which might then issue errors that in nonevlauated contexts
+// wouldn't be issued.
+
+// In these variants, the inherited ctor is constexpr, but it's only
+// called in unevaluated contexts, so no error is issued.  The
+// templateness of the ctor doesn't matter, because the only call that
+// passes args through the ellipsis is in a noexcept expr, that is not
+// evaluated.  The ctors in derived classes are created and
+// instantiated, discarding arguments passed through the ellipsis when
+// calling base ctors, but that's not reported: we only report a
+// problem when *calling* ctors that behave this way.
+namespace unevaled_call {
+  namespace no_arg_before_ellipsis {
+    namespace without_template {
+      struct foo {
+ constexpr foo(...) {}
+      };
+      struct boo : foo {
+ using foo::foo;
+      };
+      struct bar : boo {
+ using boo::boo;
+      };
+      void f() noexcept(noexcept(bar{0}));
+    }
+
+    namespace with_template {
+      struct foo {
+ template <typename... T>
+ constexpr foo(...) {}
+      };
+      struct boo : foo {
+ using foo::foo;
+      };
+      struct bar : boo {
+ using boo::boo;
+      };
+      void f() noexcept(noexcept(bar{0}));
+    }
+  }
+
+  namespace one_arg_before_ellipsis {
+    namespace without_template {
+      struct foo {
+ constexpr foo(int, ...) {}
+      };
+      struct boo : foo {
+ using foo::foo;
+      };
+      struct bar : boo {
+ using boo::boo;
+      };
+      void f() noexcept(noexcept(bar{0,1}));
+    }
+
+    namespace with_template {
+      struct foo {
+ template <typename T>
+ constexpr foo(T, ...) {}
+      };
+      struct boo : foo {
+ using foo::foo;
+      };
+      struct bar : boo {
+ using boo::boo;
+      };
+      void f() noexcept(noexcept(bar{0,1}));
+    }
+  }
+}
+
+// In these variants, the inherited ctor is constexpr, and it's called
+// in unevaluated contexts in ways that would otherwise trigger the
+// sorry message.  Here we check that the message is not issued at
+// those calls, nor at subsequent calls that use the same ctor without
+// passing arguments through its ellipsis.  We check that it is issued
+// later, when we pass the ctor arguments through the ellipsis.
+namespace evaled_bad_call_in_u {
+  namespace one_arg_before_ellipsis {
+    namespace without_template {
+      struct foo {
+ constexpr foo(int, ...) {}
+      };
+      struct boo : foo {
+ using foo::foo;
+      };
+      struct bar : boo {
+ using boo::boo;
+      };
+      void f() noexcept(noexcept(bar{0, 1}));
+      bar t(0);
+      bar u(0, 1); // { dg-message "sorry, unimplemented: passing arguments to ellipsis" }
+    }
+
+    namespace with_template {
+      struct foo {
+ template <typename T>
+ constexpr foo(T, ...) {}
+      };
+      struct boo : foo {
+ using foo::foo;
+      };
+      struct bar : boo {
+ using boo::boo;
+      };
+      void f() noexcept(noexcept(bar{0,1}));
+      bar t(0);
+      bar u(0,1); // { dg-message "sorry, unimplemented: passing arguments to ellipsis" }
+    }
+  }
+
+  namespace no_arg_before_ellipsis {
+    namespace without_template {
+      struct foo {
+ constexpr foo(...) {}
+      };
+      struct boo : foo {
+ using foo::foo;
+      };
+      struct bar : boo {
+ using boo::boo;
+      };
+      void f() noexcept(noexcept(bar{0}));
+      bar u(0); // { dg-message "sorry, unimplemented: passing arguments to ellipsis" }
+    }
+
+    namespace with_template {
+      struct foo {
+ template <typename... T>
+ constexpr foo(...) {}
+      };
+      struct boo : foo {
+ using foo::foo;
+      };
+      struct bar : boo {
+ using boo::boo;
+      };
+      void f() noexcept(noexcept(bar{0}));
+      bar u(0); // { dg-message "sorry, unimplemented: passing arguments to ellipsis" }
+    }
+  }
+}
+
+// Now, instead of instantiating a class that uses a derived ctor, we
+// introduce another template ctor that will use the varargs ctor to
+// initialize its base class.  The idea is to verify that the error
+// message is issued, even if the instantiation occurs in a
+// nonevaluated context, e.g., for constexpr templates.  In the
+// inherited_derived_ctor, we check that even an inherited ctor of a
+// constexpr ctor is instantiated and have an error message issued.
+namespace derived_ctor {
+  namespace direct_derived_ctor {
+    namespace constexpr_noninherited_ctor {
+      struct foo {
+ template <typename T>
+ constexpr foo(T, ...) {}
+      };
+      struct boo : foo {
+ using foo::foo;
+      };
+      struct bar : boo {
+ template <typename ...T>
+ constexpr bar(T ... args) : boo(args...) {} // { dg-message "sorry, unimplemented: passing arguments to ellipsis" }
+      };
+      void f() noexcept(noexcept(bar{0,1}));
+    }
+
+    namespace no_constexpr_noninherited_ctor {
+      struct foo {
+ template <typename T>
+ constexpr foo(T, ...) {}
+      };
+      struct boo : foo {
+ using foo::foo;
+      };
+      struct bar : boo {
+ template <typename ...T>
+ /* constexpr */ bar(T ... args) : boo(args...) {}
+      };
+      void f() noexcept(noexcept(bar{0,1}));
+    }
+  }
+
+  namespace inherited_derived_ctor {
+    namespace constexpr_noninherited_ctor {
+      struct foo {
+ template <typename T>
+ constexpr foo(T, ...) {}
+      };
+      struct boo : foo {
+ using foo::foo;
+      };
+      struct bor : boo {
+ template <typename ...T>
+ constexpr bor(T ... args) : boo(args...) {} // { dg-message "sorry, unimplemented: passing arguments to ellipsis" }
+      };
+      struct bar : bor {
+ using bor::bor;
+      };
+      void f() noexcept(noexcept(bar{0,1})); // { dg-message "'constexpr' expansion" }
+    }
+
+    namespace no_constexpr_noninherited_ctor {
+      struct foo {
+ template <typename T>
+ constexpr foo(T, ...) {}
+      };
+      struct boo : foo {
+ using foo::foo;
+      };
+      struct bor : boo {
+ template <typename ...T>
+ /* constexpr */ bor(T ... args) : boo(args...) {}
+      };
+      struct bar : bor {
+ using bor::bor;
+      };
+      void f() noexcept(noexcept(bar{0,1}));
+    }
+  }
+}


--
Alexandre Oliva, freedom fighter   https://FSFLA.org/blogs/lxo
Be the change, be Free!         FSF Latin America board member
GNU Toolchain Engineer                Free Software Evangelist
Hay que enGNUrecerse, pero sin perder la terGNUra jamás-GNUChe