]> granicus.if.org Git - clang/commitdiff
PR13273: When performing list-initialization with an empty initializer list,
authorRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 5 Jul 2012 08:39:21 +0000 (08:39 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 5 Jul 2012 08:39:21 +0000 (08:39 +0000)
actually perform value initialization rather than trying to fake it with a call
to the default constructor. Fixes various bugs related to the previously-missing
zero-initialization in this case.

I've also moved this and the other list initialization 'special case' from
TryConstructorInitialization into TryListInitialization where they belong.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159733 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaInit.cpp
test/CodeGenCXX/const-init-cxx11.cpp
test/CodeGenCXX/constructor-init.cpp
test/SemaCXX/constant-expression-cxx11.cpp
test/SemaCXX/cxx0x-initializer-constructor.cpp

index 3c32dbb10b8771120ba1533fce246cb90192e6c9..903c0c4b18c71810e6a73722354fccc968aeabea 100644 (file)
@@ -2708,84 +2708,39 @@ static void MaybeProduceObjCObject(Sema &S,
   }
 }
 
-/// \brief When initializing from init list via constructor, deal with the
-/// empty init list and std::initializer_list special cases.
+/// \brief When initializing from init list via constructor, handle
+/// initialization of an object of type std::initializer_list<T>.
 ///
-/// \return True if this was a special case, false otherwise.
-static bool TryListConstructionSpecialCases(Sema &S,
-                                            InitListExpr *List,
-                                            CXXRecordDecl *DestRecordDecl,
-                                            QualType DestType,
-                                            InitializationSequence &Sequence) {
-  // C++11 [dcl.init.list]p3:
-  //   List-initialization of an object or reference of type T is defined as
-  //   follows:
-  //   - If T is an aggregate, aggregate initialization is performed.
-  if (DestType->isAggregateType())
+/// \return true if we have handled initialization of an object of type
+/// std::initializer_list<T>, false otherwise.
+static bool TryInitializerListConstruction(Sema &S,
+                                           InitListExpr *List,
+                                           QualType DestType,
+                                           InitializationSequence &Sequence) {
+  QualType E;
+  if (!S.isStdInitializerList(DestType, &E))
     return false;
 
-  //   - Otherwise, if the initializer list has no elements and T is a class
-  //     type with a default constructor, the object is value-initialized.
-  if (List->getNumInits() == 0) {
-    if (CXXConstructorDecl *DefaultConstructor =
-            S.LookupDefaultConstructor(DestRecordDecl)) {
-      if (DefaultConstructor->isDeleted() ||
-          S.isFunctionConsideredUnavailable(DefaultConstructor)) {
-        // Fake an overload resolution failure.
-        OverloadCandidateSet &CandidateSet = Sequence.getFailedCandidateSet();
-        DeclAccessPair FoundDecl = DeclAccessPair::make(DefaultConstructor,
-                                              DefaultConstructor->getAccess());
-        if (FunctionTemplateDecl *ConstructorTmpl =
-                dyn_cast<FunctionTemplateDecl>(DefaultConstructor))
-          S.AddTemplateOverloadCandidate(ConstructorTmpl, FoundDecl,
-                                         /*ExplicitArgs*/ 0,
-                                         ArrayRef<Expr*>(), CandidateSet,
-                                         /*SuppressUserConversions*/ false);
-        else
-          S.AddOverloadCandidate(DefaultConstructor, FoundDecl,
-                                 ArrayRef<Expr*>(), CandidateSet,
-                                 /*SuppressUserConversions*/ false);
-        Sequence.SetOverloadFailure(
-                       InitializationSequence::FK_ListConstructorOverloadFailed,
-                       OR_Deleted);
-      } else
-        Sequence.AddConstructorInitializationStep(DefaultConstructor,
-                                                DefaultConstructor->getAccess(),
-                                                  DestType,
-                                                  /*MultipleCandidates=*/false,
-                                                  /*FromInitList=*/true,
-                                                  /*AsInitList=*/false);
+  // 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()),
+                      List->getNumInits()),
+          ArrayType::Normal, 0));
+  InitializedEntity Element = InitializedEntity::InitializeElement(S.Context,
+      0, HiddenArray);
+  for (unsigned i = 0, n = List->getNumInits(); i < n; ++i) {
+    Element.setElementIndex(i);
+    if (!S.CanPerformCopyInitialization(Element, List->getInit(i))) {
+      Sequence.SetFailed(
+          InitializationSequence::FK_InitListElementCopyFailure);
       return true;
     }
   }
-
-  //   - Otherwise, if T is a specialization of std::initializer_list, [...]
-  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()),
-                        List->getNumInits()),
-            ArrayType::Normal, 0));
-    InitializedEntity Element = InitializedEntity::InitializeElement(S.Context,
-        0, HiddenArray);
-    for (unsigned i = 0, n = List->getNumInits(); i < n; ++i) {
-      Element.setElementIndex(i);
-      if (!S.CanPerformCopyInitialization(Element, List->getInit(i))) {
-        Sequence.SetFailed(
-            InitializationSequence::FK_InitListElementCopyFailure);
-        return true;
-      }
-    }
-    Sequence.AddStdInitializerListConstructionStep(DestType);
-    return true;
-  }
-
-  // Not a special case.
-  return false;
+  Sequence.AddStdInitializerListConstructionStep(DestType);
+  return true;
 }
 
 static OverloadingResult
