[cfe-commits] Delegating constructors

Christian Adåker cadaker at gmail.com
Fri Dec 4 01:39:46 PST 2009


Hi.

As a first attempt to get familiar with the clang sources, I have
written some basic support for C++0x delegating constructors.
It lacks proper handling of exceptions with regard to destructor
calls, but exception handling in constructors doesn't seem to be fully
implemented in clang yet, anyway.

I'd be happy for any comments on the patch, and since I'm new to
clang, I'm sure there'll be several. :)

//Christian
-------------- next part --------------
Index: test/SemaCXX/delegating-constructors2.cpp
===================================================================
--- test/SemaCXX/delegating-constructors2.cpp	(revision 0)
+++ test/SemaCXX/delegating-constructors2.cpp	(revision 0)
@@ -0,0 +1,23 @@
+// Test the initializers generated for delegating constructors.
+// Only the target constructor should be generated.
+
+// RUN: clang-cc -std=c++0x -ast-print %s | FileCheck %s
+
+struct A {
+  A(); // non-trivial default constructor
+};
+
+struct B {
+  B(int);
+};
+
+struct C : B {
+  int x;
+  A a;
+
+  // CHECK: C(int xx) : B(xx), x(xx), a() {
+  C(int xx): B(xx), x(xx) {}
+
+  // CHECK: C(int a, int b) : C(a + b) {
+  C(int a, int b): C(a+b) {}
+};
Index: test/SemaCXX/delegating-constructors1.cpp
===================================================================
--- test/SemaCXX/delegating-constructors1.cpp	(revision 0)
+++ test/SemaCXX/delegating-constructors1.cpp	(revision 0)
@@ -0,0 +1,41 @@
+// Tests for C++0x delegating constructors.
+
+// RUN: clang-cc -std=c++0x -fsyntax-only -verify %s
+
+struct B {};
+
+struct A : B {
+  int x;
+
+  A() {}
+  A(int x): x(x) {}
+  A(int* p): B(), A(*p) {}       // expected-error {{more than one initializer}}
+  A(int a, int b): A(a), x(b) {} // expected-error {{more than one initializer}}
+
+  A(const A&a): A(a.x) {}
+
+  A(int *p, int a);
+  A(int a, int* p);
+};
+
+A::A(int* p, int): A(p, *p) {}   // expected-error {{delegate to itself}}
+
+typedef A AA;
+A::A(int a, int* p): AA(a, *p) {}
+
+struct C {
+  int x;
+  C(const C&);                   // expected-note {{candidate}}
+  C(int *x);                     // expected-note {{candidate}}
+  C(double *x): C(*x) {}         // expected-error {{no matching constructor}}\
+                                 // expected-note {{candidate}}
+};
+
+template<typename T>
+struct D {
+  D(T);
+  D(T a, T b): D(a+b) {}
+  D(T *x): D(x) {}               // expected-error {{delegate to itself}}
+};
+
+template struct D<int>;          // expected-note {{in instantiation}}
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td	(revision 90566)
+++ include/clang/Basic/DiagnosticSemaKinds.td	(working copy)
@@ -2066,6 +2066,15 @@
 def error_multiple_base_initialization : Error <
   "multiple initializations given for base %0">;
 
+def err_delegating_to_self : Error<
+  "a delegating constructor can't delegate to itself">;
+
+def err_delegating_too_many_initializers : Error<
+  "delegating constructor has more than one initializer">;
+
+def warn_delegating_constructors : Warning<
+  "delegating constructors are a C++0x extension">;
+
 def err_mem_init_not_member_or_class : Error<
   "member initializer %0 does not name a non-static data member or base "
   "class">;
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp	(revision 90566)
+++ lib/Sema/SemaDeclCXX.cpp	(working copy)
@@ -1201,11 +1201,18 @@
     if (DirectBaseSpec && VirtualBaseSpec)
       return Diag(BaseLoc, diag::err_base_init_direct_and_virtual)
         << BaseType << BaseDInfo->getTypeLoc().getSourceRange();
-    // C++ [base.class.init]p2:
-    // Unless the mem-initializer-id names a nonstatic data membeer of the
-    // constructor's class ot a direst or virtual base of that class, the
-    // mem-initializer is ill-formed.
-    if (!DirectBaseSpec && !VirtualBaseSpec)
+    // C++ [class.base.init]p2:
+    // Unless the mem-initializer-id names the constructor's class,
+    // a non-static data membeer of the constructor's class, or a direct
+    // or virtual base of that class, the mem-initializer is ill-formed.
+
+    if(Context.hasSameType(BaseType, Context.getTypeDeclType(ClassDecl))) {
+      // A C++0x delegating constructor
+      if (!getLangOptions().CPlusPlus0x)
+        Diag(BaseLoc, diag::warn_delegating_constructors)
+          << BaseDInfo->getTypeLoc().getSourceRange();
+    }
+    else if (!DirectBaseSpec && !VirtualBaseSpec)
       return Diag(BaseLoc, diag::err_not_direct_base_or_virtual)
         << BaseType << ClassDecl->getNameAsCString()
         << BaseDInfo->getTypeLoc().getSourceRange();
@@ -1252,18 +1259,34 @@
   llvm::DenseMap<const void *, CXXBaseOrMemberInitializer*> AllBaseFields;
   bool HasDependentBaseInit = false;
   bool HadError = false;
+  bool IsDelegating = false;
+  CXXBaseOrMemberInitializer* DelegationTarget = NULL;
 
   for (unsigned i = 0; i < NumInitializers; i++) {
     CXXBaseOrMemberInitializer *Member = Initializers[i];
     if (Member->isBaseInitializer()) {
       if (Member->getBaseClass()->isDependentType())
         HasDependentBaseInit = true;
+
+      if (Member->getConstructor() &&
+          Member->getConstructor()->getParent() == ClassDecl) {
+        IsDelegating = true;
+        if (!DelegationTarget)
+          DelegationTarget = Member;
+      }
+
       AllBaseFields[Member->getBaseClass()->getAs<RecordType>()] = Member;
     } else {
       AllBaseFields[Member->getMember()] = Member;
     }
   }
 
+  if (IsDelegating && NumInitializers != 1) {
+    Diag(Constructor->getLocation(), diag::err_delegating_too_many_initializers)
+      << Constructor->getSourceRange();
+    HadError = true;
+  }
+
   if (HasDependentBaseInit) {
     // FIXME. This does not preserve the ordering of the initializers.
     // Try (with -Wreorder)
@@ -1286,6 +1309,17 @@
       if (Member->isBaseInitializer())
         AllToInit.push_back(Member);
     }
+  } else if (IsDelegating) {
+    assert(DelegationTarget && DelegationTarget->isBaseInitializer());
+
+    // FIXME: Where possible, report cyclic delegations.
+    if (DelegationTarget->getConstructor() == Constructor) {
+      Diag(DelegationTarget->getSourceLocation(), diag::err_delegating_to_self)
+        << DelegationTarget->getSourceRange();
+      HadError = true;
+    }
+
+    AllToInit.append(Initializers, Initializers+NumInitializers);
   } else {
     // Push virtual bases before others.
     for (CXXRecordDecl::base_class_iterator VBase =
@@ -1394,6 +1428,10 @@
   // non-static data members.
   for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(),
        E = ClassDecl->field_end(); Field != E; ++Field) {
+
+    if (IsDelegating)
+      break; // Leave initialization to the target constructor.
+
     if ((*Field)->isAnonymousStructOrUnion()) {
       if (const RecordType *FieldClassType =
           Field->getType()->getAs<RecordType>()) {


More information about the cfe-commits mailing list