]> granicus.if.org Git - clang/commitdiff
Fix parsing new expressions using init lists. Probably still do the wrong thing in...
authorSebastian Redl <sebastian.redl@getdesigned.at>
Sat, 11 Feb 2012 23:51:08 +0000 (23:51 +0000)
committerSebastian Redl <sebastian.redl@getdesigned.at>
Sat, 11 Feb 2012 23:51:08 +0000 (23:51 +0000)
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

lib/Parse/ParseExprCXX.cpp
lib/Sema/SemaOverload.cpp
test/SemaCXX/cxx0x-initializer-constructor.cpp
test/SemaCXX/cxx98-compat.cpp
test/SemaCXX/generalized-initializers.cpp

index 3974909cb2ba9eca9d79e763ccd74628d5f344be..7dee5ecc5a81d635830ab6244a301e6b8d23e8e0 100644 (file)
@@ -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,
index 3493b2a9dec8d12ae6e2da8ed7df55f14519da83..b20dc955c1d32f913f0a04744b7c7c8fd74f2f2f 100644 (file)
@@ -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<FunctionTemplateDecl>(D);
+    if (ConstructorTmpl)
+      Constructor
+        = cast<CXXConstructorDecl>(ConstructorTmpl->getTemplatedDecl());
+    else
+      Constructor = cast<CXXConstructorDecl>(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<CXXConstructorDecl>(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<PointerType>()->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,
-                        UserDefinedConversionSequenceUser,
-                        OverloadCandidateSetCandidateSet,
+                        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<InitListExpr>(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;
index c0833668657edc65fbacc933a92a8a1612d09a37..4858d7af9d7e72239554f94ebe77c4fb34d020a0 100644 (file)
@@ -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 _E>
+  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 <int N>
+  struct F {
+    F() { static_assert(N == 0, ""); }
+    F(int, double) { static_assert(N == 1, ""); }
+    F(std::initializer_list<int>) { static_assert(N == 3, ""); }
+  };
+
+  template <int N>
+  struct D {
+    D(std::initializer_list<int>) { static_assert(N == 0, ""); } // expected-note 1 {{candidate}}
+    D(std::initializer_list<double>) { static_assert(N == 1, ""); } // expected-note 1 {{candidate}}
+  };
+
   template <int N>
   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
   }
 }
index 56e64d23a6ea93b51a1e9db3f908e70305fa36e7..13456e0c8d25af003b132c39ea36aaf8bedb4c4a 100644 (file)
@@ -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}}
index d696650e3eb1c0dd7aae3e72eabcc4a67322c794..e62ff365e9898e4c203482a05ac83e32fc9434f6 100644 (file)
@@ -38,89 +38,6 @@ namespace std {
   };
 }
 
-namespace objects {
-
-  struct X1 { X1(int); };
-  struct X2 { explicit X2(int); };
-
-  template <int N>
-  struct A {
-    A() { static_assert(N == 0, ""); }
-    A(int, double) { static_assert(N == 1, ""); }
-    A(std::initializer_list<int>) { static_assert(N == 3, ""); }
-  };
-
-  template <int N>
-  struct D {
-    D(std::initializer_list<int>) { static_assert(N == 0, ""); } // expected-note 1 {{candidate}}
-    D(std::initializer_list<double>) { static_assert(N == 1, ""); } // expected-note 1 {{candidate}}
-  };
-
-  template <int N>
-  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>);
-
-    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