[pushed] c++: inherited default constructor [CWG2799]

Message ID 20230919152834.3988714-1-jason@redhat.com
State Accepted
Headers
Series [pushed] c++: inherited default constructor [CWG2799] |

Checks

Context Check Description
snail/gcc-patch-check success Github commit url

Commit Message

Jason Merrill Sept. 19, 2023, 3:28 p.m. UTC
  Tested x86_64-pc-linux-gnu, applying to trunk.

-- 8< --

In this testcase, it seems clear that B should be trivially
default-constructible, since the inherited default constructor is trivial
and there are no other subobjects to initialize.  But we were saying no
because we don't define triviality of inherited constructors.

CWG discussion suggested that the solution is to implicitly declare a
default constructor when inheriting a default constructor; that makes sense
to me.

	DR 2799

gcc/cp/ChangeLog:

	* class.cc (add_implicit_default_ctor): Split out...
	(add_implicitly_declared_members): ...from here.
	Also call it when inheriting a default ctor.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/inh-ctor38.C: New test.
---
 gcc/cp/class.cc                         | 36 ++++++++++++++++---------
 gcc/testsuite/g++.dg/cpp0x/inh-ctor38.C | 19 +++++++++++++
 2 files changed, 43 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/inh-ctor38.C


base-commit: 5c5851bd93b8078bdd9665bc9bfe91fbf0028dc1
  

Patch

diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index d270dcbb14c..469e98ed8b7 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -3292,6 +3292,22 @@  one_inherited_ctor (tree ctor, tree t, tree using_decl)
     }
 }
 
+/* Implicitly declare T().  */
+
+static void
+add_implicit_default_ctor (tree t)
+{
+  TYPE_HAS_DEFAULT_CONSTRUCTOR (t) = 1;
+  CLASSTYPE_LAZY_DEFAULT_CTOR (t) = 1;
+  if (cxx_dialect >= cxx11)
+    TYPE_HAS_CONSTEXPR_CTOR (t)
+      /* Don't force the declaration to get a hard answer; if the
+	 definition would have made the class non-literal, it will still be
+	 non-literal because of the base or member in question, and that
+	 gives a better diagnostic.  */
+      = type_maybe_constexpr_default_constructor (t);
+}
+
 /* Create default constructors, assignment operators, and so forth for
    the type indicated by T, if they are needed.  CANT_HAVE_CONST_CTOR,
    and CANT_HAVE_CONST_ASSIGNMENT are nonzero if, for whatever reason,
@@ -3320,17 +3336,7 @@  add_implicitly_declared_members (tree t, tree* access_decls,
      If there is no user-declared constructor for a class, a default
      constructor is implicitly declared.  */
   if (! TYPE_HAS_USER_CONSTRUCTOR (t))
-    {
-      TYPE_HAS_DEFAULT_CONSTRUCTOR (t) = 1;
-      CLASSTYPE_LAZY_DEFAULT_CTOR (t) = 1;
-      if (cxx_dialect >= cxx11)
-	TYPE_HAS_CONSTEXPR_CTOR (t)
-	  /* Don't force the declaration to get a hard answer; if the
-	     definition would have made the class non-literal, it will still be
-	     non-literal because of the base or member in question, and that
-	     gives a better diagnostic.  */
-	  = type_maybe_constexpr_default_constructor (t);
-    }
+    add_implicit_default_ctor (t);
 
   /* [class.ctor]
 
@@ -3394,7 +3400,13 @@  add_implicitly_declared_members (tree t, tree* access_decls,
 	  location_t loc = input_location;
 	  input_location = DECL_SOURCE_LOCATION (using_decl);
 	  for (tree fn : ovl_range (ctor_list))
-	    one_inherited_ctor (fn, t, using_decl);
+	    {
+	      if (!TYPE_HAS_DEFAULT_CONSTRUCTOR (t) && default_ctor_p (fn))
+		/* CWG2799: Inheriting a default constructor gives us a default
+		   constructor, not just an inherited constructor.  */
+		add_implicit_default_ctor (t);
+	      one_inherited_ctor (fn, t, using_decl);
+	    }
 	  *access_decls = TREE_CHAIN (*access_decls);
 	  input_location = loc;
 	}
diff --git a/gcc/testsuite/g++.dg/cpp0x/inh-ctor38.C b/gcc/testsuite/g++.dg/cpp0x/inh-ctor38.C
new file mode 100644
index 00000000000..56217be1aae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/inh-ctor38.C
@@ -0,0 +1,19 @@ 
+// CWG 2799
+// Test that inheriting a trivial default constructor produces a trivial
+// default constructor.
+
+// { dg-do compile { target c++11 } }
+
+#include <type_traits>
+
+struct A {
+  A() = default;
+};
+
+struct B : A
+{
+  using A::A;
+  B(int);
+};
+
+static_assert (std::is_trivially_constructible<B>::value, "");