[clang] bc713f6 - PR48763: Better handling for classes that inherit a default constructor.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 18 18:54:29 PST 2021


Author: Richard Smith
Date: 2021-01-18T18:54:04-08:00
New Revision: bc713f6a004723d1325bc16e1efc32d0ac82f939

URL: https://github.com/llvm/llvm-project/commit/bc713f6a004723d1325bc16e1efc32d0ac82f939
DIFF: https://github.com/llvm/llvm-project/commit/bc713f6a004723d1325bc16e1efc32d0ac82f939.diff

LOG: PR48763: Better handling for classes that inherit a default constructor.

The C++ standard wording doesn't appear to properly handle the case
where a class inherits a default constructor from a base class. Various
properties of classes are defined in terms of the corresponding property
of the default constructor, and in this case, the class does not have a
default constructor despite being default-constructible, which the
wording doesn't handle properly.

This change implements a tentative fix for these problems, which has
also been proposed to the C++ committee: if a class would inherit a
default constructor, and does not explicitly declare one, then one is
implicitly declared.

Added: 
    

Modified: 
    clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
    clang/include/clang/AST/DeclCXX.h
    clang/lib/AST/DeclCXX.cpp
    clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p15.cpp
    clang/test/CXX/special/class.ctor/p6-0x.cpp
    clang/test/CXX/special/class.inhctor/p1.cpp
    clang/test/CXX/special/class.inhctor/p2.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
index 4ce6771259d9..d15d6698860f 100644
--- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
+++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
@@ -131,6 +131,10 @@ FIELD(HasUninitializedFields, 1, NO_MERGE)
 /// constructors from a base class.
 FIELD(HasInheritedConstructor, 1, NO_MERGE)
 
+/// True if there are any member using-declarations that inherit
+/// default constructors from a base class.
+FIELD(HasInheritedDefaultConstructor, 1, NO_MERGE)
+
 /// True if there are any member using-declarations named
 /// 'operator='.
 FIELD(HasInheritedAssignment, 1, NO_MERGE)

