From: Sebastian Redl Date: Tue, 17 Jan 2012 22:49:42 +0000 (+0000) Subject: Sema support for initialization of std::initializer_list from initializer lists. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2b916b8b55aaf0152ab9ad630c8454bf6373b085;p=clang Sema support for initialization of std::initializer_list from initializer lists. This does not yet support CodeGen. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@148349 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index f919eb2946..8b4573b371 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -553,7 +553,9 @@ public: /// \brief Pass an object by indirect restore. SK_PassByIndirectRestore, /// \brief Produce an Objective-C object pointer. - SK_ProduceObjCObject + SK_ProduceObjCObject, + /// \brief Construct a std::initializer_list from an initializer list. + SK_StdInitializerList }; /// \brief A single step in the initialization sequence. @@ -657,7 +659,10 @@ public: FK_ListInitializationFailed, /// \brief Initializer has a placeholder type which cannot be /// resolved by initialization. - FK_PlaceholderType + FK_PlaceholderType, + /// \brief Failed to initialize a std::initializer_list because copy + /// construction of some element failed. + FK_InitListElementCopyFailure }; private: @@ -866,6 +871,10 @@ public: /// retaining it). void AddProduceObjCObjectStep(QualType T); + /// \brief Add a step to construct a std::initializer_list object from an + /// initializer list. + void AddStdInitializerListConstructionStep(QualType T); + /// \brief Add steps to unwrap a initializer list for a reference around a /// single element and rewrap it at the end. void RewrapReferenceInitList(QualType T, InitListExpr *Syntactic); @@ -886,7 +895,7 @@ public: return FailedCandidateSet; } - /// brief Get the overloading result, for when the initialization + /// \brief Get the overloading result, for when the initialization /// sequence failed due to a bad overload. OverloadingResult getFailedOverloadResult() const { return FailedOverloadResult; diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index b6033a58f3..2f9b33edeb 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -21,6 +21,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/TypeLoc.h" +#include "llvm/ADT/APInt.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include @@ -2408,6 +2409,7 @@ void InitializationSequence::Step::Destroy() { case SK_PassByIndirectCopyRestore: case SK_PassByIndirectRestore: case SK_ProduceObjCObject: + case SK_StdInitializerList: break; case SK_ConversionSequence: @@ -2445,6 +2447,7 @@ bool InitializationSequence::isAmbiguous() const { case FK_ListInitializationFailed: case FK_VariableLengthArrayHasInitializer: case FK_PlaceholderType: + case FK_InitListElementCopyFailure: return false; case FK_ReferenceInitOverloadFailed: @@ -2777,6 +2780,13 @@ void InitializationSequence::AddProduceObjCObjectStep(QualType T) { Steps.push_back(S); } +void InitializationSequence::AddStdInitializerListConstructionStep(QualType T) { + Step S; + S.Kind = SK_StdInitializerList; + S.Type = T; + Steps.push_back(S); +} + void InitializationSequence::RewrapReferenceInitList(QualType T, InitListExpr *Syntactic) { assert(Syntactic->getNumInits() == 1 && @@ -2839,7 +2849,7 @@ static bool TryListConstructionSpecialCases(Sema &S, CXXRecordDecl *DestRecordDecl, QualType DestType, InitializationSequence &Sequence) { - // C++0x [dcl.init.list]p3: + // C++11 [dcl.init.list]p3: // List-initialization of an object of type T is defined as follows: // - If the initializer list has no elements and T is a class type with // a default constructor, the object is value-initialized. @@ -2876,7 +2886,28 @@ static bool TryListConstructionSpecialCases(Sema &S, } // - Otherwise, if T is a specialization of std::initializer_list, [...] - // FIXME: Implement. + QualType E; + if (S.isStdInitializerList(DestType, &E)) { + // Check that each individual element can be copy-constructed. But since we + // have no place to store further information, we'll recalculate everything + // later. + InitializedEntity HiddenArray = InitializedEntity::InitializeTemporary( + S.Context.getConstantArrayType(E, + llvm::APInt(S.Context.getTypeSize(S.Context.getSizeType()),NumArgs), + ArrayType::Normal, 0)); + InitializedEntity Element = InitializedEntity::InitializeElement(S.Context, + 0, HiddenArray); + for (unsigned i = 0; i < NumArgs; ++i) { + Element.setElementIndex(i); + if (!S.CanPerformCopyInitialization(Element, Args[i])) { + Sequence.SetFailed( + InitializationSequence::FK_InitListElementCopyFailure); + return true; + } + } + Sequence.AddStdInitializerListConstructionStep(DestType); + return true; + } // Not a special case. return false; @@ -4819,7 +4850,8 @@ InitializationSequence::Perform(Sema &S, case SK_ArrayInit: case SK_PassByIndirectCopyRestore: case SK_PassByIndirectRestore: - case SK_ProduceObjCObject: { + case SK_ProduceObjCObject: + case SK_StdInitializerList: { assert(Args.size() == 1); CurInit = Args.get()[0]; if (!CurInit.get()) return ExprError(); @@ -5246,6 +5278,40 @@ InitializationSequence::Perform(Sema &S, CK_ARCProduceObject, CurInit.take(), 0, VK_RValue)); break; + + case SK_StdInitializerList: { + QualType Dest = Step->Type; + QualType E; + bool Success = S.isStdInitializerList(Dest, &E); + (void)Success; + assert(Success && "Destination type changed?"); + InitListExpr *ILE = cast(CurInit.take()); + unsigned NumInits = ILE->getNumInits(); + SmallVector Converted(NumInits); + InitializedEntity HiddenArray = InitializedEntity::InitializeTemporary( + S.Context.getConstantArrayType(E, + llvm::APInt(S.Context.getTypeSize(S.Context.getSizeType()), + NumInits), + ArrayType::Normal, 0)); + InitializedEntity Element =InitializedEntity::InitializeElement(S.Context, + 0, HiddenArray); + for (unsigned i = 0; i < NumInits; ++i) { + Element.setElementIndex(i); + ExprResult Init = S.Owned(ILE->getInit(i)); + ExprResult Res = S.PerformCopyInitialization(Element, + Init.get()->getExprLoc(), + Init); + assert(!Res.isInvalid() && "Result changed since try phase."); + Converted[i] = Res.take(); + } + InitListExpr *Semantic = new (S.Context) + InitListExpr(S.Context, ILE->getLBraceLoc(), + Converted.data(), NumInits, ILE->getRBraceLoc()); + Semantic->setSyntacticForm(ILE); + Semantic->setType(Dest); + CurInit = S.Owned(Semantic); + break; + } } } @@ -5584,6 +5650,37 @@ bool InitializationSequence::Diagnose(Sema &S, // FIXME: Already diagnosed! break; } + + case FK_InitListElementCopyFailure: { + // Try to perform all copies again. + InitListExpr* InitList = cast(Args[0]); + unsigned NumInits = InitList->getNumInits(); + QualType DestType = Entity.getType(); + QualType E; + bool Success = S.isStdInitializerList(DestType, &E); + (void)Success; + assert(Success && "Where did the std::initializer_list go?"); + InitializedEntity HiddenArray = InitializedEntity::InitializeTemporary( + S.Context.getConstantArrayType(E, + llvm::APInt(S.Context.getTypeSize(S.Context.getSizeType()), + NumInits), + ArrayType::Normal, 0)); + InitializedEntity Element = InitializedEntity::InitializeElement(S.Context, + 0, HiddenArray); + // Show at most 3 errors. Otherwise, you'd get a lot of errors for errors + // where the init list type is wrong, e.g. + // std::initializer_list list = { 1, 2, 3, 4, 5, 6, 7, 8 }; + // FIXME: Emit a note if we hit the limit? + int ErrorCount = 0; + for (unsigned i = 0; i < NumInits && ErrorCount < 3; ++i) { + Element.setElementIndex(i); + ExprResult Init = S.Owned(InitList->getInit(i)); + if (S.PerformCopyInitialization(Element, Init.get()->getExprLoc(), Init) + .isInvalid()) + ++ErrorCount; + } + break; + } } PrintInitLocationNote(S, Entity); @@ -5694,6 +5791,10 @@ void InitializationSequence::dump(raw_ostream &OS) const { case FK_ListConstructorOverloadFailed: OS << "list constructor overloading failed"; break; + + case FK_InitListElementCopyFailure: + OS << "copy construction of initializer list element failed"; + break; } OS << '\n'; return; @@ -5815,6 +5916,10 @@ void InitializationSequence::dump(raw_ostream &OS) const { case SK_ProduceObjCObject: OS << "Objective-C object retension"; break; + + case SK_StdInitializerList: + OS << "std::initializer_list from initializer list"; + break; } } } diff --git a/test/SemaCXX/cxx0x-initializer-scalars.cpp b/test/SemaCXX/cxx0x-initializer-scalars.cpp index a12181d158..e0843c2246 100644 --- a/test/SemaCXX/cxx0x-initializer-scalars.cpp +++ b/test/SemaCXX/cxx0x-initializer-scalars.cpp @@ -55,5 +55,4 @@ namespace integral { emptylist({}); emptylist({}, {}, {}); } - } diff --git a/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp b/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp new file mode 100644 index 0000000000..fc0df68575 --- /dev/null +++ b/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp @@ -0,0 +1,60 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s + +namespace std { + typedef decltype(sizeof(int)) size_t; + + // libc++'s implementation + template + class initializer_list + { + const _E* __begin_; + size_t __size_; + + initializer_list(const _E* __b, size_t __s) + : __begin_(__b), + __size_(__s) + {} + + public: + typedef _E value_type; + typedef const _E& reference; + typedef const _E& const_reference; + typedef size_t size_type; + + typedef const _E* iterator; + typedef const _E* const_iterator; + + initializer_list() : __begin_(nullptr), __size_(0) {} + + size_t size() const {return __size_;} + const _E* begin() const {return __begin_;} + const _E* end() const {return __begin_ + __size_;} + }; +} + +struct one { char c[1]; }; +struct two { char c[2]; }; + +struct A { + int a, b; +}; + +struct B { + B(); + B(int, int); +}; + +void simple_list() { + std::initializer_list il = { 1, 2, 3 }; + std::initializer_list dl = { 1.0, 2.0, 3 }; + std::initializer_list al = { {1, 2}, {2, 3}, {3, 4} }; + std::initializer_list bl = { {1, 2}, {2, 3}, {} }; +} + +void function_call() { + void f(std::initializer_list); + f({1, 2, 3}); + + void g(std::initializer_list); + g({ {1, 2}, {2, 3}, {} }); +} diff --git a/test/SemaCXX/generalized-initializers.cpp b/test/SemaCXX/generalized-initializers.cpp index 9e11e267f1..2e7df9aeaf 100644 --- a/test/SemaCXX/generalized-initializers.cpp +++ b/test/SemaCXX/generalized-initializers.cpp @@ -41,8 +41,6 @@ namespace std { namespace integral { void initializer_list() { - std::initializer_list il = { 1, 2, 3 }; - std::initializer_list dl = { 1.0, 2.0, 3 }; auto l = {1, 2, 3, 4}; static_assert(same_type>::value, ""); auto bl = {1, 2.0}; // expected-error {{cannot deduce}} @@ -152,7 +150,7 @@ namespace litb { C c({1, 2}); // expected-error {{}} // valid (by copy constructor). - C d({1, 2L}); // expected-error {{}} + C d({1, 2L}); // valid C e{1, 2};