r227224 - Implement the remaining portion of DR1467 from r227022. I may have overlooked a few things, but this implementation comes straight from the DR resolution itself.

Larisse Voufo lvoufo at google.com
Tue Jan 27 10:47:05 PST 2015


Author: lvoufo
Date: Tue Jan 27 12:47:05 2015
New Revision: 227224

URL: http://llvm.org/viewvc/llvm-project?rev=227224&view=rev
Log:
Implement the remaining portion of DR1467 from r227022. I may have overlooked a few things, but this implementation comes straight from the DR resolution itself.

Modified:
    cfe/trunk/lib/Sema/SemaInit.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/test/CXX/drs/dr14xx.cpp
    cfe/trunk/test/CXX/special/class.inhctor/p2.cpp
    cfe/trunk/test/SemaCXX/cxx0x-initializer-constructor.cpp
    cfe/trunk/test/SemaCXX/cxx0x-initializer-references.cpp
    cfe/trunk/test/SemaCXX/explicit.cpp

Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=227224&r1=227223&r2=227224&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Tue Jan 27 12:47:05 2015
@@ -3110,7 +3110,7 @@ ResolveConstructorOverload(Sema &S, Sour
                            ArrayRef<NamedDecl *> Ctors,
                            OverloadCandidateSet::iterator &Best,
                            bool CopyInitializing, bool AllowExplicit,
-                           bool OnlyListConstructors, bool InitListSyntax) {
+                           bool OnlyListConstructors) {
   CandidateSet.clear();
 
   for (ArrayRef<NamedDecl *>::iterator
@@ -3129,20 +3129,13 @@ ResolveConstructorOverload(Sema &S, Sour
       Constructor = cast<CXXConstructorDecl>(D);
 
       // C++11 [over.best.ics]p4:
-      //   However, when considering the argument of a constructor or
-      //   user-defined conversion function that is a candidate:
-      //    -- by 13.3.1.3 when invoked for the copying/moving of a temporary
-      //       in the second step of a class copy-initialization,
-      //    -- by 13.3.1.7 when passing the initializer list as a single
-      //       argument or when the initializer list has exactly one elementand
-      //       a conversion to some class X or reference to (possibly
-      //       cv-qualified) X is considered for the first parameter of a
-      //       constructor of X, or
-      //    -- by 13.3.1.4, 13.3.1.5, or 13.3.1.6 in all cases,
-      //   only standard conversion sequences and ellipsis conversion sequences
-      //   are considered.
-      if ((CopyInitializing || (InitListSyntax && Args.size() == 1)) &&
-          Constructor->isCopyOrMoveConstructor())
+      //   ... and the constructor or user-defined conversion function is a
+      //   candidate by
+      //   — 13.3.1.3, when the argument is the temporary in the second step
+      //     of a class copy-initialization, or
+      //   — 13.3.1.4, 13.3.1.5, or 13.3.1.6 (in all cases),
+      //   user-defined conversion sequences are not considered.
+      if (CopyInitializing && Constructor->isCopyOrMoveConstructor())
         SuppressUserConversions = true;
     }
 
@@ -3222,16 +3215,12 @@ static void TryConstructorInitialization
   OverloadCandidateSet::iterator Best;
   bool AsInitializerList = false;
 
-  // C++14 DR 1467 [over.match.list]p1:
+  // C++11 [over.match.list]p1, per DR1467:
   //   When objects of non-aggregate type T are list-initialized, such that
   //   8.5.4 [dcl.init.list] specifies that overload resolution is performed
   //   according to the rules in this section, overload resolution selects
   //   the constructor in two phases:
   //
-  // C++11 [over.match.list]p1:
-  //   When objects of non-aggregate type T are list-initialized, overload
-  //   resolution selects the constructor in two phases:
-  //
   //   - Initially, the candidate functions are the initializer-list
   //     constructors of the class T and the argument list consists of the
   //     initializer list as a single argument.
@@ -3245,8 +3234,7 @@ static void TryConstructorInitialization
       Result = ResolveConstructorOverload(S, Kind.getLocation(), Args,
                                           CandidateSet, Ctors, Best,
                                           CopyInitialization, AllowExplicit,
-                                          /*OnlyListConstructor=*/true,
-                                          InitListSyntax);
+                                          /*OnlyListConstructor=*/true);
 
     // Time to unwrap the init list.
     Args = MultiExprArg(ILE->getInits(), ILE->getNumInits());
@@ -3262,8 +3250,7 @@ static void TryConstructorInitialization
     Result = ResolveConstructorOverload(S, Kind.getLocation(), Args,
                                         CandidateSet, Ctors, Best,
                                         CopyInitialization, AllowExplicit,
-                                        /*OnlyListConstructors=*/false,
-                                        InitListSyntax);
+                                        /*OnlyListConstructors=*/false);
   }
   if (Result) {
     Sequence.SetOverloadFailure(InitListSyntax ?
@@ -3439,7 +3426,7 @@ static void TryListInitialization(Sema &
     return;
   }
 
-  // C++14 DR1467 [dcl.init.list]p3:
+  // C++11 [dcl.init.list]p3, per DR1467:
   // - If T is a class type and the initializer list has a single element of
   //   type cv U, where U is T or a class derived from T, the object is
   //   initialized from that element (by copy-initialization for
@@ -3449,8 +3436,8 @@ static void TryListInitialization(Sema &
   //   single element that is an appropriately-typed string literal
   //   (8.5.2 [dcl.init.string]), initialization is performed as described
   //   in that section.
-  // - Otherwise, If T is an aggregate, [...] (continue below).
-  if (S.getLangOpts().CPlusPlus14 && InitList->getNumInits() == 1) {
+  // - Otherwise, if T is an aggregate, [...] (continue below).
+  if (S.getLangOpts().CPlusPlus11 && InitList->getNumInits() == 1) {
     if (DestType->isRecordType()) {
       QualType InitType = InitList->getInit(0)->getType();
       if (S.Context.hasSameUnqualifiedType(InitType, DestType) ||
@@ -3519,12 +3506,6 @@ static void TryListInitialization(Sema &
       InitList->getInit(0)->getType()->isRecordType()) {
     //   - Otherwise, if the initializer list has a single element of type E
     //     [...references are handled above...], the object or reference is
-    //     initialized from that element; if a narrowing conversion is required
-    //     to convert the element to T, the program is ill-formed.
-    //
-    // C++14 DR1467:
-    //   - Otherwise, if the initializer list has a single element of type E
-    //     [...references are handled above...], the object or reference is
     //     initialized from that element (by copy-initialization for
     //     copy-list-initialization, or by direct-initialization for
     //     direct-list-initialization); if a narrowing conversion is required

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=227224&r1=227223&r2=227224&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Tue Jan 27 12:47:05 2015
@@ -3336,7 +3336,26 @@ CompareImplicitConversionSequences(Sema
   // Two implicit conversion sequences of the same form are
   // indistinguishable conversion sequences unless one of the
   // following rules apply: (C++ 13.3.3.2p3):
+  
+  // List-initialization sequence L1 is a better conversion sequence than
+  // list-initialization sequence L2 if:
+  // - L1 converts to std::initializer_list<X> for some X and L2 does not, or,
+  //   if not that,
+  // - L1 converts to type “array of N1 T”, L2 converts to type “array of N2 T”,
+  //   and N1 is smaller than N2.,
+  // even if one of the other rules in this paragraph would otherwise apply.
+  if (!ICS1.isBad()) {
+    if (ICS1.isStdInitializerListElement() &&
+        !ICS2.isStdInitializerListElement())
+      return ImplicitConversionSequence::Better;
+    if (!ICS1.isStdInitializerListElement() &&
+        ICS2.isStdInitializerListElement())
+      return ImplicitConversionSequence::Worse;
+  }
+
   if (ICS1.isStandard())
+    // Standard conversion sequence S1 is a better conversion sequence than
+    // standard conversion sequence S2 if [...]
     Result = CompareStandardConversionSequences(S,
                                                 ICS1.Standard, ICS2.Standard);
   else if (ICS1.isUserDefined()) {
@@ -3357,19 +3376,6 @@ CompareImplicitConversionSequences(Sema
                                           ICS2.UserDefined.ConversionFunction);
   }
 
-  // List-initialization sequence L1 is a better conversion sequence than
-  // list-initialization sequence L2 if L1 converts to std::initializer_list<X>
-  // for some X and L2 does not.
-  if (Result == ImplicitConversionSequence::Indistinguishable &&
-      !ICS1.isBad()) {
-    if (ICS1.isStdInitializerListElement() &&
-        !ICS2.isStdInitializerListElement())
-      return ImplicitConversionSequence::Better;
-    if (!ICS1.isStdInitializerListElement() &&
-        ICS2.isStdInitializerListElement())
-      return ImplicitConversionSequence::Worse;
-  }
-
   return Result;
 }
 
@@ -4453,11 +4459,53 @@ TryListConversion(Sema &S, InitListExpr
   if (S.RequireCompleteType(From->getLocStart(), ToType, 0))
     return Result;
 
+  // Per DR1467:
+  //   If the parameter type is a class X and the initializer list has a single
+  //   element of type cv U, where U is X or a class derived from X, the
+  //   implicit conversion sequence is the one required to convert the element
+  //   to the parameter type.
+  //
+  //   Otherwise, if the parameter type is a character array [... ]
+  //   and the initializer list has a single element that is an
+  //   appropriately-typed string literal (8.5.2 [dcl.init.string]), the
+  //   implicit conversion sequence is the identity conversion.
+  if (From->getNumInits() == 1) {
+    if (ToType->isRecordType()) {
+      QualType InitType = From->getInit(0)->getType();
+      if (S.Context.hasSameUnqualifiedType(InitType, ToType) ||
+          S.IsDerivedFrom(InitType, ToType))
+        return TryCopyInitialization(S, From->getInit(0), ToType,
+                                     SuppressUserConversions,
+                                     InOverloadResolution,
+                                     AllowObjCWritebackConversion);
+    }
+    if (S.Context.getAsArrayType(ToType)) {
+      InitializedEntity Entity =
+        InitializedEntity::InitializeParameter(S.Context, ToType,
+                                               /*Consumed=*/false);
+      if (S.CanPerformCopyInitialization(Entity, From)) {
+        Result.setStandard();
+        Result.Standard.setAsIdentityConversion();
+        Result.Standard.setFromType(ToType);
+        Result.Standard.setAllToTypes(ToType);
+        return Result;
+      }
+    }
+  }
+
+  // C++14 [over.ics.list]p2: Otherwise, if the parameter type [...] (below).
   // C++11 [over.ics.list]p2:
   //   If the parameter type is std::initializer_list<X> or "array of X" and
   //   all the elements can be implicitly converted to X, the implicit
   //   conversion sequence is the worst conversion necessary to convert an
   //   element of the list to X.
+  //
+  // C++14 [over.ics.list]p3:
+  //   Otherwise, if the parameter type is “array of N X”, if the initializer
+  //   list has exactly N elements or if it has fewer than N elements and X is
+  //   default-constructible, and if all the elements of the initializer list
+  //   can be implicitly converted to X, the implicit conversion sequence is
+  //   the worst conversion necessary to convert an element of the list to X.
   bool toStdInitializerList = false;
   QualType X;
   if (ToType->isArrayType())
@@ -4496,6 +4544,7 @@ TryListConversion(Sema &S, InitListExpr
     return Result;
   }
 
+  // C++14 [over.ics.list]p4:
   // C++11 [over.ics.list]p3:
   //   Otherwise, if the parameter is a non-aggregate class X and overload
   //   resolution chooses a single best constructor [...] the implicit
@@ -4511,6 +4560,7 @@ TryListConversion(Sema &S, InitListExpr
                                     /*AllowObjCConversionOnExplicit=*/false);
   }
 
+  // C++14 [over.ics.list]p5:
   // C++11 [over.ics.list]p4:
   //   Otherwise, if the parameter has an aggregate type which can be
   //   initialized from the initializer list [...] the implicit conversion
@@ -4537,6 +4587,7 @@ TryListConversion(Sema &S, InitListExpr
     return Result;
   }
 
+  // C++14 [over.ics.list]p6:
   // C++11 [over.ics.list]p5:
   //   Otherwise, if the parameter is a reference, see 13.3.3.1.4.
   if (ToType->isReferenceType()) {
@@ -4605,14 +4656,15 @@ TryListConversion(Sema &S, InitListExpr
     return Result;
   }
 
+  // C++14 [over.ics.list]p7:
   // C++11 [over.ics.list]p6:
   //   Otherwise, if the parameter type is not a class:
   if (!ToType->isRecordType()) {
-    //    - if the initializer list has one element, the implicit conversion
-    //      sequence is the one required to convert the element to the
-    //      parameter type.
+    //    - if the initializer list has one element that is not itself an
+    //      initializer list, the implicit conversion sequence is the one
+    //      required to convert the element to the parameter type.
     unsigned NumInits = From->getNumInits();
-    if (NumInits == 1)
+    if (NumInits == 1 && !dyn_cast<InitListExpr>(From->getInit(0)))
       Result = TryCopyInitialization(S, From->getInit(0), ToType,
                                      SuppressUserConversions,
                                      InOverloadResolution,
@@ -4628,6 +4680,7 @@ TryListConversion(Sema &S, InitListExpr
     return Result;
   }
 
+  // C++14 [over.ics.list]p8:
   // C++11 [over.ics.list]p7:
   //   In all cases other than those enumerated above, no conversion is possible
   return Result;

Modified: cfe/trunk/test/CXX/drs/dr14xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr14xx.cpp?rev=227224&r1=227223&r2=227224&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr14xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr14xx.cpp Tue Jan 27 12:47:05 2015
@@ -227,9 +227,18 @@ namespace std {
     const _E* begin() const {return __begin_;}
     const _E* end()   const {return __begin_ + __size_;}
   };
+
+  template < class _T1, class _T2 > struct pair { _T2 second; };
+
+  template<typename T> struct basic_string {
+    basic_string(const T* x) {}
+    ~basic_string() {};
+  };
+  typedef basic_string<char> string;
+
 } // std
 
-namespace dr1467 {
+namespace dr1467 {  // dr1467: yes c++11
   // List-initialization of aggregate from same-type object
   
   namespace basic0 {
@@ -298,23 +307,138 @@ namespace dr1467 {
     
     X x;
     X x2{x};
-#if __cplusplus == 201103L
-    // expected-error at -2 {{excess elements in struct initializer}}
-#endif
-
-    // TODO: Only Items 1 and 2 from DR1467 are covered for now.
-    // Implement remaining items, and expand here as necessary.
-
   } // dr_example
+  
+  namespace nonaggregate {
+    
+    struct NonAggregate {
+      NonAggregate() {}
+    };
+    
+    struct WantsIt {
+      WantsIt(NonAggregate);
+    };
+    
+    void f(NonAggregate);
+    void f(WantsIt);
+    
+    void test1() {
+      NonAggregate n;
+      f({n});
+    }
+
+    void test2() {
+      NonAggregate x;
+      NonAggregate y{x};
+      NonAggregate z{{x}};
+    }
+
+  } // nonaggregate
 
 } // dr1467
 
 
-namespace dr1490 {
+namespace dr1490 {  // dr1490: yes c++11
   // List-initialization from a string literal
-  
+
   char s[4]{"abc"};                   // Ok
   std::initializer_list<char>{"abc"}; // expected-error {{expected unqualified-id}}}
-  
+
 } // dr1490
+
+namespace dr1589 {   // dr1589: yes c++11
+  // Ambiguous ranking of list-initialization sequences
+
+  void f0(long, int=0);                 // Would makes selection of #0 ambiguous
+  void f0(long);                        // #0
+  void f0(std::initializer_list<int>);  // #00
+  void g0() { f0({1L}); }               // chooses #00
+  
+  void f1(int, int=0);                    // Would make selection of #1 ambiguous
+  void f1(int);                           // #1
+  void f1(std::initializer_list<long>);   // #2
+  void g1() { f1({42}); }                 // chooses #2
+
+  void f2(std::pair<const char*, const char*>, int = 0); // Would makes selection of #3 ambiguous
+  void f2(std::pair<const char*, const char*>); // #3
+  void f2(std::initializer_list<std::string>);  // #4
+  void g2() { f2({"foo","bar"}); }              // chooses #4
+  
+  namespace with_error {
+    
+    void f0(long);                        // #0    expected-note {{candidate function}}
+    void f0(std::initializer_list<int>);  // #00   expected-note {{candidate function}}
+    void f0(std::initializer_list<int>, int = 0);  // Makes selection of #00 ambiguous \
+                                                 // expected-note {{candidate function}}
+    void g0() { f0({1L}); }                 // chooses #00    expected-error{{call to 'f0' is ambiguous}}
+    
+    void f1(int);                           // #1   expected-note {{candidate function}}
+    void f1(std::initializer_list<long>);   // #2   expected-note {{candidate function}}
+    void f1(std::initializer_list<long>, int = 0);   // Makes selection of #00 ambiguous \
+                                                   // expected-note {{candidate function}}
+    void g1() { f1({42}); }                 // chooses #2   expected-error{{call to 'f1' is ambiguous}}
+
+    void f2(std::pair<const char*, const char*>); // #3   TODO: expected- note {{candidate function}}
+    void f2(std::initializer_list<std::string>);  // #4   expected-note {{candidate function}}
+    void f2(std::initializer_list<std::string>, int = 0);   // Makes selection of #00 ambiguous \
+                                                          // expected-note {{candidate function}}
+    void g2() { f2({"foo","bar"}); }        // chooses #4   expected-error{{call to 'f2' is ambiguous}}
+
+  }
+
+} // dr1589
+
+
+namespace dr1631 {  // dr1589: yes c++11
+  // Incorrect overload resolution for single-element initializer-list
+
+  struct A { int a[1]; };
+  struct B { B(int); };
+  void f(B, int);
+  void f(B, int, int = 0);
+  void f(int, A);
+
+  void test() {
+    f({0}, {{1}});
+  }
+
+  namespace with_error {
+    void f(B, int);           // TODO: expected- note {{candidate function}}
+    void f(int, A);           // expected-note {{candidate function}}
+    void f(int, A, int = 0);  // expected-note {{candidate function}}
+
+    void test() {
+      f({0}, {{1}});        // expected-error{{call to 'f' is ambiguous}}
+    }
+  }
+
+} // dr1631
+
+namespace dr1756 {  // dr1490: yes c++11
+  // Direct-list-initialization of a non-class object
+  
+  int a{0};
+  
+  struct X { operator int(); } x;
+  int b{x};
+}
+
+namespace dr1758 {  // dr1758: yes c++11
+  // Explicit conversion in copy/move list initialization
+
+  struct X { X(); };
+  struct Y { explicit operator X(); } y;
+  X x{y};
+
+  struct A {
+    A() {}
+    A(const A &) {}
+  };
+  struct B {
+    operator A() { return A(); }
+  } b;
+  A a{b};
+
+} // dr1758
+
 #endif

Modified: cfe/trunk/test/CXX/special/class.inhctor/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/special/class.inhctor/p2.cpp?rev=227224&r1=227223&r2=227224&view=diff
==============================================================================
--- cfe/trunk/test/CXX/special/class.inhctor/p2.cpp (original)
+++ cfe/trunk/test/CXX/special/class.inhctor/p2.cpp Tue Jan 27 12:47:05 2015
@@ -96,7 +96,7 @@ struct TemplateCtors {
   template<typename T = int> TemplateCtors(int, int = 0, int = 0); // expected-note {{inherited from here}}
 };
 
-struct UsingTemplateCtors : TemplateCtors {
+struct UsingTemplateCtors : TemplateCtors {  // expected-note 2{{candidate is the implicit}}
   using TemplateCtors::TemplateCtors; // expected-note 4{{here}} expected-note {{candidate}}
 
   constexpr UsingTemplateCtors(X<0>, X<0>) {}

Modified: cfe/trunk/test/SemaCXX/cxx0x-initializer-constructor.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx0x-initializer-constructor.cpp?rev=227224&r1=227223&r2=227224&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx0x-initializer-constructor.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx0x-initializer-constructor.cpp Tue Jan 27 12:47:05 2015
@@ -214,7 +214,9 @@ namespace PR12092 {
 
 namespace PR12117 {
   struct A { A(int); }; 
-  struct B { B(A); } b{{0}};
+  struct B { B(A); } b{{0}};   // expected-error {{call to constructor of 'struct B' is ambiguous}} \
+                                // expected-note 2{{candidate is the implicit}} \
+                                // expected-note {{candidate constructor}}
   struct C { C(int); } c{0};
 }
 

Modified: cfe/trunk/test/SemaCXX/cxx0x-initializer-references.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx0x-initializer-references.cpp?rev=227224&r1=227223&r2=227224&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx0x-initializer-references.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx0x-initializer-references.cpp Tue Jan 27 12:47:05 2015
@@ -105,12 +105,13 @@ namespace inner_init {
   B b2 { { 0 } };
   B b3 { { { 0 } } }; // expected-warning {{braces around scalar init}}
 
-  struct C { C(int); };
+  struct C { C(int); };   // expected-note 2{{candidate constructor (the implicit}} \
+                          // expected-note {{candidate constructor not viable: cannot convert initializer list argument to 'int'}}
   struct D { C &&r; };
   D d1 { 0 }; // ok, 0 implicitly converts to C
   D d2 { { 0 } }; // ok, { 0 } calls C(0)
   D d3 { { { 0 } } }; // ok, { { 0 } } calls C({ 0 })
-  D d4 { { { { 0 } } } }; // expected-warning {{braces around scalar init}}
+  D d4 { { { { 0 } } } }; // expected-error {{no matching constructor for initialization of 'inner_init::C &&'}}
 
   struct E { explicit E(int); }; // expected-note 2{{here}}
   struct F { E &&r; };

Modified: cfe/trunk/test/SemaCXX/explicit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/explicit.cpp?rev=227224&r1=227223&r2=227224&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/explicit.cpp (original)
+++ cfe/trunk/test/SemaCXX/explicit.cpp Tue Jan 27 12:47:05 2015
@@ -56,7 +56,7 @@ namespace Conversion {
   void testExplicit()
   {
     // Taken from 12.3.2p2
-    class X { X(); }; // expected-note+ {{candidate constructor}}
+    class X { X(); };
     class Y { }; // expected-note+ {{candidate constructor (the implicit}}
 
     struct Z {
@@ -89,14 +89,10 @@ namespace Conversion {
     const Y& y11{z}; // expected-error {{excess elements}} expected-note {{in initialization of temporary of type 'const Y'}}
     const int& y12{z};
 
-    // X is not an aggregate, so constructors are considered.
-    // However, by 13.3.3.1/4, only standard conversion sequences and
-    // ellipsis conversion sequences are considered here, so this is not
-    // allowed.
-    // FIXME: It's not really clear that this is a sensible restriction for this
-    // case. g++ allows this, EDG does not.
-    const X x1{z}; // expected-error {{no matching constructor}}
-    const X& x2{z}; // expected-error {{no matching constructor}}
+    // X is not an aggregate, so constructors are considered,
+    // per 13.3.3.1/4 & DR1467.
+    const X x1{z};
+    const X& x2{z};
   }
   
   void testBool() {






More information about the cfe-commits mailing list