@@ -2886,11 +2841,6 @@ static void TryConstructorInitialization(Sema &S,
   CXXRecordDecl *DestRecordDecl
     = cast<CXXRecordDecl>(DestRecordType->getDecl());
 
-  if (InitListSyntax &&
-      TryListConstructionSpecialCases(S, cast<InitListExpr>(Args[0]),
-                                      DestRecordDecl, DestType, Sequence))
-    return;
-
   // Build the candidate set directly in the initialization sequence
   // structure, so that it will persist if we fail.
   OverloadCandidateSet &CandidateSet = Sequence.getFailedCandidateSet();
@@ -2917,15 +2867,21 @@ static void TryConstructorInitialization(Sema &S,
   //     constructors of the class T and the argument list consists of the
   //     initializer list as a single argument.
   if (InitListSyntax) {
+    InitListExpr *ILE = cast<InitListExpr>(Args[0]);
     AsInitializerList = true;
-    Result = ResolveConstructorOverload(S, Kind.getLocation(), Args, NumArgs,
-                                        CandidateSet, ConStart, ConEnd, Best,
-                                        CopyInitialization, AllowExplicit,
-                                        /*OnlyListConstructor=*/true,
-                                        InitListSyntax);
+
+    // If the initializer list has no elements and T has a default constructor,
+    // the first phase is omitted.
+    if (ILE->getNumInits() != 0 ||
+        (!DestRecordDecl->hasDeclaredDefaultConstructor() &&
+         !DestRecordDecl->needsImplicitDefaultConstructor()))
+      Result = ResolveConstructorOverload(S, Kind.getLocation(), Args, NumArgs,
+                                          CandidateSet, ConStart, ConEnd, Best,
+                                          CopyInitialization, AllowExplicit,
+                                          /*OnlyListConstructor=*/true,
+                                          InitListSyntax);
 
     // Time to unwrap the init list.
-    InitListExpr *ILE = cast<InitListExpr>(Args[0]);
     Args = ILE->getInits();
     NumArgs = ILE->getNumInits();
   }
@@ -2933,7 +2889,7 @@ static void TryConstructorInitialization(Sema &S,
   // C++11 [over.match.list]p1:
   //   - If no viable initializer-list constructor is found, overload resolution
   //     is performed again, where the candidate functions are all the
-  //     constructors of the class T nad the argument list consists of the
+  //     constructors of the class T and the argument list consists of the
   //     elements of the initializer list.
   if (Result == OR_No_Viable_Function) {
     AsInitializerList = false;
@@ -2951,7 +2907,7 @@ static void TryConstructorInitialization(Sema &S,
     return;
   }
 
-  // C++0x [dcl.init]p6:
+  // C++11 [dcl.init]p6:
   //   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.
@@ -3018,6 +2974,12 @@ static void TryReferenceInitializationCore(Sema &S,
                                            Qualifiers T2Quals,
                                            InitializationSequence &Sequence);
 
+static void TryValueInitialization(Sema &S,
+                                   const InitializedEntity &Entity,
+                                   const InitializationKind &Kind,
+                                   InitializationSequence &Sequence,
+                                   InitListExpr *InitList = 0);
+
 static void TryListInitialization(Sema &S,
                                   const InitializedEntity &Entity,
                                   const InitializationKind &Kind,
@@ -3113,14 +3075,31 @@ static void TryListInitialization(Sema &S,
       return;
     }
 
+    // C++11 [dcl.init.list]p3:
+    //   - If T is an aggregate, aggregate initialization is performed.
     if (!DestType->isAggregateType()) {
       if (S.getLangOpts().CPlusPlus0x) {
+        //   - Otherwise, if the initializer list has no elements and T is a
+        //     class type with a default constructor, the object is
+        //     value-initialized.
+        if (InitList->getNumInits() == 0) {
+          CXXRecordDecl *RD = DestType->getAsCXXRecordDecl();
+          if (RD->hasDeclaredDefaultConstructor() ||
+              RD->needsImplicitDefaultConstructor()) {
+            TryValueInitialization(S, Entity, Kind, Sequence, InitList);
+            return;
+          }
+        }
+
+        //   - Otherwise, if T is a specialization of std::initializer_list<E>,
+        //     an initializer_list object constructed [...]
+        if (TryInitializerListConstruction(S, InitList, DestType, Sequence))
+          return;
+
+        //   - Otherwise, if T is a class type, constructors are considered.
         Expr *Arg = InitList;
-        // A direct-initializer is not list-syntax, i.e. there's no special
-        // treatment of "A a({1, 2});".
-        TryConstructorInitialization(S, Entity, Kind, &Arg, 1, DestType, 
-                                     Sequence,
-                               Kind.getKind() != InitializationKind::IK_Direct);
+        TryConstructorInitialization(S, Entity, Kind, &Arg, 1, DestType,
+                                     Sequence, /*InitListSyntax*/true);
       } else
         Sequence.SetFailed(
             InitializationSequence::FK_InitListBadDestinationType);
@@ -3605,7 +3584,11 @@ static void TryStringLiteralInitialization(Sema &S,
 static void TryValueInitialization(Sema &S,
                                    const InitializedEntity &Entity,
                                    const InitializationKind &Kind,
-                                   InitializationSequence &Sequence) {
+                                   InitializationSequence &Sequence,
+                                   InitListExpr *InitList) {
+  assert((!InitList || InitList->getNumInits() == 0) &&
+         "Shouldn't use value-init for non-empty init lists");
+
   // C++98 [dcl.init]p5, C++11 [dcl.init]p7:
   //
   //   To value-initialize an object of type T means:
@@ -3616,17 +3599,15 @@ static void TryValueInitialization(Sema &S,
 
   if (const RecordType *RT = T->getAs<RecordType>()) {
     if (CXXRecordDecl *ClassDecl = dyn_cast<CXXRecordDecl>(RT->getDecl())) {
-      // C++98:
-      // -- if T is a class type (clause 9) with a user-declared
-      //    constructor (12.1), then the default constructor for T is
-      //    called (and the initialization is ill-formed if T has no
-      //    accessible default constructor);
+      bool NeedZeroInitialization = true;
       if (!S.getLangOpts().CPlusPlus0x) {
+        // C++98:
+        // -- if T is a class type (clause 9) with a user-declared constructor
+        //    (12.1), then the default constructor for T is called (and the
+        //    initialization is ill-formed if T has no accessible default
+        //    constructor);
         if (ClassDecl->hasUserDeclaredConstructor())
-          // FIXME: we really want to refer to a single subobject of the array,
-          // but Entity doesn't have a way to capture that (yet).
-          return TryConstructorInitialization(S, Entity, Kind, 0, 0,
-                                              T, Sequence);
+          NeedZeroInitialization = false;
       } else {
         // C++11:
         // -- if T is a class type (clause 9) with either no default constructor
@@ -3634,8 +3615,7 @@ static void TryValueInitialization(Sema &S,
         //    or deleted, then the object is default-initialized;
         CXXConstructorDecl *CD = S.LookupDefaultConstructor(ClassDecl);
         if (!CD || !CD->getCanonicalDecl()->isDefaulted() || CD->isDeleted())
-          return TryConstructorInitialization(S, Entity, Kind, 0, 0,
-                                              T, Sequence);
+          NeedZeroInitialization = false;
       }
 
       // -- if T is a (possibly cv-qualified) non-union class type without a
@@ -3644,8 +3624,19 @@ static void TryValueInitialization(Sema &S,
       //    default-initialized;
       // FIXME: The 'non-union' here is a defect (not yet assigned an issue
       // number). Update the quotation when the defect is resolved.
-      Sequence.AddZeroInitializationStep(Entity.getType());
-      return TryConstructorInitialization(S, Entity, Kind, 0, 0, T, Sequence);
+      if (NeedZeroInitialization)
+        Sequence.AddZeroInitializationStep(Entity.getType());
+
+      // If this is list-value-initialization, pass the empty init list on when
+      // building the constructor call. This affects the semantics of a few
+      // things (such as whether an explicit default constructor can be called).
+      Expr *InitListAsExpr = InitList;
+      Expr **Args = InitList ? &InitListAsExpr : 0;
+      unsigned NumArgs = InitList ? 1 : 0;
+      bool InitListSyntax = InitList;
+
+      return TryConstructorInitialization(S, Entity, Kind, Args, NumArgs, T,
+                                          Sequence, InitListSyntax);
     }
   }
 
@@ -4100,8 +4091,8 @@ InitializationSequence::InitializationSequence(Sema &S,
         AddArrayInitStep(DestType);
       }
     }
-    // Note: as a GNU C++ extension, we allow initialization of a
-    // class member from a parenthesized initializer list.
+    // Note: as a GNU C++ extension, we allow list-initialization of a
+    // class member of array type from a parenthesized initializer list.
     else if (S.getLangOpts().CPlusPlus &&
              Entity.getKind() == InitializedEntity::EK_Member &&
              Initializer && isa<InitListExpr>(Initializer)) {
@@ -4901,7 +4892,6 @@ InitializationSequence::Perform(Sema &S,
   case SK_QualificationConversionXValue:
   case SK_QualificationConversionRValue:
   case SK_ConversionSequence:
-  case SK_ListConstructorCall:
   case SK_ListInitialization:
   case SK_UnwrapInitList:
   case SK_RewrapInitList:
@@ -4921,6 +4911,7 @@ InitializationSequence::Perform(Sema &S,
   }
 
   case SK_ConstructorInitialization:
+  case SK_ListConstructorCall:
   case SK_ZeroInitialization:
     break;
   }
@@ -5211,7 +5202,8 @@ InitializationSequence::Perform(Sema &S,
       InitializedEntity TempEntity = InitializedEntity::InitializeTemporary(
                                         Entity.getType().getNonReferenceType());
       bool UseTemporary = Entity.getType()->isReferenceType();
-      InitListExpr *InitList = cast<InitListExpr>(CurInit.get());
+      assert(Args.size() == 1 && "expected a single argument for list init");
+      InitListExpr *InitList = cast<InitListExpr>(Args.get()[0]);
       S.Diag(InitList->getExprLoc(), diag::warn_cxx98_compat_ctor_list_init)
         << InitList->getSourceRange();
       MultiExprArg Arg(InitList->getInits(), InitList->getNumInits());
@@ -5259,7 +5251,8 @@ InitializationSequence::Perform(Sema &S,
       step_iterator NextStep = Step;
       ++NextStep;
       if (NextStep != StepEnd &&
-          NextStep->Kind == SK_ConstructorInitialization) {
+          (NextStep->Kind == SK_ConstructorInitialization ||
+           NextStep->Kind == SK_ListConstructorCall)) {
         // The need for zero-initialization is recorded directly into
         // the call to the object's constructor within the next step.
         ConstructorInitRequiresZeroInit = true;
index d1b91ba3f5c890430dc09ba2a15a32c6cb13da63..07385eedbae385a9a34d0135be157ffd0b5036b0 100644 (file)
@@ -309,6 +309,20 @@ namespace VirtualMembers {
   static nsMemoryImpl sGlobalMemory;
 }
 
+namespace PR13273 {
+  struct U {
+    int t;
+    U() = default;
+  };
+
+  struct S : U {
+    S() = default;
+  };
+
+  // CHECK: @_ZN7PR13273L1sE = {{.*}} zeroinitializer
+  const S s {};
+}
+
 // Constant initialization tests go before this point,
 // dynamic initialization tests go after.
 
index 9f808f6680ed0bcbbad2d21ccd17907165f08660..b33184e3966e99a7064fe3bd21083cf738f90b99 100644 (file)
@@ -131,6 +131,26 @@ namespace rdar9694300 {
   }
 }
 
+// Check that we emit a zero initialization step for list-value-initialization
+// which calls a trivial default constructor.
+namespace PR13273 {
+  struct U {
+    int t;
+    U() = default;
+  };
+
+  struct S : U {
+    S() = default;
+  };
+
+  // CHECK: define {{.*}}@_ZN7PR132731fEv(
+  int f() {
+    // CHECK-NOT: }
+    // CHECK: llvm.memset{{.*}}i8 0
+    return (new S{})->t;
+  }
+}
+
 template<typename T>
 struct X {
   X(const X &);
index d1f1f924c666b71ada63d347262f2b2fba29572b..facd4375b0cb546d12301f458377b7b95e365d31 100644 (file)
@@ -1302,3 +1302,19 @@ namespace PR12826 {
   constexpr Foo id(Foo x) { return x; }
   constexpr Foo res(id(Foo()));
 }
+
+namespace PR13273 {
+  struct U {
+    int t;
+    U() = default;
+  };
+
+  struct S : U {
+    S() = default;
+  };
+
+  // S's default constructor isn't constexpr, because U's default constructor
+  // doesn't initialize 't', but it's trivial, so value-initialization doesn't
+  // actually call it.
+  static_assert(S{}.t == 0, "");
+}
index 09aca24b704bdca5b343bdc717db5f611d93ba47..223e140ffc02067bdcf8f3f857772fc3ab13aed5 100644 (file)
@@ -279,5 +279,28 @@ namespace PR12498 {
   {
     c->foo({ nullptr, 1 }); // expected-error{{initialization of incomplete type 'const PR12498::ArrayRef'}}
   }
+}
+
+namespace explicit_default {
+  struct A {
+    explicit A(); // expected-note{{here}}
+  };
+  A a {}; // ok
+  // This is copy-list-initialization, and we choose an explicit constructor
+  // (even though we do so via value-initialization), so the initialization is
+  // ill-formed.
+  A b = {}; // expected-error{{chosen constructor is explicit}}
+}
 
+namespace init_list_default {
+  struct A {
+    A(std::initializer_list<int>);
+  };
+  A a {}; // calls initializer list constructor
+
+  struct B {
+    B();
+    B(std::initializer_list<int>) = delete;
+  };
+  B b {}; // calls default constructor
 }