From 4c7718d51b743b621d7c215eff78004c5ea40cd8 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 20 Jul 2016 21:02:43 +0000 Subject: [PATCH] When copying an array into a lambda, destroy temporaries from the copy-constructor immediately and enter a partial array cleanup for previously-copied elements. Fixes PR28595. llvm-svn: 276180 --- clang/lib/CodeGen/CGClass.cpp | 35 +++++++++++++ clang/test/CodeGenCXX/lambda-expressions.cpp | 54 +++++++++++++++++++- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 7ed891f426aa..4d69c3ff5be0 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -562,6 +562,20 @@ static void EmitBaseInitializer(CodeGenFunction &CGF, isBaseVirtual); } +/// Initialize a member of aggregate type using the given expression +/// as an initializer. +/// +/// The member may be an array. If so: +/// - the destination l-value will be a pointer of the *base* element type, +/// - ArrayIndexVar will be a pointer to a variable containing the current +/// index within the destination array, and +/// - ArrayIndexes will be an array of index variables, one for each level +/// of array nesting, which will need to be updated as appropriate for the +/// array structure. +/// +/// On an array, this function will invoke itself recursively. Each time, +/// it drills into one nesting level of the member type and sets up a +/// loop updating the appropriate array index variable. static void EmitAggMemberInitializer(CodeGenFunction &CGF, LValue LHS, Expr *Init, @@ -569,10 +583,18 @@ static void EmitAggMemberInitializer(CodeGenFunction &CGF, QualType T, ArrayRef ArrayIndexes, unsigned Index) { + assert(ArrayIndexVar.isValid() == (ArrayIndexes.size() != 0)); + if (Index == ArrayIndexes.size()) { LValue LV = LHS; + Optional Scope; + if (ArrayIndexVar.isValid()) { + // When we're processing an array, the temporaries from each + // element's construction are destroyed immediately. + Scope.emplace(CGF); + // If we have an array index variable, load it and use it as an offset. // Then, increment the value. llvm::Value *Dest = LHS.getPointer(); @@ -586,6 +608,19 @@ static void EmitAggMemberInitializer(CodeGenFunction &CGF, CharUnits EltSize = CGF.getContext().getTypeSizeInChars(T); CharUnits Align = LV.getAlignment().alignmentOfArrayElement(EltSize); LV.setAddress(Address(Dest, Align)); + + // Enter a partial-array EH cleanup to destroy previous members + // of the array if this initialization throws. + if (CGF.CGM.getLangOpts().Exceptions) { + if (auto DtorKind = T.isDestructedType()) { + if (CGF.needsEHCleanup(DtorKind)) { + CGF.pushRegularPartialArrayCleanup(LHS.getPointer(), + LV.getPointer(), T, + LV.getAlignment(), + CGF.getDestroyer(DtorKind)); + } + } + } } switch (CGF.getEvaluationKind(T)) { diff --git a/clang/test/CodeGenCXX/lambda-expressions.cpp b/clang/test/CodeGenCXX/lambda-expressions.cpp index f59d360314e4..2006f0f81efe 100644 --- a/clang/test/CodeGenCXX/lambda-expressions.cpp +++ b/clang/test/CodeGenCXX/lambda-expressions.cpp @@ -116,6 +116,56 @@ int *PR22071_fun() { return [&] { return &y; }(); } +namespace pr28595 { + struct Temp { + Temp(); + ~Temp() noexcept(false); + }; + struct A { + A(); + A(const A &a, const Temp &temp = Temp()); + ~A(); + }; + + // CHECK-LABEL: define void @_ZN7pr285954testEv() + void test() { + // CHECK: [[ARRAY:%.*]] = alloca [3 x [5 x [[A:%.*]]]], align 1 + // CHECK: [[DESTIDX:%.*]] = alloca i64, align 8 + // CHECK: [[I0:%.*]] = alloca i64, align 8 + // CHECK: [[I1:%.*]] = alloca i64, align 8 + A array[3][5]; + + // CHECK: [[DESTBASE:%.*]] = bitcast [3 x [5 x [[A]]]]* {{.*}} to [[A]]* + // CHECK: store i64 0, i64* [[DESTIDX]], align 8 + // CHECK: store i64 0, i64* [[I0]], align 8 + // CHECK: br label + // CHECK: icmp ult + // CHECK: store i64 0, i64* [[I1]], align 8 + // CHECK: br label + // CHECK: icmp ult + // CHECK: [[T0:%.*]] = load i64, i64* [[DESTIDX]], align 8 + // CHECK: [[DEST:%.*]] = getelementptr inbounds [[A]], [[A]]* [[DESTBASE]], i64 [[T0]] + // CHECK: invoke void @_ZN7pr285954TempC1Ev + // CHECK: invoke void @_ZN7pr285951AC1ERKS0_RKNS_4TempE + // CHECK: invoke void @_ZN7pr285954TempD1Ev + // CHECK: landingpad + // CHECK: landingpad + // CHECK: br label [[CLEANUP:%.*]]{{$}} + // CHECK: landingpad + // CHECK: invoke void @_ZN7pr285954TempD1Ev + // CHECK: br label [[CLEANUP]] + // CHECK: icmp eq [[A]]* [[DESTBASE]], [[DEST]] + // CHECK: [[T0:%.*]] = phi [[A]]* + // CHECK: [[T1:%.*]] = getelementptr inbounds [[A]], [[A]]* [[T0]], i64 -1 + // CHECK: call void @_ZN7pr285951AD1Ev([[A]]* [[T1]]) + // CHECK: icmp eq [[A]]* [[T1]], [[DESTBASE]] + (void) [array]{}; + + // Skip over the initialization loop. + // CHECK: [[BEGIN:%.*]] = getelementptr inbounds [3 x [5 x [[A]]]], [3 x [5 x [[A]]]]* [[ARRAY]], i32 0, i32 0, i32 0 + } +} + // CHECK-LABEL: define internal void @"_ZZ1e1ES_bEN3$_5D2Ev" // CHECK-LABEL: define internal i32 @"_ZZ1fvEN3$_68__invokeEii" @@ -126,9 +176,9 @@ int *PR22071_fun() { // CHECK-NEXT: call i32 @"_ZZ1fvENK3$_6clEii" // CHECK-NEXT: ret i32 -// CHECK-LABEL: define internal void @"_ZZ1hvEN4$_108__invokeEv"(%struct.A* noalias sret %agg.result) {{.*}} { +// CHECK-LABEL: define internal void @"_ZZ1hvEN4$_118__invokeEv"(%struct.A* noalias sret %agg.result) {{.*}} { // CHECK-NOT: = -// CHECK: call void @"_ZZ1hvENK4$_10clEv"(%struct.A* sret %agg.result, +// CHECK: call void @"_ZZ1hvENK4$_11clEv"(%struct.A* sret %agg.result, // CHECK-NEXT: ret void struct A { ~A(); }; void h() {