Constructor decloning can turn the C1/C2 entry points into thunks
that call the unified primary constructor, C4.  That works only if
C4 remains a usable external interface.

For libstdc++ explicit instantiations on MinGW, that is not the case:
libstdc++ exports the complete and base constructor entry points, but
not the unified C4 symbol.  With -Os this could leave user code with
references to an undefined C4 constructor, as in PR libstdc++/125359.

Avoid the thunking path for constructors in maybe_clone_body.  Keep
constructor clones as real bodies so callers continue to target C1/C2
instead of the unified primary constructor.

gcc/cp/ChangeLog:

        PR libstdc++/125359
        * optimize.cc (maybe_clone_body): Do not thunk constructor clones
        through the unified primary constructor.

 gcc/testsuite/ChangeLog:

        PR libstdc++/125359
        * g++.dg/ext/pr125359.C: New test.

Signed-off-by: Oleg Tolmatcev <[email protected]>
---
 gcc/cp/optimize.cc                  | 10 +++++++---
 gcc/testsuite/g++.dg/ext/pr125359.C | 23 +++++++++++++++++++++++
 2 files changed, 30 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/pr125359.C

diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
index 31ee2aaccf..7f1968ce04 100644
--- a/gcc/cp/optimize.cc
+++ b/gcc/cp/optimize.cc
@@ -556,9 +556,13 @@ maybe_clone_body (tree fn)
 
   bool can_alias = can_alias_cdtor (fn);
 
-  /* If we decide to turn clones into thunks, they will branch to fn.
-     Must have original function available to call.  */
-  if (!can_alias && maybe_thunk_body (fn, need_alias))
+  /* If we decide to turn clones into thunks, they will branch to FN.
+     For constructors that would make the C1/C2 entry points call the
+     unified primary ctor (C4), which is not a stable external interface.
+     Keep constructors as real cloned bodies so callers stay on C1/C2.  */
+  if (!can_alias
+      && !DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (fn)
+      && maybe_thunk_body (fn, need_alias))
     {
       pop_from_top_level ();
       /* We still need to emit the original function.  */
diff --git a/gcc/testsuite/g++.dg/ext/pr125359.C 
b/gcc/testsuite/g++.dg/ext/pr125359.C
new file mode 100644
index 0000000000..0d894abef6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/pr125359.C
@@ -0,0 +1,23 @@
+// PR libstdc++/125359
+// { dg-do link { target *-*-mingw* } }
+// { dg-options "-std=gnu++20 -Os" }
+
+#include <string>
+#include <utility>
+#include <vector>
+
+struct S
+{
+  std::string s;
+};
+
+int
+main ()
+{
+  std::vector<S> v;
+
+  S x = {};
+  x.s = "hello";
+
+  v.push_back (std::move (x));
+}
-- 
2.55.0.windows.1

Reply via email to