diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index ca2a5bc22936..0251311dad9d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -50,7 +50,7 @@ bool CIRGenFunction::IsConstructorDelegationValid( // FIXME: Decide if we can do a delegation of a delegating constructor. if (Ctor->isDelegatingConstructor()) - llvm_unreachable("NYI"); + return false; return true; } @@ -585,7 +585,7 @@ void CIRGenFunction::buildCtorPrologue(const CXXConstructorDecl *CD, CXXCtorType CtorType, FunctionArgList &Args) { if (CD->isDelegatingConstructor()) - llvm_unreachable("NYI"); + return buildDelegatingCXXConstructorCall(CD, Args); const CXXRecordDecl *ClassDecl = CD->getParent(); @@ -1379,6 +1379,51 @@ void CIRGenFunction::EnterDtorCleanups(const CXXDestructorDecl *DD, assert(!MissingFeatures::sanitizeDtor()); } +namespace { +struct CallDelegatingCtorDtor final : EHScopeStack::Cleanup { + const CXXDestructorDecl *Dtor; + Address Addr; + CXXDtorType Type; + + CallDelegatingCtorDtor(const CXXDestructorDecl *D, Address Addr, + CXXDtorType Type) + : Dtor(D), Addr(Addr), Type(Type) {} + + void Emit(CIRGenFunction &CGF, Flags flags) override { + // We are calling the destructor from within the constructor. + // Therefore, "this" should have the expected type. + QualType ThisTy = Dtor->getFunctionObjectParameterType(); + CGF.buildCXXDestructorCall(Dtor, Type, /*ForVirtualBase=*/false, + /*Delegating=*/true, Addr, ThisTy); + } +}; +} // end anonymous namespace + +void CIRGenFunction::buildDelegatingCXXConstructorCall( + const CXXConstructorDecl *Ctor, const FunctionArgList &Args) { + assert(Ctor->isDelegatingConstructor()); + + Address ThisPtr = LoadCXXThisAddress(); + + AggValueSlot AggSlot = AggValueSlot::forAddr( + ThisPtr, Qualifiers(), AggValueSlot::IsDestructed, + AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsNotAliased, + AggValueSlot::MayOverlap, AggValueSlot::IsNotZeroed, + // Checks are made by the code that calls constructor. + AggValueSlot::IsSanitizerChecked); + + buildAggExpr(Ctor->init_begin()[0]->getInit(), AggSlot); + + const CXXRecordDecl *ClassDecl = Ctor->getParent(); + if (CGM.getLangOpts().Exceptions && !ClassDecl->hasTrivialDestructor()) { + CXXDtorType Type = + CurGD.getCtorType() == Ctor_Complete ? Dtor_Complete : Dtor_Base; + + EHStack.pushCleanup( + EHCleanup, ClassDecl->getDestructor(), ThisPtr, Type); + } +} + void CIRGenFunction::buildCXXDestructorCall(const CXXDestructorDecl *DD, CXXDtorType Type, bool ForVirtualBase, @@ -1710,4 +1755,4 @@ void CIRGenFunction::buildCXXAggrConstructorCall( if (constantCount.use_empty()) constantCount.erase(); -} \ No newline at end of file +} diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp index d1be80d1f968..89f60f52f34d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp @@ -402,7 +402,9 @@ void CIRGenFunction::buildCXXConstructExpr(const CXXConstructExpr *E, Type = Ctor_Complete; break; case CXXConstructionKind::Delegating: - llvm_unreachable("NYI"); + // We should be emitting a constructor; GlobalDecl will assert this + Type = CurGD.getCtorType(); + Delegating = true; break; case CXXConstructionKind::VirtualBase: ForVirtualBase = true; diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 67ea4dc389dc..7703b6cd6ee7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1649,6 +1649,13 @@ class CIRGenFunction : public CIRGenTypeCache { const FunctionArgList &Args, clang::SourceLocation Loc); + // It's important not to confuse this and the previous function. Delegating + // constructors are the C++11 feature. The constructor delegate optimization + // is used to reduce duplication in the base and complete constructors where + // they are substantially the same. + void buildDelegatingCXXConstructorCall(const CXXConstructorDecl *Ctor, + const FunctionArgList &Args); + /// We are performing a delegate call; that is, the current function is /// delegating to another one. Produce a r-value suitable for passing the /// given parameter. diff --git a/clang/test/CIR/CodeGen/delegating-ctor.cpp b/clang/test/CIR/CodeGen/delegating-ctor.cpp new file mode 100644 index 000000000000..b230ea6f1d5c --- /dev/null +++ b/clang/test/CIR/CodeGen/delegating-ctor.cpp @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -fexceptions -fcxx-exceptions %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +struct Delegating { + Delegating(); + Delegating(int); +}; + +// Check that the constructor being delegated to is called with the correct +// arguments. +Delegating::Delegating() : Delegating(0) {} + +// CHECK-LABEL: cir.func @_ZN10DelegatingC2Ev(%arg0: !cir.ptr {{.*}}) {{.*}} { +// CHECK-NEXT: %0 = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] {alignment = 8 : i64} +// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr, !cir.ptr> +// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr>, !cir.ptr +// CHECK-NEXT: %2 = cir.const #cir.int<0> : !s32i +// CHECK-NEXT: cir.call @_ZN10DelegatingC2Ei(%1, %2) : (!cir.ptr, !s32i) -> () +// CHECK-NEXT: cir.return +// CHECK-NEXT: } + +struct DelegatingWithZeroing { + int i; + DelegatingWithZeroing() = default; + DelegatingWithZeroing(int); +}; + +// Check that the delegating constructor performs zero-initialization here. +// FIXME: we should either emit the trivial default constructor or remove the +// call to it in a lowering pass. +DelegatingWithZeroing::DelegatingWithZeroing(int) : DelegatingWithZeroing() {} + +// CHECK-LABEL: cir.func @_ZN21DelegatingWithZeroingC2Ei(%arg0: !cir.ptr {{.*}}, %arg1: !s32i {{.*}}) {{.*}} { +// CHECK-NEXT: %0 = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] {alignment = 8 : i64} +// CHECK-NEXT: %1 = cir.alloca !s32i, !cir.ptr, ["", init] {alignment = 4 : i64} +// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr, !cir.ptr> +// CHECK-NEXT: cir.store %arg1, %1 : !s32i, !cir.ptr +// CHECK-NEXT: %2 = cir.load %0 : !cir.ptr>, !cir.ptr +// CHECK-NEXT: %3 = cir.const #cir.zero : !ty_22DelegatingWithZeroing22 +// CHECK-NEXT: cir.store %3, %2 : !ty_22DelegatingWithZeroing22, !cir.ptr +// CHECK-NEXT: cir.call @_ZN21DelegatingWithZeroingC2Ev(%2) : (!cir.ptr) -> () extra(#fn_attr1) +// CHECK-NEXT: cir.return +// CHECK-NEXT: } + +void canThrow(); +struct HasNonTrivialDestructor { + HasNonTrivialDestructor(); + HasNonTrivialDestructor(int); + ~HasNonTrivialDestructor(); +}; + +// Check that we call the destructor whenever a cleanup is needed. +// FIXME: enable and check this when exceptions are fully supported. +#if 0 +HasNonTrivialDestructor::HasNonTrivialDestructor(int) + : HasNonTrivialDestructor() { + canThrow(); +} +#endif + +// From clang/test/CodeGenCXX/cxx0x-delegating-ctors.cpp, check that virtual +// inheritance and delegating constructors interact correctly. +// FIXME: enable and check this when virtual inheritance is fully supported. +#if 0 +namespace PR14588 { +void other(); + +class Base { +public: + Base() { squawk(); } + virtual ~Base() {} + + virtual void squawk() { other(); } +}; + +class Foo : public virtual Base { +public: + Foo(); + Foo(const void *inVoid); + virtual ~Foo() {} + + virtual void squawk() { other(); } +}; + +Foo::Foo() : Foo(nullptr) { other(); } +Foo::Foo(const void *inVoid) { squawk(); } +} // namespace PR14588 +#endif