diff  --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 568eeb614a76..e32101bb2276 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -744,9 +744,14 @@ class CXXRecordDecl : public RecordDecl {
   ///
   /// This value is used for lazy creation of default constructors.
   bool needsImplicitDefaultConstructor() const {
-    return !data().UserDeclaredConstructor &&
-           !(data().DeclaredSpecialMembers & SMF_DefaultConstructor) &&
-           (!isLambda() || lambdaIsDefaultConstructibleAndAssignable());
+    return (!data().UserDeclaredConstructor &&
+            !(data().DeclaredSpecialMembers & SMF_DefaultConstructor) &&
+            (!isLambda() || lambdaIsDefaultConstructibleAndAssignable())) ||
+           // FIXME: Proposed fix to core wording issue: if a class inherits
+           // a default constructor and doesn't explicitly declare one, one
+           // is declared implicitly.
+           (data().HasInheritedDefaultConstructor &&
+            !(data().DeclaredSpecialMembers & SMF_DefaultConstructor));
   }
 
   /// Determine whether this class has any user-declared constructors.

diff  --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index b806adf36bfb..0368ada0b81c 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -81,7 +81,9 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
       HasPublicFields(false), HasMutableFields(false), HasVariantMembers(false),
       HasOnlyCMembers(true), HasInClassInitializer(false),
       HasUninitializedReferenceMember(false), HasUninitializedFields(false),
-      HasInheritedConstructor(false), HasInheritedAssignment(false),
+      HasInheritedConstructor(false),
+      HasInheritedDefaultConstructor(false),
+      HasInheritedAssignment(false),
       NeedOverloadResolutionForCopyConstructor(false),
       NeedOverloadResolutionForMoveConstructor(false),
       NeedOverloadResolutionForCopyAssignment(false),
@@ -814,6 +816,8 @@ void CXXRecordDecl::addedMember(Decl *D) {
     //   constructor [...]
     if (Constructor->isConstexpr() && !Constructor->isCopyOrMoveConstructor())
       data().HasConstexprNonCopyMoveConstructor = true;
+    if (!isa<CXXConstructorDecl>(D) && Constructor->isDefaultConstructor())
+      data().HasInheritedDefaultConstructor = true;
   }
 
   // Handle destructors.

diff  --git a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p15.cpp b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p15.cpp
index d64807ed5abd..4951ed6eb959 100644
--- a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p15.cpp
+++ b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p15.cpp
@@ -36,7 +36,7 @@ namespace default_ctor {
   };
 
   struct A {
-    A(); // expected-note {{candidate}}
+    A();
 
     A(C &&);
     C &operator=(C&&);
@@ -48,7 +48,7 @@ namespace default_ctor {
   };
 
   struct B {
-    B(); // expected-note {{candidate}}
+    B();
 
     B(C &&);
     C &operator=(C&&);
@@ -66,9 +66,9 @@ namespace default_ctor {
     using B::operator=;
   };
   struct D : A, B {
-    using A::A; // expected-note 2{{inherited here}}
+    using A::A; // expected-note {{inherited here}}
     using A::operator=;
-    using B::B; // expected-note 2{{inherited here}}
+    using B::B; // expected-note {{inherited here}}
     using B::operator=;
 
     D(int);
@@ -84,7 +84,9 @@ namespace default_ctor {
 
   // D does not declare D(), D(D&&), nor operator=(D&&), so the base class
   // versions are inherited.
-  D d; // expected-error {{ambiguous}}
+  // Exception: we implicitly declare a default constructor so that we can
+  // reason about its properties.
+  D d; // ok, implicitly declared
   void f(D d) {
     D d2(static_cast<D&&>(d)); // ok, ignores inherited constructors
     D d3(convert_to_D1{}); // ok, ignores inherited constructors

diff  --git a/clang/test/CXX/special/class.ctor/p6-0x.cpp b/clang/test/CXX/special/class.ctor/p6-0x.cpp
index 9860317aa122..156a2b20c6b5 100644
--- a/clang/test/CXX/special/class.ctor/p6-0x.cpp
+++ b/clang/test/CXX/special/class.ctor/p6-0x.cpp
@@ -94,3 +94,34 @@ namespace UnionCtors {
     friend constexpr E::E() noexcept; // expected-error {{follows non-constexpr declaration}}
   };
 }
+
+namespace PR48763 {
+  // FIXME: We implement a speculative wording fix here: if a class inherits a
+  // default constructor and doesn't declare one itself, we declare an default
+  // constructor implicitly. This allows us to meanignfully reason about
+  // whether that default constructor is constexpr, trivial, and so on.
+  struct A { constexpr A() {} };
+  struct B : A {
+    using A::A;
+    constexpr B(int) {}
+  };
+  struct C { B b; };
+  constexpr C c;
+
+  struct D { int n; };
+  struct E : D { using D::D; E(int); };
+  static_assert(E().n == 0, "");
+  static_assert(E{}.n == 0, "");
+
+  struct F { E e; };
+  static_assert(F().e.n == 0, "");
+  static_assert(F{}.e.n == 0, "");
+
+  union U { E e; };
+  U u; // OK, trivial default constructor
+
+  struct G { G(); };
+  struct H : D { using D::D; H(int); G g; };
+  union V { H h; }; // expected-note {{field 'h' has a non-trivial default constructor}}
+  V v; // expected-error {{deleted}}
+}

diff  --git a/clang/test/CXX/special/class.inhctor/p1.cpp b/clang/test/CXX/special/class.inhctor/p1.cpp
index 389f365b7428..d116a3a561d8 100644
--- a/clang/test/CXX/special/class.inhctor/p1.cpp
+++ b/clang/test/CXX/special/class.inhctor/p1.cpp
@@ -4,9 +4,9 @@
 // for the wording that used to be there.
 
 struct A {
-  A(...); // expected-note {{candidate constructor}} expected-note {{candidate inherited constructor}}
-  A(int = 0, int = 0, int = 0, int = 0, ...); // expected-note 3{{candidate constructor}} expected-note 3{{candidate inherited constructor}}
-  A(int = 0, int = 0, ...); // expected-note 3{{candidate constructor}} expected-note 3{{candidate inherited constructor}}
+  A(...); // expected-note {{candidate constructor}}
+  A(int = 0, int = 0, int = 0, int = 0, ...); // expected-note 3{{candidate constructor}} expected-note 2{{candidate inherited constructor}}
+  A(int = 0, int = 0, ...); // expected-note 3{{candidate constructor}} expected-note 2{{candidate inherited constructor}}
 
   template<typename T> A(T, int = 0, ...);
 
@@ -14,15 +14,15 @@ struct A {
   template<typename T, int N> A(const T (&)[N], int = 0); // expected-note {{candidate constructor}} expected-note {{candidate inherited constructor}}
 };
 
-struct B : A {
-  using A::A; // expected-note 9{{inherited here}}
+struct B : A { // expected-note {{because base class 'A' has multiple default constructors}}
+  using A::A; // expected-note 6{{inherited here}}
   B(void*);
 };
 
 struct C {} c;
 
 A a0{}; // expected-error {{ambiguous}}
-B b0{}; // expected-error {{ambiguous}}
+B b0{}; // expected-error {{deleted}}
 
 A a1{1}; // expected-error {{ambiguous}}
 B b1{1}; // expected-error {{ambiguous}}

diff  --git a/clang/test/CXX/special/class.inhctor/p2.cpp b/clang/test/CXX/special/class.inhctor/p2.cpp
index f84dc6487573..9bb3f0c1507f 100644
--- a/clang/test/CXX/special/class.inhctor/p2.cpp
+++ b/clang/test/CXX/special/class.inhctor/p2.cpp
@@ -96,7 +96,7 @@ static_assert(ce.k2 == 'x', "");
 
 
 struct TemplateCtors { // expected-note 2{{candidate constructor (the implicit}}
-  constexpr TemplateCtors() {} // expected-note {{candidate inherited constructor}}
+  constexpr TemplateCtors() {}
   template<template<int> class T> TemplateCtors(X<0>, T<0>); // expected-note {{here}} expected-note {{candidate inherited constructor}}
   template<int N> TemplateCtors(X<1>, X<N>); // expected-note {{here}} expected-note {{candidate inherited constructor}}
   template<typename T> TemplateCtors(X<2>, T); // expected-note {{here}} expected-note {{candidate inherited constructor}}
@@ -104,8 +104,8 @@ struct TemplateCtors { // expected-note 2{{candidate constructor (the implicit}}
   template<typename T = int> TemplateCtors(int, int = 0, int = 0);
 };
 
-struct UsingTemplateCtors : TemplateCtors { // expected-note 2{{candidate constructor (the implicit}}
-  using TemplateCtors::TemplateCtors; // expected-note 6{{inherited here}}
+struct UsingTemplateCtors : TemplateCtors { // expected-note 3{{candidate constructor (the implicit}}
+  using TemplateCtors::TemplateCtors; // expected-note 5{{inherited here}}
 
   constexpr UsingTemplateCtors(X<0>, X<0>) {} // expected-note {{not viable}}
   constexpr UsingTemplateCtors(X<1>, X<1>) {} // expected-note {{not viable}}


        


More information about the cfe-commits mailing list