From: Sebastian Redl Date: Sat, 11 Feb 2012 23:51:08 +0000 (+0000) Subject: Fix parsing new expressions using init lists. Probably still do the wrong thing in... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=56a04287a1c713870d1e03206cce785e985cc866;p=clang Fix parsing new expressions using init lists. Probably still do the wrong thing in cases involving array new. Show that many cases using initializer list constructors work, in that they parse and pass semantic analysis. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150316 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 3974909cb2..7dee5ecc5a 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -2182,8 +2182,10 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) { } else if (Tok.is(tok::l_brace) && getLang().CPlusPlus0x) { Diag(Tok.getLocation(), diag::warn_cxx98_compat_generalized_initializer_lists); - // FIXME: Have to communicate the init-list to ActOnCXXNew. - ParseBraceInitializer(); + ExprResult InitList = ParseBraceInitializer(); + if (InitList.isInvalid()) + return InitList; + ConstructorArgs.push_back(InitList.take()); } return Actions.ActOnCXXNew(Start, UseGlobal, PlacementLParen, diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 3493b2a9de..b20dc955c1 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -2750,6 +2750,76 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType, return UnwrappedAnyPointer && Context.hasSameUnqualifiedType(FromType,ToType); } +static OverloadingResult +IsInitializerListConstructorConversion(Sema &S, Expr *From, QualType ToType, + CXXRecordDecl *To, + UserDefinedConversionSequence &User, + OverloadCandidateSet &CandidateSet, + bool AllowExplicit) { + DeclContext::lookup_iterator Con, ConEnd; + for (llvm::tie(Con, ConEnd) = S.LookupConstructors(To); + Con != ConEnd; ++Con) { + NamedDecl *D = *Con; + DeclAccessPair FoundDecl = DeclAccessPair::make(D, D->getAccess()); + + // Find the constructor (which may be a template). + CXXConstructorDecl *Constructor = 0; + FunctionTemplateDecl *ConstructorTmpl + = dyn_cast(D); + if (ConstructorTmpl) + Constructor + = cast(ConstructorTmpl->getTemplatedDecl()); + else + Constructor = cast(D); + + bool Usable = !Constructor->isInvalidDecl() && + S.isInitListConstructor(Constructor) && + (AllowExplicit || !Constructor->isExplicit()); + if (Usable) { + if (ConstructorTmpl) + S.AddTemplateOverloadCandidate(ConstructorTmpl, FoundDecl, + /*ExplicitArgs*/ 0, + &From, 1, CandidateSet, + /*SuppressUserConversions=*/true); + else + S.AddOverloadCandidate(Constructor, FoundDecl, + &From, 1, CandidateSet, + /*SuppressUserConversions=*/true); + } + } + + bool HadMultipleCandidates = (CandidateSet.size() > 1); + + OverloadCandidateSet::iterator Best; + switch (CandidateSet.BestViableFunction(S, From->getLocStart(), Best, true)) { + case OR_Success: { + // Record the standard conversion we used and the conversion function. + CXXConstructorDecl *Constructor = cast(Best->Function); + S.MarkFunctionReferenced(From->getLocStart(), Constructor); + + QualType ThisType = Constructor->getThisType(S.Context); + // Initializer lists don't have conversions as such. + User.Before.setAsIdentityConversion(); + User.HadMultipleCandidates = HadMultipleCandidates; + User.ConversionFunction = Constructor; + User.FoundConversionFunction = Best->FoundDecl; + User.After.setAsIdentityConversion(); + User.After.setFromType(ThisType->getAs()->getPointeeType()); + User.After.setAllToTypes(ToType); + return OR_Success; + } + + case OR_No_Viable_Function: + return OR_No_Viable_Function; + case OR_Deleted: + return OR_Deleted; + case OR_Ambiguous: + return OR_Ambiguous; + } + + llvm_unreachable("Invalid OverloadResult!"); +} + /// Determines whether there is a user-defined conversion sequence /// (C++ [over.ics.user]) that converts expression From to the type /// ToType. If such a conversion exists, User will contain the @@ -2762,8 +2832,8 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType, /// functions (C++0x [class.conv.fct]p2). static OverloadingResult IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType, - UserDefinedConversionSequence& User, - OverloadCandidateSet& CandidateSet, + UserDefinedConversionSequence &User, + OverloadCandidateSet &CandidateSet, bool AllowExplicit) { // Whether we will only visit constructors. bool ConstructorsOnly = false; @@ -2796,9 +2866,17 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType, Expr **Args = &From; unsigned NumArgs = 1; bool ListInitializing = false; - // If we're list-initializing, we pass the individual elements as - // arguments, not the entire list. if (InitListExpr *InitList = dyn_cast(From)) { + // But first, see if there is an init-list-contructor that will work. + OverloadingResult Result = IsInitializerListConstructorConversion( + S, From, ToType, ToRecordDecl, User, CandidateSet, AllowExplicit); + if (Result != OR_No_Viable_Function) + return Result; + // Never mind. + CandidateSet.clear(); + + // If we're list-initializing, we pass the individual elements as + // arguments, not the entire list. Args = InitList->getInits(); NumArgs = InitList->getNumInits(); ListInitializing = true; diff --git a/test/SemaCXX/cxx0x-initializer-constructor.cpp b/test/SemaCXX/cxx0x-initializer-constructor.cpp index c083366865..4858d7af9d 100644 --- a/test/SemaCXX/cxx0x-initializer-constructor.cpp +++ b/test/SemaCXX/cxx0x-initializer-constructor.cpp @@ -3,6 +3,38 @@ struct one { char c[1]; }; struct two { char c[2]; }; +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_;} + }; +} + namespace objects { struct X1 { X1(int); }; @@ -14,6 +46,19 @@ namespace objects { A(int, double) { static_assert(N == 1, ""); } }; + template + struct F { + F() { static_assert(N == 0, ""); } + F(int, double) { static_assert(N == 1, ""); } + F(std::initializer_list) { static_assert(N == 3, ""); } + }; + + template + struct D { + D(std::initializer_list) { static_assert(N == 0, ""); } // expected-note 1 {{candidate}} + D(std::initializer_list) { static_assert(N == 1, ""); } // expected-note 1 {{candidate}} + }; + template struct E { E(int, int) { static_assert(N == 0, ""); } @@ -26,6 +71,22 @@ namespace objects { { A<1> a{1, 1.0}; } { A<1> a = {1, 1.0}; } + { F<0> f{}; } + { F<0> f = {}; } + // Narrowing conversions don't affect viability. The next two choose + // the initializer_list constructor. + // FIXME: Emit narrowing conversion errors. + { F<3> f{1, 1.0}; } // xpected-error {{narrowing conversion}} + { F<3> f = {1, 1.0}; } // xpected-error {{narrowing conversion}} + { F<3> f{1, 2, 3, 4, 5, 6, 7, 8}; } + { F<3> f = {1, 2, 3, 4, 5, 6, 7, 8}; } + { F<3> f{1, 2, 3, 4, 5, 6, 7, 8}; } + { F<3> f{1, 2}; } + + { D<0> d{1, 2, 3}; } + { D<1> d{1.0, 2.0, 3.0}; } + { D<-1> d{1, 2.0}; } // expected-error {{ambiguous}} + { E<0> e{1, 2}; } } @@ -57,6 +118,8 @@ namespace objects { void inline_init() { (void) C{1, 1.0}; (void) new C{1, 1.0}; + (void) A<1>{1, 1.0}; + (void) new A<1>{1, 1.0}; } struct B { // expected-note 2 {{candidate constructor}} @@ -77,5 +140,10 @@ namespace objects { static_assert(sizeof(ov1({{1, 1.0}, 2, {3, 4}})) == sizeof(one), "bad overload"); ov1({1}); // expected-error {{no matching function}} + + one ov2(int); + two ov2(F<3>); + static_assert(sizeof(ov2({1})) == sizeof(one), "bad overload"); // list -> int ranks as identity + static_assert(sizeof(ov2({1, 2, 3})) == sizeof(two), "bad overload"); // list -> F only viable } } diff --git a/test/SemaCXX/cxx98-compat.cpp b/test/SemaCXX/cxx98-compat.cpp index 56e64d23a6..13456e0c8d 100644 --- a/test/SemaCXX/cxx98-compat.cpp +++ b/test/SemaCXX/cxx98-compat.cpp @@ -39,7 +39,8 @@ void Lambda() { } int InitList() { - (void)new int {}; // expected-warning {{generalized initializer lists are incompatible with C++98}} + (void)new int {}; // expected-warning {{generalized initializer lists are incompatible with C++98}} \ + // expected-warning {{scalar initialized from empty initializer list is incompatible with C++98}} (void)int{}; // expected-warning {{generalized initializer lists are incompatible with C++98}} int x { 0 }; // expected-warning {{generalized initializer lists are incompatible with C++98}} return { 0 }; // expected-warning {{generalized initializer lists are incompatible with C++98}} diff --git a/test/SemaCXX/generalized-initializers.cpp b/test/SemaCXX/generalized-initializers.cpp index d696650e3e..e62ff365e9 100644 --- a/test/SemaCXX/generalized-initializers.cpp +++ b/test/SemaCXX/generalized-initializers.cpp @@ -38,89 +38,6 @@ namespace std { }; } -namespace objects { - - struct X1 { X1(int); }; - struct X2 { explicit X2(int); }; - - template - struct A { - A() { static_assert(N == 0, ""); } - A(int, double) { static_assert(N == 1, ""); } - A(std::initializer_list) { static_assert(N == 3, ""); } - }; - - template - struct D { - D(std::initializer_list) { static_assert(N == 0, ""); } // expected-note 1 {{candidate}} - D(std::initializer_list) { static_assert(N == 1, ""); } // expected-note 1 {{candidate}} - }; - - template - struct E { - E(int, int) { static_assert(N == 0, ""); } - E(X1, int) { static_assert(N == 1, ""); } - }; - - void overload_resolution() { - { A<0> a{}; } - { A<0> a = {}; } - // Narrowing conversions don't affect viability. The next two choose - // the initializer_list constructor. - { A<3> a{1, 1.0}; } // expected-error {{narrowing conversion}} - { A<3> a = {1, 1.0}; } // expected-error {{narrowing conversion}} - { A<3> a{1, 2, 3, 4, 5, 6, 7, 8}; } - { A<3> a = {1, 2, 3, 4, 5, 6, 7, 8}; } - { A<3> a{1, 2, 3, 4, 5, 6, 7, 8}; } - { A<3> a{1, 2}; } - - { D<0> d{1, 2, 3}; } - { D<1> d{1.0, 2.0, 3.0}; } - { D<-1> d{1, 2.0}; } // expected-error {{ambiguous}} - - { E<0> e{1, 2}; } - } - - void explicit_implicit() { - { X1 x{0}; } - { X1 x = {0}; } - { X2 x{0}; } - { X2 x = {0}; } // expected-error {{explicit}} - } - - struct C { - C(); - C(int, double); - C(int, int); - C(std::initializer_list); - - int operator[](C); - }; - - C function_call() { - void takes_C(C); - takes_C({1, 1.0}); - - C c; - c[{1, 1.0}]; - - return {1, 1.0}; - } - - void inline_init() { - (void) A<1>{1, 1.0}; - (void) new A<1>{1, 1.0}; - } - - struct B { - B(C, int, C); - }; - - void nested_init() { - B b{{1, 1.0}, 2, {3, 4, 5, 6, 7}}; - } -} - namespace litb { // invalid