]> granicus.if.org Git - clang/commitdiff
Fully implement delegating constructors!
authorSean Hunt <scshunt@csclub.uwaterloo.ca>
Sun, 1 May 2011 07:04:31 +0000 (07:04 +0000)
committerSean Hunt <scshunt@csclub.uwaterloo.ca>
Sun, 1 May 2011 07:04:31 +0000 (07:04 +0000)
As far as I know, this implementation is complete but might be missing a
few optimizations. Exceptions and virtual bases are handled correctly.

Because I'm an optimist, the web page has appropriately been updated. If
I'm wrong, feel free to downgrade its support categories.

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

14 files changed:
include/clang/AST/DeclCXX.h
include/clang/AST/ExprCXX.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Initialization.h
include/clang/Sema/Sema.h
lib/CodeGen/CGClass.cpp
lib/CodeGen/CGExprCXX.cpp
lib/CodeGen/CodeGenFunction.h
lib/Lex/PPMacroExpansion.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaInit.cpp
test/CodeGenCXX/cxx0x-delegating-ctors.cpp [new file with mode: 0644]
test/SemaCXX/cxx0x-delegating-ctors.cpp [new file with mode: 0644]
www/cxx_status.html

index 9d337382a52e489b2e28f26398f1be70d61545ea..8c819e38781d3834dc876a13b49050cc016d0a0c 100644 (file)
@@ -1608,6 +1608,23 @@ public:
   void setCtorInitializers(CXXCtorInitializer ** initializers) {
     CtorInitializers = initializers;
   }
+
+  /// isDelegatingConstructor - Whether this constructor is a
+  /// delegating constructor
+  bool isDelegatingConstructor() const {
+    return (getNumCtorInitializers() == 1) &&
+      CtorInitializers[0]->isDelegatingInitializer();
+  }
+
+  /// getTargetConstructor - When this constructor delegates to
+  /// another, retrieve the target
+  CXXConstructorDecl *getTargetConstructor() const {
+    if (isDelegatingConstructor())
+      return CtorInitializers[0]->getTargetConstructor();
+    else
+      return 0;
+  }
+
   /// isDefaultConstructor - Whether this constructor is a default
   /// constructor (C++ [class.ctor]p5), which can be used to
   /// default-initialize a class of this type.
index 0040f13cd3080aa29e8cca85f954e10570016e63..a97057973745a7bb1e172a0b8ae7c72cedb57c3b 100644 (file)
@@ -780,7 +780,8 @@ public:
   enum ConstructionKind {
     CK_Complete,
     CK_NonVirtualBase,
-    CK_VirtualBase
+    CK_VirtualBase,
+    CK_Delegating
   };
     
 private:
index 04ff97c1550975b64c018ee88810bca57eaad215..b05e13313e28a19099c87b08022f61f5e6428b91 100644 (file)
@@ -1008,6 +1008,10 @@ def err_delegation_unimplemented : Error<
   "delegating constructors are not fully implemented">;
 def err_delegating_initializer_alone : Error<
   "an initializer for a delegating constructor must appear alone">;
+def err_delegating_ctor_loop : Error<
+  "constructor %0 delegates to itself (possibly indirectly)">;
+def err_delegating_codegen_not_implemented : Error<
+  "code generation for delegating constructors not implemented">;
 
 // C++0x range-based for loop
 def err_for_range_decl_must_be_var : Error<
index d3865ce31423f327bd7da34eeec7919126eb7b94..e83e5c0ccfdaa55d65b3a071b552b544795ae0a9 100644 (file)
@@ -65,7 +65,7 @@ public:
     /// \brief The entity being initialized is a base member subobject.
     EK_Base,
     /// \brief The initialization is being done by a delegating constructor.
-    EK_Delegation,
+    EK_Delegating,
     /// \brief The entity being initialized is an element of a vector.
     /// or vector.
     EK_VectorElement,
@@ -215,7 +215,7 @@ public:
 
   /// \brief Create the initialization entity for a delegated constructor.
   static InitializedEntity InitializeDelegation(QualType Type) {
-    return InitializedEntity(EK_Delegation, SourceLocation(), Type);
+    return InitializedEntity(EK_Delegating, SourceLocation(), Type);
   }
   
   /// \brief Create the initialization entity for a member subobject.
index 0744ced9e98e30c7fd8315b44f5e6cc48f7cc8d3..7dc42b9c569750233e90d9514717ba03a9f1df1f 100644 (file)
@@ -3072,6 +3072,9 @@ public:
                                            SourceLocation LParenLoc,
                                            CXXRecordDecl *ClassDecl);
 
