diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -11402,7 +11402,8 @@ if (LHS.isInvalid()) return ExprError(); - ExprResult RHS = getDerived().TransformExpr(E->getRHS()); + ExprResult RHS = + getDerived().TransformInitializer(E->getRHS(), /*NotCopyInit=*/false); if (RHS.isInvalid()) return ExprError(); @@ -11950,7 +11951,8 @@ ExprResult Second; if (E->getNumArgs() == 2) { - Second = getDerived().TransformExpr(E->getArg(1)); + Second = + getDerived().TransformInitializer(E->getArg(1), /*NotCopyInit=*/false); if (Second.isInvalid()) return ExprError(); } diff --git a/clang/test/Analysis/missing-bind-temporary.cpp b/clang/test/Analysis/missing-bind-temporary.cpp --- a/clang/test/Analysis/missing-bind-temporary.cpp +++ b/clang/test/Analysis/missing-bind-temporary.cpp @@ -7,8 +7,6 @@ int global; namespace variant_0 { -// This variant of the code works correctly. Function foo() is not a template -// function. Note that there are two destructors within foo(). class A { public: @@ -46,9 +44,6 @@ } // end namespace variant_0 namespace variant_1 { -// Suddenly, if we turn foo() into a template, we are missing a -// CXXBindTemporaryExpr in the AST, and therefore we're missing a -// temporary destructor in the CFG. class A { public: @@ -59,8 +54,6 @@ A a; }; -// FIXME: Find the construction context for {} and enforce the temporary -// destructor. // CHECK: template<> void foo(int) // CHECK: [B1] // CHECK-NEXT: 1: (CXXConstructExpr, [B1.2], B) @@ -68,10 +61,12 @@ // CHECK-NEXT: 3: operator= // CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, FunctionToPointerDecay, B &(*)(B &&) noexcept) // CHECK-NEXT: 5: i -// CHECK-NEXT: 6: {} (CXXConstructExpr, B) -// CHECK-NEXT: 7: [B1.6] -// CHECK-NEXT: 8: [B1.5] = [B1.7] (OperatorCall) -// CHECK-NEXT: 9: [B1.2].~B() (Implicit destructor) +// CHECK-NEXT: 6: {} (CXXConstructExpr, [B1.7], [B1.8], B) +// CHECK-NEXT: 7: [B1.6] (BindTemporary) +// CHECK-NEXT: 8: [B1.7] +// CHECK-NEXT: 9: [B1.5] = [B1.8] (OperatorCall) +// CHECK-NEXT: 10: ~B() (Temporary object destructor) +// CHECK-NEXT: 11: [B1.2].~B() (Implicit destructor) template void foo(T) { B i; i = {}; @@ -80,8 +75,7 @@ void bar() { global = 0; foo(1); - // FIXME: Should be TRUE, i.e. we should call (and inline) two destructors. - clang_analyzer_eval(global == 2); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(global == 2); // expected-warning{{TRUE [debug.ExprInspection]}} } } // end namespace variant_1 diff --git a/clang/test/CodeGenCXX/gh62818.cpp b/clang/test/CodeGenCXX/gh62818.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/gh62818.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -std=c++17 -emit-llvm -triple x86_64-linux-gnu -o - %s | FileCheck %s + +void doSomething(); + +struct A { + A() {}; + ~A() noexcept { + doSomething(); + } + + A & operator=(A a) & noexcept { + return *this; + } +}; + +template +struct B { + void test() {a = {};} + // CHECK: define linkonce_odr void @_ZN1BIiE4testEv + // CHECK: call void @_ZN1AC1Ev(ptr noundef nonnull align 1 dereferenceable(1) + // CHECK: [[CALL:%.*]] = call noundef nonnull align 1 dereferenceable(1) ptr @_ZNR1AaSES_ + // CHECK: call void @_ZN1AD2Ev(ptr noundef nonnull align 1 dereferenceable(1) + + A a; +}; + +void client(B &f) {f.test();}