thakis updated this revision to Diff 48107.
thakis added a comment.

Checkpointing; not production quality. I tried implementing the general DR 253 
rule, and this patch mostly does that. It currently walks all fields and all 
bases for every const record decl; there should probably be a cache for "are 
all fields obviously initialized"; probably on CXXRecordDecl. I had to change 
some test cases, but nothing surprising.

However, while this patch does fix `const std::unordered_map<int, int> um;` 
with libstdc++5.3, it does _not_ fix `std::vector<int> v;` or `const 
std::unordered_map<int, int> um;` when -D_GLIBCXX_DEBUG=1 is passed in. 
Investigating that case more now.


http://reviews.llvm.org/D16552

Files:
  lib/Sema/SemaInit.cpp
  test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp
  test/CXX/dcl.decl/dcl.init/p6.cpp
  test/SemaCXX/attr-selectany.cpp
  test/SemaCXX/constexpr-value-init.cpp
  test/SemaCXX/cxx0x-cursory-default-delete.cpp
  test/SemaCXX/illegal-member-initialization.cpp

Index: test/SemaCXX/illegal-member-initialization.cpp
===================================================================
--- test/SemaCXX/illegal-member-initialization.cpp
+++ test/SemaCXX/illegal-member-initialization.cpp
@@ -7,6 +7,7 @@
 };
 
 struct B {
+  int field;
 };
 
 struct X {
Index: test/SemaCXX/cxx0x-cursory-default-delete.cpp
===================================================================
--- test/SemaCXX/cxx0x-cursory-default-delete.cpp
+++ test/SemaCXX/cxx0x-cursory-default-delete.cpp
@@ -11,6 +11,7 @@
   non_const_copy& operator = (non_const_copy&) &;
   non_const_copy& operator = (non_const_copy&) &&;
   non_const_copy() = default; // expected-note {{not viable}}
+  int uninit_field;
 };
 non_const_copy::non_const_copy(non_const_copy&) = default; // expected-note {{not viable}}
 non_const_copy& non_const_copy::operator = (non_const_copy&) & = default; // expected-note {{not viable}}
@@ -30,6 +31,56 @@
   ncc = cncc; // expected-error {{no viable overloaded}}
 };
 
+struct no_fields { };
+struct all_init {
+  int a = 0;
+  int b = 0;
+};
+struct some_init {
+  int a = 0;
+  int b;
+  int c = 0;
+};
+struct some_init_def {
+  some_init_def() = default;
+  int a = 0;
+  int b;
+  int c = 0;
+};
+struct some_init_ctor {
+  some_init_ctor();
+  int a = 0;
+  int b;
+  int c = 0;
+};
+struct sub_some_init : public some_init_def { };
+struct sub_some_init_ctor : public some_init_def {
+  sub_some_init_ctor();
+};
+struct some_init_container {
+  some_init_def sid;
+};
+struct some_init_container_ctor {
+  some_init_container_ctor();
+  some_init_def sid;
+};
+struct no_fields_container {
+  no_fields nf;
+};
+
+void constobjs() {
+  const no_fields nf; // ok
+  const all_init ai; // ok
+  const some_init si; // expected-error {{default initialization of an object of const type 'const some_init' without a user-provided default constructor}}
+  const some_init_def sid; // expected-error {{default initialization of an object of const type 'const some_init_def' without a user-provided default constructor}}
+  const some_init_ctor sic; // ok
+  const sub_some_init ssi; // expected-error {{default initialization of an object of const type 'const sub_some_init' without a user-provided default constructor}}
+  const sub_some_init_ctor ssic; // ok
+  const some_init_container sicon; // expected-error {{default initialization of an object of const type 'const some_init_container' without a user-provided default constructor}}
+  const some_init_container_ctor siconc; // ok
+  const no_fields_container nfc; // ok
+}
+
 struct non_const_derived : non_const_copy {
   non_const_derived(const non_const_derived&) = default; // expected-error {{requires it to be non-const}}
   non_const_derived& operator =(non_const_derived&) = default;
Index: test/SemaCXX/constexpr-value-init.cpp
===================================================================
--- test/SemaCXX/constexpr-value-init.cpp
+++ test/SemaCXX/constexpr-value-init.cpp
@@ -14,7 +14,7 @@
   constexpr A a; // expected-error {{constant expression}} expected-note {{in call to 'A()'}}
 }
 
-constexpr B b1; // expected-error {{without a user-provided default constructor}}
+constexpr B b1; // ok
 constexpr B b2 = B(); // ok
 static_assert(b2.a.a == 1, "");
 static_assert(b2.a.b == 2, "");
Index: test/SemaCXX/attr-selectany.cpp
===================================================================
--- test/SemaCXX/attr-selectany.cpp
+++ test/SemaCXX/attr-selectany.cpp
@@ -39,7 +39,9 @@
 // The D3D11 headers do something like this.  MSVC doesn't error on this at
 // all, even without the __declspec(selectany), in violation of the standard.
 // We fall back to a warning for selectany to accept headers.
-struct SomeStruct {};
+struct SomeStruct {
+  int foo;
+};
 extern const __declspec(selectany) SomeStruct some_struct; // expected-warning {{default initialization of an object of const type 'const SomeStruct' without a user-provided default constructor is a Microsoft extension}}
 
 // It should be possible to redeclare variables that were defined
Index: test/CXX/dcl.decl/dcl.init/p6.cpp
===================================================================
--- test/CXX/dcl.decl/dcl.init/p6.cpp
+++ test/CXX/dcl.decl/dcl.init/p6.cpp
@@ -4,8 +4,8 @@
 
 // If a program calls for the default initialization of an object of a
 // const-qualified type T, T shall be a class type with a