+  bool SetDelegatingInitializer(CXXConstructorDecl *Constructor,
+                                CXXCtorInitializer *Initializer);
+
   bool SetCtorInitializers(CXXConstructorDecl *Constructor,
                            CXXCtorInitializer **Initializers,
                            unsigned NumInitializers, bool AnyErrors);
index 2cb554902e1dab61db6c97723ad40a7ad0ad914a..1227b4c1b248d08828c75d4b91e412b319131b7a 100644 (file)
@@ -658,6 +658,10 @@ static bool IsConstructorDelegationValid(const CXXConstructorDecl *Ctor) {
   if (Ctor->getType()->getAs<FunctionProtoType>()->isVariadic())
     return false;
 
+  // FIXME: Decide if we can do a delegation of a delegating constructor.
+  if (Ctor->isDelegatingConstructor())
+    return false;
+
   return true;
 }
 
@@ -710,6 +714,9 @@ void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) {
 void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD,
                                        CXXCtorType CtorType,
                                        FunctionArgList &Args) {
+  if (CD->isDelegatingConstructor())
+    return EmitDelegatingCXXConstructorCall(CD, Args);
+
   const CXXRecordDecl *ClassDecl = CD->getParent();
 
   llvm::SmallVector<CXXCtorInitializer *, 8> MemberInitializers;
@@ -721,8 +728,10 @@ void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD,
     
     if (Member->isBaseInitializer())
       EmitBaseInitializer(*this, ClassDecl, Member, CtorType);
-    else
+    else if (Member->isAnyMemberInitializer())
       MemberInitializers.push_back(Member);
+    else
+      llvm_unreachable("Delegating initializer on non-delegating constructor");
   }
 
   InitializeVTablePointers(ClassDecl);
@@ -1262,6 +1271,19 @@ CodeGenFunction::EmitDelegateCXXConstructorCall(const CXXConstructorDecl *Ctor,
            ReturnValueSlot(), DelegateArgs, Ctor);
 }
 
+void
+CodeGenFunction::EmitDelegatingCXXConstructorCall(const CXXConstructorDecl *Ctor,
+                                                  const FunctionArgList &Args) {
+  assert(Ctor->isDelegatingConstructor());
+
+  llvm::Value *ThisPtr = LoadCXXThis();
+
+  AggValueSlot AggSlot = AggValueSlot::forAddr(ThisPtr, false, /*Lifetime*/ true);
+
+  EmitAggExpr(Ctor->init_begin()[0]->getInit(), AggSlot);
+}
+
+
 void CodeGenFunction::EmitCXXDestructorCall(const CXXDestructorDecl *DD,
                                             CXXDtorType Type,
                                             bool ForVirtualBase,
index 2f3e4680a79ad746fd92dbedd15f787744750636..e66afc6290c2f7d7393df444db624eb1e52a93b0 100644 (file)
@@ -404,9 +404,16 @@ CodeGenFunction::EmitCXXConstructExpr(const CXXConstructExpr *E,
                                E->arg_begin(), E->arg_end());
   }
   else {
-    CXXCtorType Type = 
-      (E->getConstructionKind() == CXXConstructExpr::CK_Complete) 
-      ? Ctor_Complete : Ctor_Base;
+    CXXCtorType Type;
+    CXXConstructExpr::ConstructionKind K = E->getConstructionKind();
+    if (K == CXXConstructExpr::CK_Delegating) {
+      // We should be emitting a constructor; GlobalDecl will assert this
+      Type = CurGD.getCtorType();
+    } else {
+      Type = (E->getConstructionKind() == CXXConstructExpr::CK_Complete)
+             ? Ctor_Complete : Ctor_Base;
+    }
+
     bool ForVirtualBase = 
       E->getConstructionKind() == CXXConstructExpr::CK_VirtualBase;
     
index 913b5dfa7d59df0f422839be90a0e085db1a2392..169c576f1a0ec0ff3171b350a704ef9ff69947bc 100644 (file)
@@ -1496,6 +1496,12 @@ public:
   void EmitDelegateCXXConstructorCall(const CXXConstructorDecl *Ctor,
                                       CXXCtorType CtorType,
                                       const FunctionArgList &Args);
+  // It's important not to confuse this and the previous function. Delegating
+  // constructors are the C++0x feature. The constructor delegate optimization
+  // is used to reduce duplication in the base and complete consturctors where
+  // they are substantially the same.
+  void EmitDelegatingCXXConstructorCall(const CXXConstructorDecl *Ctor,
+                                        const FunctionArgList &Args);
   void EmitCXXConstructorCall(const CXXConstructorDecl *D, CXXCtorType Type,
                               bool ForVirtualBase, llvm::Value *This,
                               CallExpr::const_arg_iterator ArgBeg,
index bacd62464a311e15cf80aa9093e3e7e8b3ba36eb..d6e0d3a1c08b94eb92d6ea3676d863a85ab8f2da 100644 (file)
@@ -561,6 +561,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
            .Case("cxx_auto_type", LangOpts.CPlusPlus0x)
            .Case("cxx_decltype", LangOpts.CPlusPlus0x)
            .Case("cxx_default_function_template_args", LangOpts.CPlusPlus0x)
+           .Case("cxx_delegating_constructors", LangOpts.CPlusPlus0x)
            .Case("cxx_deleted_functions", LangOpts.CPlusPlus0x)
            .Case("cxx_inline_namespaces", LangOpts.CPlusPlus0x)
          //.Case("cxx_lambdas", false)
index bd4aac8985c6ba79a12fd1eb6cf749fef34f471a..dc0a41e79eb7bdfcdba97a68a356ee12cf826490 100644 (file)
@@ -2068,6 +2068,30 @@ static bool CollectFieldInitializer(BaseAndFieldInfo &Info,
 
   return false;
 }
+
+bool
+Sema::SetDelegatingInitializer(CXXConstructorDecl *Constructor,
+                               CXXCtorInitializer *Initializer) {
+  Constructor->setNumCtorInitializers(1);
+  CXXCtorInitializer **initializer =
+    new (Context) CXXCtorInitializer*[1];
+  memcpy(initializer, &Initializer, sizeof (CXXCtorInitializer*));
+  Constructor->setCtorInitializers(initializer);
+
+  // FIXME: This doesn't catch indirect loops yet
+  CXXConstructorDecl *Target = Initializer->getTargetConstructor();
+  while (Target) {
+    if (Target == Constructor) {
+      Diag(Initializer->getSourceLocation(), diag::err_delegating_ctor_loop)
+        << Constructor;
+      return true;
+    }
+    Target = Target->getTargetConstructor();
+  }
+
+  return false;
+}
+
                                
 bool
 Sema::SetCtorInitializers(CXXConstructorDecl *Constructor,
@@ -2442,7 +2466,11 @@ void Sema::ActOnMemInitializers(Decl *ConstructorDecl,
              diag::err_delegating_initializer_alone)
           << MemInits[0]->getSourceRange();
         HadError = true;
+        // We will treat this as being the only initializer.
       }
+      SetDelegatingInitializer(Constructor, *MemInits);
+      // Return immediately as the initializer is set.
+      return;
     }
   }
 
