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