thakis created this revision.
thakis added a reviewer: rsmith.
thakis added a subscriber: cfe-commits.

libstdc++ is using `= default` instead of `{}` for more and more of its 
container classes, because they think DR 253 should be accepted 
(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60284). Since clang doesn't 
implement DR 253, clang complains on definitions of empty const objects of 
these containers. This is unfortunate, since `const std::vector<int> 
empty_vec;` builds fine with gcc, clang with older libstdc++ versions, clang 
with libc++, and Visual Studio.

This patch adds a clang-side workaround for libstdc++ types: For types in 
system headers that are in namespace std and that have no uninitialized fields 
and an explicitly defaulted constructed, clang now allows defining const 
objects of that type. Related to PR23381. Similar in spirit to r212238.

http://reviews.llvm.org/D16552

Files:
  lib/Sema/SemaInit.cpp
  test/SemaCXX/cxx0x-const-defaulted-ctor-system-header.cpp

Index: test/SemaCXX/cxx0x-const-defaulted-ctor-system-header.cpp
===================================================================
--- test/SemaCXX/cxx0x-const-defaulted-ctor-system-header.cpp
+++ test/SemaCXX/cxx0x-const-defaulted-ctor-system-header.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+// libstdc++5.3 uses `= default` for its container constructors.
+#ifdef BE_THE_HEADER
+#pragma clang system_header
+namespace std {
+template <class T>
+struct vector {
+  vector() = default;
+};
+}
+#else
+
+#define BE_THE_HEADER
+#include __FILE__
+const std::vector<int> empty_vector; // no diagnostic
+#endif
Index: lib/Sema/SemaInit.cpp
===================================================================
--- lib/Sema/SemaInit.cpp
+++ lib/Sema/SemaInit.cpp
@@ -360,6 +360,15 @@
   // semantic analysis and code generation.
   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,
@@ -419,16 +428,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)
@@ -3420,6 +3421,18 @@
   return CandidateSet.BestViableFunction(S, DeclLoc, Best);
 }
 
+static bool HasUninitializedFields(CXXRecordDecl* R) {
+  for (const auto *F : R->fields()) {
+    if (F->hasInClassInitializer() || F->isUnnamedBitfield())
+      continue;
+    if (CXXRecordDecl *FieldType = F->getType()->getAsCXXRecordDecl())
+      if (FieldType->hasDefaultConstructor())
+        continue;
+    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.
@@ -3517,18 +3530,28 @@
   //   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 in system headers that
+    // are in namespace std and that only have fields that have default
+    // constructors, look the other way.  See also core issue 253.
+    CXXRecordDecl *R = CtorDecl->getParent();
+    bool AllowConstDefaultInit =
+        S.SourceMgr.isInSystemHeader(CtorDecl->getLocation()) &&
+        CtorDecl->isDefaulted() && IsContainedInNamespaceStd(S, R) &&
+        !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