index 938ace851e62a67622f6fd69f3f958661205ed3e..ca3fd6dfcb456eac43a3cdf42cb00dfb140d2a4f 100644 (file)
@@ -2045,7 +2045,7 @@ DeclarationName InitializedEntity::getName() const {
   case EK_New:
   case EK_Temporary:
   case EK_Base:
-  case EK_Delegation:
+  case EK_Delegating:
   case EK_ArrayElement:
   case EK_VectorElement:
   case EK_BlockElement:
@@ -2068,7 +2068,7 @@ DeclaratorDecl *InitializedEntity::getDecl() const {
   case EK_New:
   case EK_Temporary:
   case EK_Base:
-  case EK_Delegation:
+  case EK_Delegating:
   case EK_ArrayElement:
   case EK_VectorElement:
   case EK_BlockElement:
@@ -2091,7 +2091,7 @@ bool InitializedEntity::allowsNRVO() const {
   case EK_New:
   case EK_Temporary:
   case EK_Base:
-  case EK_Delegation:
+  case EK_Delegating:
   case EK_ArrayElement:
   case EK_VectorElement:
   case EK_BlockElement:
@@ -3346,7 +3346,7 @@ getAssignmentAction(const InitializedEntity &Entity) {
   case InitializedEntity::EK_New:
   case InitializedEntity::EK_Exception:
   case InitializedEntity::EK_Base:
-  case InitializedEntity::EK_Delegation:
+  case InitializedEntity::EK_Delegating:
     return Sema::AA_Initializing;
 
   case InitializedEntity::EK_Parameter:
@@ -3383,7 +3383,7 @@ static bool shouldBindAsTemporary(const InitializedEntity &Entity) {
   case InitializedEntity::EK_New:
   case InitializedEntity::EK_Variable:
   case InitializedEntity::EK_Base:
-  case InitializedEntity::EK_Delegation:
+  case InitializedEntity::EK_Delegating:
   case InitializedEntity::EK_VectorElement:
   case InitializedEntity::EK_Exception:
   case InitializedEntity::EK_BlockElement:
@@ -3405,7 +3405,7 @@ static bool shouldDestroyTemporary(const InitializedEntity &Entity) {
     case InitializedEntity::EK_Result:
     case InitializedEntity::EK_New:
     case InitializedEntity::EK_Base:
-    case InitializedEntity::EK_Delegation:
+    case InitializedEntity::EK_Delegating:
     case InitializedEntity::EK_VectorElement:
     case InitializedEntity::EK_BlockElement:
       return false;
@@ -3490,7 +3490,7 @@ static ExprResult CopyObject(Sema &S,
   case InitializedEntity::EK_Temporary:
   case InitializedEntity::EK_New:
   case InitializedEntity::EK_Base:
-  case InitializedEntity::EK_Delegation:
+  case InitializedEntity::EK_Delegating:
   case InitializedEntity::EK_VectorElement:
   case InitializedEntity::EK_BlockElement:
     Loc = CurInitExpr->getLocStart();
@@ -4061,6 +4061,9 @@ InitializationSequence::Perform(Sema &S,
             CXXConstructExpr::CK_VirtualBase :
             CXXConstructExpr::CK_NonVirtualBase;
         }
+        if (Entity.getKind() == InitializedEntity::EK_Delegating) {
+          ConstructKind = CXXConstructExpr::CK_Delegating;
+        }
 
         // Only get the parenthesis range if it is a direct construction.
         SourceRange parenRange =
diff --git a/test/CodeGenCXX/cxx0x-delegating-ctors.cpp b/test/CodeGenCXX/cxx0x-delegating-ctors.cpp
new file mode 100644 (file)
index 0000000..5b432c7
--- /dev/null
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -emit-llvm -fexceptions -fcxx-exceptions -std=c++0x -o - %s | FileCheck %s
+
+struct non_trivial {
+  non_trivial();
+  ~non_trivial();
+};
+non_trivial::non_trivial() {}
+non_trivial::~non_trivial() {}
+
+// We use a virtual base to ensure that the constructor
+// delegation optimization (complete->base) can't be
+// performed.
+struct delegator {
+  non_trivial n; 
+  delegator();
+  delegator(int);
+  delegator(char);
+  delegator(bool);
+};
+
+delegator::delegator() {
+  throw 0;
+}
+
+// CHECK: define void @_ZN9delegatorC1Ei
+// CHECK: call void @_ZN9delegatorC1Ev
+// CHECK-NOT: lpad
+// CHECK: ret
+// CHECK-NOT: lpad
+// CHECK: define void @_ZN9delegatorC2Ei
+// CHECK: call void @_ZN9delegatorC2Ev
+// CHECK-NOT: lpad
+// CHECK: ret
+// CHECK-NOT: lpad
+delegator::delegator(int)
+  : delegator()
+{}
+
+delegator::delegator(bool)
+{}
+
+// CHECK: define void @_ZN9delegatorC2Ec
+// CHECK: call void @_ZN9delegatorC2Eb
+// CHECK: call void @__cxa_throw
+delegator::delegator(char)
+  : delegator(true) {
+  throw 0;
+}
diff --git a/test/SemaCXX/cxx0x-delegating-ctors.cpp b/test/SemaCXX/cxx0x-delegating-ctors.cpp
new file mode 100644 (file)
index 0000000..b211cb1
--- /dev/null
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++0x -verify %s
+
+struct foo {
+  int i;
+  foo();
+  foo(int);
+  foo(int, int);
+  foo(bool);
+  foo(char);
+  foo(float*);
+  foo(float&);
+};
+
+// Good
+foo::foo (int i) : i(i) {
+}
+// Good
+foo::foo () : foo(-1) {
+}
+// Good
+foo::foo (int, int) : foo() {
+}
+
+foo::foo (bool) : foo(true) { // expected-error{{delegates to itself}}
+}
+
+// Good
+foo::foo (float* f) : foo(*f) {
+}
+
+// FIXME: This should error
+foo::foo (float &f) : foo(&f) {
+}
+
+foo::foo (char) : i(3), foo(3) { // expected-error{{must appear alone}}
+}
index 40c623fd9a70a0cf2f6ac945d1e3999d8b8b91eb..f0361d38a5ba3170b63add6348dfeabdb6f0e290 100644 (file)
@@ -348,10 +348,10 @@ welcome!</p>
 <tr><td colspan="7" class="category">Class Modifications</td></tr>
 <tr>
   <td>delegating constructors</td>
-  <td></td>
-  <td></td>
-  <td></td>
-  <td></td>
+  <td class="complete"></td>
+  <td class="complete"></td>
+  <td class="complete"></td>
+  <td class="complete"></td>
   <td>12.6.2</td>
   <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1986">N1986</a></td>
 </tr>