-// user-provided default constructor.
-struct MakeNonPOD { MakeNonPOD(); };
+// user-provided default constructor, except if T has no uninitialized fields.
+struct MakeNonPOD { MakeNonPOD(); int field; };
 struct NoUserDefault : public MakeNonPOD { };
 struct HasUserDefault { HasUserDefault(); };
 
@@ -16,7 +16,7 @@
 }
 
 // rdar://8501008
-struct s0 {};
+struct s0 { int field; };
 struct s1 { static const s0 foo; };
 const struct s0 s1::foo; // expected-error{{default initialization of an object of const type 'const struct s0' without a user-provided default constructor}}
 
Index: test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp
===================================================================
--- test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp
+++ test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp
@@ -116,6 +116,7 @@
 namespace PR13492 {
   struct B {
     B() = default;
+    int field;
   };
 
   void f() {
Index: lib/Sema/SemaInit.cpp
===================================================================
--- lib/Sema/SemaInit.cpp
+++ lib/Sema/SemaInit.cpp
@@ -363,6 +363,15 @@
   InitListExpr *getFullyStructuredList() const { return FullyStructuredList; }
 };
 
+bool IsContainedInNamespaceStd(Sema& S, CXXRecordDecl *R) {
+  for (NamespaceDecl *ND = dyn_cast<NamespaceDecl>(R->getDeclContext()); ND;
+       ND = dyn_cast<NamespaceDecl>(ND->getParent())) {
+    if (S.getStdNamespace()->InEnclosingNamespaceSetOf(ND))
+      return true;
+  }
+  return false;
+}
+
 } // end anonymous namespace
 
 ExprResult InitListChecker::PerformEmptyInit(Sema &SemaRef,
@@ -422,14 +431,8 @@
     if (CtorDecl->getMinRequiredArguments() == 0 &&
         CtorDecl->isExplicit() && R->getDeclName() &&
         SemaRef.SourceMgr.isInSystemHeader(CtorDecl->getLocation())) {
-      bool IsInStd = false;
-      for (NamespaceDecl *ND = dyn_cast<NamespaceDecl>(R->getDeclContext());
-           ND && !IsInStd; ND = dyn_cast<NamespaceDecl>(ND->getParent())) {
-        if (SemaRef.getStdNamespace()->InEnclosingNamespaceSetOf(ND))
-          IsInStd = true;
-      }
-
-      if (IsInStd && llvm::StringSwitch<bool>(R->getName()) 
+      if (IsContainedInNamespaceStd(SemaRef, R) &&
+          llvm::StringSwitch<bool>(R->getName())
               .Cases("basic_string", "deque", "forward_list", true)
               .Cases("list", "map", "multimap", "multiset", true)
               .Cases("priority_queue", "queue", "set", "stack", true)
@@ -3418,6 +3421,38 @@
   return CandidateSet.BestViableFunction(S, DeclLoc, Best);
 }
 
+static bool HasUninitializedFields(const CXXRecordDecl* R) {
+  for (const auto *F : R->fields()) {
+    if (F->hasInClassInitializer() || F->isUnnamedBitfield())
+      continue;
+    if (CXXRecordDecl *FieldType = F->getType()->getAsCXXRecordDecl()) {
+      if (FieldType->hasUserProvidedDefaultConstructor())
+        continue;
+      if (HasUninitializedFields(FieldType)) {
+FieldType->dump();
+        return true;
+      } else {
+        // Has class type but class has no user-provided default ctor. Only
+        // a problem if that class has uninitialized fields.
+        if (!HasUninitializedFields(FieldType))
+          continue;
+      }
+    }
+    // field has no intializer
+    F->dump();
+    return true;
+  }
+  for (const auto& BI : R->bases()) {
+    const RecordType *RT = BI.getType()->getAs<RecordType>();
+    CXXRecordDecl *Base = cast<CXXRecordDecl>(RT->getDecl());
+    if (HasUninitializedFields(Base)) {
+Base->dump();
+      return true;
+    }
+  }
+  return false;
+}
+
 /// \brief Attempt initialization by constructor (C++ [dcl.init]), which
 /// enumerates the constructors of the initialized entity and performs overload
 /// resolution to select the best.
@@ -3515,18 +3550,24 @@
   //   If a program calls for the default initialization of an object
   //   of a const-qualified type T, T shall be a class type with a
   //   user-provided default constructor.
+  CXXConstructorDecl *CtorDecl = cast<CXXConstructorDecl>(Best->Function);
   if (Kind.getKind() == InitializationKind::IK_Default &&
-      Entity.getType().isConstQualified() &&
-      !cast<CXXConstructorDecl>(Best->Function)->isUserProvided()) {
-    if (!maybeRecoverWithZeroInitialization(S, Sequence, Entity))
-      Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst);
-    return;
+      Entity.getType().isConstQualified() && !CtorDecl->isUserProvided()) {
+    // Some libstdc++ types use `MyType() = default;` which technically isn't
+    // a user-provided default constructor.  For types that don't have
+    // uninitialized fields, suppress the error.  See also core issue 253.
+    CXXRecordDecl *R = CtorDecl->getParent();
+    bool AllowConstDefaultInit = !HasUninitializedFields(R);
+    if (!AllowConstDefaultInit) {
+      if (!maybeRecoverWithZeroInitialization(S, Sequence, Entity))
+        Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst);
+      return;
+    }
   }
 
   // C++11 [over.match.list]p1:
   //   In copy-list-initialization, if an explicit constructor is chosen, the
   //   initializer is ill-formed.
-  CXXConstructorDecl *CtorDecl = cast<CXXConstructorDecl>(Best->Function);
   if (IsListInit && !Kind.AllowExplicit() && CtorDecl->isExplicit()) {
     Sequence.SetFailed(InitializationSequence::FK_ExplicitConstructor);
     return;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to