mizvekov updated this revision to Diff 332184. mizvekov added a comment. Changed:
- Implemented co_return as per Aaron's suggestion. - Imported coroutine tests from D68845 <https://reviews.llvm.org/D68845>. - Test tags shortened: cxxYY_cxxZZ means every version from YY to ZZ, instead of cxxAA_BB_CC_DD. - Existing test on decltype(auto) updated to also run on c++2b. - Change one of the test cases to just instantiate the relevant template, as per Arthur. Still missing: Making this run on all tests: - A lot of tests are running in only one old standard. Changing everything here would generate too much noise. - Some simplifications: - Aggregate common code for adding the implicit cast. - PerformMoveOrCopyInitialization can be changed to PerformCopyInitialization in the cases where AllowNRVO is false. - Can easily skip adding the implicit cast if expr was already a xvalue. - getCopyElisionCandidate needs refactoring IMHO. It is doing too much in one function, and this patch makes it worse. There are a lot of calls to it sprinkled around in the same sequence. Some problems: - On the current code, we want two things out of it: "Can we perform copy elision" AND "Can we perform implicit move". We end up calling it twice for that, with two parameters, instead of once returning both answers. - With this patch, now we need to call it even before we have the function return type available, then decide this type based on it, and then call it again to figure out if we can do copy elision. I will try to propose some way to improve that. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D99005/new/ https://reviews.llvm.org/D99005 Files: clang/include/clang/Sema/Sema.h clang/lib/Sema/Sema.cpp clang/lib/Sema/SemaCoroutine.cpp clang/lib/Sema/SemaExprCXX.cpp clang/lib/Sema/SemaStmt.cpp clang/lib/Sema/SemaType.cpp clang/test/CXX/class/class.init/class.copy.elision/p3.cpp clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-14.cpp clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-1y.cpp clang/test/CXX/special/class.copy/p33-0x.cpp clang/test/SemaCXX/coroutine-rvo.cpp
Index: clang/test/SemaCXX/coroutine-rvo.cpp =================================================================== --- clang/test/SemaCXX/coroutine-rvo.cpp +++ clang/test/SemaCXX/coroutine-rvo.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -stdlib=libc++ -std=c++1z -fcoroutines-ts -fsyntax-only +// RUN: %clang_cc1 -verify -std=c++17 -fcoroutines-ts -fsyntax-only %s namespace std::experimental { template <class Promise = void> struct coroutine_handle { @@ -39,10 +39,14 @@ }; struct MoveOnly { - MoveOnly() {}; + MoveOnly() = default; MoveOnly(const MoveOnly&) = delete; - MoveOnly(MoveOnly&&) noexcept {}; - ~MoveOnly() {}; + MoveOnly(MoveOnly &&) = default; +}; + +struct NoCopyNoMove { + NoCopyNoMove() = default; + NoCopyNoMove(const NoCopyNoMove &) = delete; }; template <typename T> @@ -52,18 +56,93 @@ auto final_suspend() noexcept { return suspend_never{}; } auto get_return_object() { return task{}; } static void unhandled_exception() {} - void return_value(T&& value) {} + void return_value(T &&value) {} // expected-note 4{{passing argument}} }; }; -task<MoveOnly> f() { - MoveOnly value; +task<NoCopyNoMove> local2val() { + NoCopyNoMove value; + co_return value; +} + +task<NoCopyNoMove &> local2ref() { + NoCopyNoMove value; + co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}} +} + +// We need the move constructor for construction of the coroutine. +task<MoveOnly> param2val(MoveOnly value) { co_return value; } -int main() { - f(); - return 0; +task<NoCopyNoMove> lvalue2val(NoCopyNoMove &value) { + co_return value; // expected-error {{rvalue reference to type 'NoCopyNoMove' cannot bind to lvalue of type 'NoCopyNoMove'}} +} + +task<NoCopyNoMove> rvalue2val(NoCopyNoMove &&value) { + co_return value; +} + +task<NoCopyNoMove &> lvalue2ref(NoCopyNoMove &value) { + co_return value; +} + +task<NoCopyNoMove &> rvalue2ref(NoCopyNoMove &&value) { + co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}} +} + +struct To { + operator MoveOnly() &&; +}; +task<MoveOnly> conversion_operator() { + To t; + co_return t; +} + +struct Construct { + Construct(MoveOnly); +}; +task<Construct> converting_constructor() { + MoveOnly w; + co_return w; } -// expected-no-diagnostics +struct Derived : MoveOnly {}; +task<MoveOnly> derived2base() { + Derived result; + co_return result; +} + +struct RetThis { + task<RetThis> foo() && { + co_return *this; // expected-error {{rvalue reference to type 'RetThis' cannot bind to lvalue of type 'RetThis'}} + } +}; + +template <typename, typename> +struct is_same { static constexpr bool value = false; }; + +template <typename T> +struct is_same<T, T> { static constexpr bool value = true; }; + +template <typename T> +struct template_return_task { + struct promise_type { + auto initial_suspend() { return suspend_never{}; } + auto final_suspend() noexcept { return suspend_never{}; } + auto get_return_object() { return template_return_task{}; } + static void unhandled_exception(); + template <typename U> + void return_value(U &&value) { + static_assert(is_same<T, U>::value); + } + }; +}; + +template_return_task<MoveOnly> param2template(MoveOnly value) { + co_return value; // We should deduce U = MoveOnly. +} + +template_return_task<NoCopyNoMove &> lvalue2template(NoCopyNoMove &value) { + co_return value; // We should deduce U = NoCopyNoMove&. +} Index: clang/test/CXX/special/class.copy/p33-0x.cpp =================================================================== --- clang/test/CXX/special/class.copy/p33-0x.cpp +++ clang/test/CXX/special/class.copy/p33-0x.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -std=c++11 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -std=c++2b -fsyntax-only -verify=expected %s +// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -std=c++11 -fsyntax-only -verify=expected %s class X { X(const X&); @@ -22,12 +23,12 @@ throw x; throw x2; } - + namespace PR10142 { struct X { X(); X(X&&); - X(const X&) = delete; // expected-note 2{{'X' has been explicitly marked deleted here}} + X(const X &) = delete; // expected-note 2{{'X' has been explicitly marked deleted here}} }; void f(int i) { @@ -36,7 +37,7 @@ X x2; if (i) throw x2; // okay - throw x; // expected-error{{call to deleted constructor of 'PR10142::X'}} + throw x; // expected-error{{call to deleted constructor of 'PR10142::X'}} } catch (...) { } } @@ -48,7 +49,7 @@ T x2; if (i) throw x2; // okay - throw x; // expected-error{{call to deleted constructor of 'PR10142::X'}} + throw x; // expected-error{{call to deleted constructor of 'PR10142::X'}} } catch (...) { } } Index: clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-14.cpp =================================================================== --- clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-14.cpp +++ clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-14.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -verify -std=c++1y %s +// RUN: %clang_cc1 -verify -std=c++2b -verify=expected,cxx2b %s +// RUN: %clang_cc1 -verify -std=c++14 -verify=expected,cxx14 %s namespace std { template<typename T> struct initializer_list { @@ -28,7 +29,7 @@ auto x4a = (i); decltype(auto) x4d = (i); using Int = decltype(x4a); -using IntLRef = decltype(x4d); +using IntLRef = decltype(x4d); // cxx2b-note {{previous definition is here}} auto x5a = f(); decltype(auto) x5d = f(); @@ -79,7 +80,7 @@ auto f3a(int n) { return (n); } decltype(auto) f3d(int n) { return (n); } // expected-warning {{reference to stack memory}} using Int = decltype(f3a(0)); -using IntLRef = decltype(f3d(0)); +using IntLRef = decltype(f3d(0)); // cxx2b-error {{type alias redefinition with different types ('decltype(f3d(0))' (aka 'int &&') vs 'decltype(x4d)' (aka 'int &'))}} auto f4a(int n) { return f(); } decltype(auto) f4d(int n) { return f(); } @@ -89,7 +90,7 @@ auto f5aa(int n) { auto x = f(); return x; } auto f5ad(int n) { decltype(auto) x = f(); return x; } decltype(auto) f5da(int n) { auto x = f(); return x; } -decltype(auto) f5dd(int n) { decltype(auto) x = f(); return x; } // expected-error {{rvalue reference to type 'int' cannot bind to lvalue}} +decltype(auto) f5dd(int n) { decltype(auto) x = f(); return x; } // cxx14-error {{rvalue reference to type 'int' cannot bind to lvalue}} using Int = decltype(f5aa(0)); using Int = decltype(f5ad(0)); using Int = decltype(f5da(0)); Index: clang/test/CXX/class/class.init/class.copy.elision/p3.cpp =================================================================== --- clang/test/CXX/class/class.init/class.copy.elision/p3.cpp +++ clang/test/CXX/class/class.init/class.copy.elision/p3.cpp @@ -1,7 +1,8 @@ -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected,cxx20 %s -// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_14_17 %s -// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_14_17 %s -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_14_17 %s +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fcxx-exceptions -verify=expected,cxx20_2b,cxx2b %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_20,cxx20_2b %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17,cxx11_20 %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17,cxx11_20 %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17,cxx11_20 %s namespace test_delete_function { struct A1 { @@ -54,38 +55,38 @@ namespace test_implicitly_movable_rvalue_ref { struct A1 { A1(A1 &&); - A1(const A1 &) = delete; // cxx11_14_17-note {{'A1' has been explicitly marked deleted here}} + A1(const A1 &) = delete; // cxx11_17-note {{'A1' has been explicitly marked deleted here}} }; A1 test1(A1 &&a) { - return a; // cxx11_14_17-error {{call to deleted constructor of 'test_implicitly_movable_rvalue_ref::A1'}} + return a; // cxx11_17-error {{call to deleted constructor of 'test_implicitly_movable_rvalue_ref::A1'}} } struct A2 { A2(A2 &&); private: - A2(const A2 &); // cxx11_14_17-note {{declared private here}} + A2(const A2 &); // cxx11_17-note {{declared private here}} }; A2 test2(A2 &&a) { - return a; // cxx11_14_17-error {{calling a private constructor of class 'test_implicitly_movable_rvalue_ref::A2'}} + return a; // cxx11_17-error {{calling a private constructor of class 'test_implicitly_movable_rvalue_ref::A2'}} } struct B1 { B1(const B1 &); - B1(B1 &&) = delete; // cxx20-note {{'B1' has been explicitly marked deleted here}} + B1(B1 &&) = delete; // cxx20_2b-note {{'B1' has been explicitly marked deleted here}} }; B1 test3(B1 &&b) { - return b; // cxx20-error {{call to deleted constructor of 'test_implicitly_movable_rvalue_ref::B1'}} + return b; // cxx20_2b-error {{call to deleted constructor of 'test_implicitly_movable_rvalue_ref::B1'}} } struct B2 { B2(const B2 &); private: - B2(B2 &&); // cxx20-note {{declared private here}} + B2(B2 &&); // cxx20_2b-note {{declared private here}} }; B2 test4(B2 &&b) { - return b; // cxx20-error {{calling a private constructor of class 'test_implicitly_movable_rvalue_ref::B2'}} + return b; // cxx20_2b-error {{calling a private constructor of class 'test_implicitly_movable_rvalue_ref::B2'}} } } // namespace test_implicitly_movable_rvalue_ref @@ -96,13 +97,14 @@ struct A1 { A1(const A1 &); - A1(A1 &&) = delete; // cxx20-note {{'A1' has been explicitly marked deleted here}} + A1(A1 &&) = delete; // cxx20_2b-note {{'A1' has been explicitly marked deleted here}} + // expected-note@-1 {{'A1' has been explicitly marked deleted here}} }; void test1() { try { func(); } catch (A1 a) { - throw a; // cxx20-error {{call to deleted constructor of 'test_throw_parameter::A1'}} + throw a; // cxx20_2b-error {{call to deleted constructor of 'test_throw_parameter::A1'}} } } @@ -110,15 +112,21 @@ A2(const A2 &); private: - A2(A2 &&); // cxx20-note {{declared private here}} + A2(A2 &&); // cxx20_2b-note {{declared private here}} }; void test2() { try { func(); } catch (A2 a) { - throw a; // cxx20-error {{calling a private constructor of class 'test_throw_parameter::A2'}} + throw a; // cxx20_2b-error {{calling a private constructor of class 'test_throw_parameter::A2'}} } } + +void test3(A1 a) try { + func(); +} catch (...) { + throw a; // expected-error {{call to deleted constructor of 'test_throw_parameter::A1'}} +} } // namespace test_throw_parameter // In C++20, during the first overload resolution, the selected function no @@ -128,42 +136,42 @@ struct A1 { operator C() &&; - operator C() const & = delete; // cxx11_14_17-note {{'operator C' has been explicitly marked deleted here}} + operator C() const & = delete; // cxx11_17-note {{'operator C' has been explicitly marked deleted here}} }; C test1() { A1 a; - return a; // cxx11_14_17-error {{conversion function from 'test_non_ctor_conversion::A1' to 'test_non_ctor_conversion::C' invokes a deleted function}} + return a; // cxx11_17-error {{conversion function from 'test_non_ctor_conversion::A1' to 'test_non_ctor_conversion::C' invokes a deleted function}} } struct A2 { operator C() &&; private: - operator C() const &; // cxx11_14_17-note {{declared private here}} + operator C() const &; // cxx11_17-note {{declared private here}} }; C test2() { A2 a; - return a; // cxx11_14_17-error {{'operator C' is a private member of 'test_non_ctor_conversion::A2'}} + return a; // cxx11_17-error {{'operator C' is a private member of 'test_non_ctor_conversion::A2'}} } struct B1 { operator C() const &; - operator C() && = delete; // cxx20-note {{'operator C' has been explicitly marked deleted here}} + operator C() && = delete; // cxx20_2b-note {{'operator C' has been explicitly marked deleted here}} }; C test3() { B1 b; - return b; // cxx20-error {{conversion function from 'test_non_ctor_conversion::B1' to 'test_non_ctor_conversion::C' invokes a deleted function}} + return b; // cxx20_2b-error {{conversion function from 'test_non_ctor_conversion::B1' to 'test_non_ctor_conversion::C' invokes a deleted function}} } struct B2 { operator C() const &; private: - operator C() &&; // cxx20-note {{declared private here}} + operator C() &&; // cxx20_2b-note {{declared private here}} }; C test4() { B2 b; - return b; // cxx20-error {{'operator C' is a private member of 'test_non_ctor_conversion::B2'}} + return b; // cxx20_2b-error {{'operator C' is a private member of 'test_non_ctor_conversion::B2'}} } } // namespace test_non_ctor_conversion @@ -182,35 +190,35 @@ NeedRvalueRef(B2 &&); }; struct NeedValue { - NeedValue(A1); // cxx11_14_17-note 2 {{passing argument to parameter here}} + NeedValue(A1); // cxx11_17-note 2 {{passing argument to parameter here}} NeedValue(A2); - NeedValue(B1); // cxx20-note 2 {{passing argument to parameter here}} + NeedValue(B1); // cxx20_2b-note 2 {{passing argument to parameter here}} NeedValue(B2); }; struct A1 { A1(); A1(A1 &&); - A1(const A1 &) = delete; // cxx11_14_17-note 3 {{'A1' has been explicitly marked deleted here}} + A1(const A1 &) = delete; // cxx11_17-note 3 {{'A1' has been explicitly marked deleted here}} }; NeedValue test_1_1() { // not rvalue reference // same type A1 a; - return a; // cxx11_14_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}} + return a; // cxx11_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}} } class DerivedA1 : public A1 {}; A1 test_1_2() { // rvalue reference // not same type DerivedA1 a; - return a; // cxx11_14_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}} + return a; // cxx11_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}} } NeedValue test_1_3() { // not rvalue reference // not same type DerivedA1 a; - return a; // cxx11_14_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}} + return a; // cxx11_17-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::A1'}} } struct A2 { @@ -218,51 +226,51 @@ A2(A2 &&); private: - A2(const A2 &); // cxx11_14_17-note 3 {{declared private here}} + A2(const A2 &); // cxx11_17-note 3 {{declared private here}} }; NeedValue test_2_1() { // not rvalue reference // same type A2 a; - return a; // cxx11_14_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}} + return a; // cxx11_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}} } class DerivedA2 : public A2 {}; A2 test_2_2() { // rvalue reference // not same type DerivedA2 a; - return a; // cxx11_14_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}} + return a; // cxx11_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}} } NeedValue test_2_3() { // not rvalue reference // not same type DerivedA2 a; - return a; // cxx11_14_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}} + return a; // cxx11_17-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::A2'}} } struct B1 { B1(); B1(const B1 &); - B1(B1 &&) = delete; // cxx20-note 3 {{'B1' has been explicitly marked deleted here}} + B1(B1 &&) = delete; // cxx20_2b-note 3 {{'B1' has been explicitly marked deleted here}} }; NeedValue test_3_1() { // not rvalue reference // same type B1 b; - return b; // cxx20-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}} + return b; // cxx20_2b-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}} } class DerivedB1 : public B1 {}; B1 test_3_2() { // rvalue reference // not same type DerivedB1 b; - return b; // cxx20-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}} + return b; // cxx20_2b-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}} } NeedValue test_3_3() { // not rvalue reference // not same type DerivedB1 b; - return b; // cxx20-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}} + return b; // cxx20_2b-error {{call to deleted constructor of 'test_ctor_param_rvalue_ref::B1'}} } struct B2 { @@ -270,25 +278,67 @@ B2(const B2 &); private: - B2(B2 &&); // cxx20-note 3 {{declared private here}} + B2(B2 &&); // cxx20_2b-note 3 {{declared private here}} }; NeedValue test_4_1() { // not rvalue reference // same type B2 b; - return b; // cxx20-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}} + return b; // cxx20_2b-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}} } class DerivedB2 : public B2 {}; B2 test_4_2() { // rvalue reference // not same type DerivedB2 b; - return b; // cxx20-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}} + return b; // cxx20_2b-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}} } NeedValue test_4_3() { // not rvalue reference // not same type DerivedB2 b; - return b; // cxx20-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}} + return b; // cxx20_2b-error {{calling a private constructor of class 'test_ctor_param_rvalue_ref::B2'}} } } // namespace test_ctor_param_rvalue_ref +namespace test_simpler_implicit_move { +struct CopyOnly { + CopyOnly(); // cxx2b-note {{candidate constructor not viable: requires 0 arguments, but 1 was provided}} + // cxx2b-note@-1 {{candidate constructor not viable: requires 0 arguments, but 1 was provided}} + CopyOnly(CopyOnly &); // cxx2b-note {{candidate constructor not viable: expects an lvalue for 1st argument}} + // cxx2b-note@-1 {{candidate constructor not viable: expects an lvalue for 1st argument}} +}; +struct MoveOnly { + MoveOnly(); + MoveOnly(MoveOnly &&); +}; +MoveOnly &&rref(); + +MoveOnly &&test1(MoveOnly &&w) { + return w; // cxx11_20-error {{cannot bind to lvalue of type}} +} + +CopyOnly test2(bool b) { + static CopyOnly w1; + CopyOnly w2; + if (b) { + return w1; + } else { + return w2; // cxx2b-error {{no matching constructor for initialization}} + } +} + +template <class T> T &&test3(T &&x) { return x; } // cxx11_20-error {{cannot bind to lvalue of type}} +template MoveOnly& test3<MoveOnly&>(MoveOnly&); +template MoveOnly&& test3<MoveOnly>(MoveOnly&&); // cxx11_20-note {{in instantiation of function template specialization}} + +MoveOnly &&test4() { + MoveOnly &&x = rref(); + return x; // cxx11_20-error {{cannot bind to lvalue of type}} +} + +void test5() try { + CopyOnly x; + throw x; // cxx2b-error {{no matching constructor for initialization}} +} catch (...) { +} +} // namespace test_simpler_implicit_move Index: clang/lib/Sema/SemaType.cpp =================================================================== --- clang/lib/Sema/SemaType.cpp +++ clang/lib/Sema/SemaType.cpp @@ -8839,6 +8839,10 @@ if (E->isTypeDependent()) return S.Context.DependentTy; + Expr *IDExpr = E; + if (auto *ImplCastExpr = dyn_cast<ImplicitCastExpr>(E)) + IDExpr = ImplCastExpr->getSubExpr(); + // C++11 [dcl.type.simple]p4: // The type denoted by decltype(e) is defined as follows: @@ -8849,7 +8853,7 @@ // Note that this does not pick up the implicit 'const' for a template // parameter object. This rule makes no difference before C++20 so we apply // it unconditionally. - if (const auto *SNTTPE = dyn_cast<SubstNonTypeTemplateParmExpr>(E)) + if (const auto *SNTTPE = dyn_cast<SubstNonTypeTemplateParmExpr>(IDExpr)) return SNTTPE->getParameterType(S.Context); // - if e is an unparenthesized id-expression or an unparenthesized class @@ -8858,21 +8862,22 @@ // functions, the program is ill-formed; // // We apply the same rules for Objective-C ivar and property references. - if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(IDExpr)) { const ValueDecl *VD = DRE->getDecl(); if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(VD)) return TPO->getType().getUnqualifiedType(); return VD->getType(); - } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) { + } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(IDExpr)) { if (const ValueDecl *VD = ME->getMemberDecl()) if (isa<FieldDecl>(VD) || isa<VarDecl>(VD)) return VD->getType(); - } else if (const ObjCIvarRefExpr *IR = dyn_cast<ObjCIvarRefExpr>(E)) { + } else if (const ObjCIvarRefExpr *IR = dyn_cast<ObjCIvarRefExpr>(IDExpr)) { return IR->getDecl()->getType(); - } else if (const ObjCPropertyRefExpr *PR = dyn_cast<ObjCPropertyRefExpr>(E)) { + } else if (const ObjCPropertyRefExpr *PR = + dyn_cast<ObjCPropertyRefExpr>(IDExpr)) { if (PR->isExplicitProperty()) return PR->getExplicitProperty()->getType(); - } else if (auto *PE = dyn_cast<PredefinedExpr>(E)) { + } else if (auto *PE = dyn_cast<PredefinedExpr>(IDExpr)) { return PE->getType(); } @@ -8885,8 +8890,8 @@ // entity. using namespace sema; if (S.getCurLambda()) { - if (isa<ParenExpr>(E)) { - if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens())) { + if (isa<ParenExpr>(IDExpr)) { + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(IDExpr->IgnoreParens())) { if (VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl())) { QualType T = S.getCapturedDeclRefType(Var, DRE->getLocation()); if (!T.isNull()) Index: clang/lib/Sema/SemaStmt.cpp =================================================================== --- clang/lib/Sema/SemaStmt.cpp +++ clang/lib/Sema/SemaStmt.cpp @@ -3041,6 +3041,8 @@ /// NRVO, or NULL if there is no such candidate. VarDecl *Sema::getCopyElisionCandidate(QualType ReturnType, Expr *E, CopyElisionSemanticsKind CESK) { + if (auto *ImplCastExpr = dyn_cast<ImplicitCastExpr>(E)) + E = ImplCastExpr->getSubExpr(); // - in a return statement in a function [where] ... // ... the expression is the name of a non-volatile automatic object ... DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E->IgnoreParens()); @@ -3086,15 +3088,14 @@ if (VD->hasAttr<BlocksAttr>()) return false; - // ...non-volatile... - if (VD->getType().isVolatileQualified()) - return false; - - // C++20 [class.copy.elision]p3: - // ...rvalue reference to a non-volatile... - if (VD->getType()->isRValueReferenceType() && - (!(CESK & CES_AllowRValueReferenceType) || - VD->getType().getNonReferenceType().isVolatileQualified())) + QualType VDNonRefType = VDType; + if (VDType->isReferenceType()) { + if (!(CESK & CES_AllowRValueReferenceType) || + !VDType->isRValueReferenceType()) + return false; + VDNonRefType = VDType.getNonReferenceType(); + } + if (!VDNonRefType->isObjectType() || VDNonRefType.isVolatileQualified()) return false; if (CESK & CES_AllowDifferentTypes) @@ -3102,8 +3103,8 @@ // Variables with higher required alignment than their type's ABI // alignment cannot use NRVO. - if (!VD->getType()->isDependentType() && VD->hasAttr<AlignedAttr>() && - Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VD->getType())) + if (!VDNonRefType->isDependentType() && VD->hasAttr<AlignedAttr>() && + Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDNonRefType)) return false; return true; @@ -3222,14 +3223,13 @@ bool NeedSecondOverloadResolution = true; if (AllowNRVO) { - CopyElisionSemanticsKind CESK = CES_Strict; - if (getLangOpts().CPlusPlus20) { - CESK = CES_ImplicitlyMovableCXX20; - } else if (getLangOpts().CPlusPlus11) { - CESK = CES_ImplicitlyMovableCXX11CXX14CXX17; - } - if (!NRVOCandidate) { + CopyElisionSemanticsKind CESK = CES_Strict; + if (getLangOpts().CPlusPlus20) { + CESK = CES_ImplicitlyMovableCXX20; + } else if (getLangOpts().CPlusPlus11) { + CESK = CES_ImplicitlyMovableCXX11CXX14CXX17; + } NRVOCandidate = getCopyElisionCandidate(ResultType, Value, CESK); } @@ -3432,8 +3432,9 @@ InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc, FnRetType, NRVOCandidate != nullptr); - ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVOCandidate, - FnRetType, RetValExp); + ExprResult Res = PerformMoveOrCopyInitialization( + Entity, NRVOCandidate, FnRetType, RetValExp, + /*AllowNRVO=*/!getLangOpts().CPlusPlus2b); if (Res.isInvalid()) { // FIXME: Cleanup temporaries here, anyway? return StmtError(); @@ -3650,6 +3651,15 @@ if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp)) return StmtError(); + if (getLangOpts().CPlusPlus2b && RetValExp) { + if (VarDecl *VD = getCopyElisionCandidate(QualType{}, RetValExp, + CES_ImplicitlyMovableCXX20)) { + RetValExp = ImplicitCastExpr::Create( + Context, VD->getType().getNonReferenceType(), CK_NoOp, RetValExp, + nullptr, VK_XValue, FPOptionsOverride()); + } + } + if (isa<CapturingScopeInfo>(getCurFunction())) return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp); @@ -3846,8 +3856,9 @@ InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc, RetType, NRVOCandidate != nullptr); - ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVOCandidate, - RetType, RetValExp); + ExprResult Res = PerformMoveOrCopyInitialization( + Entity, NRVOCandidate, RetType, RetValExp, + /*AllowNRVO=*/!getLangOpts().CPlusPlus2b); if (Res.isInvalid()) { // FIXME: Clean up temporaries here anyway? return StmtError(); Index: clang/lib/Sema/SemaExprCXX.cpp =================================================================== --- clang/lib/Sema/SemaExprCXX.cpp +++ clang/lib/Sema/SemaExprCXX.cpp @@ -851,6 +851,15 @@ Diag(OpLoc, diag::err_omp_simd_region_cannot_use_stmt) << "throw"; if (Ex && !Ex->isTypeDependent()) { + if (IsThrownVarInScope && getLangOpts().CPlusPlus2b) { + if (VarDecl *VD = getCopyElisionCandidate(QualType{}, Ex, + CES_ImplicitlyMovableCXX20)) { + Ex = ImplicitCastExpr::Create( + Context, VD->getType().getNonReferenceType(), CK_NoOp, Ex, nullptr, + VK_XValue, FPOptionsOverride()); + } + } + QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType()); if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex)) return ExprError(); @@ -878,7 +887,8 @@ OpLoc, ExceptionObjectTy, /*NRVO=*/NRVOVariable != nullptr); ExprResult Res = PerformMoveOrCopyInitialization( - Entity, NRVOVariable, QualType(), Ex, IsThrownVarInScope); + Entity, NRVOVariable, QualType(), Ex, + /*AllowNRVO=*/!getLangOpts().CPlusPlus2b && IsThrownVarInScope); if (Res.isInvalid()) return ExprError(); Ex = Res.get(); Index: clang/lib/Sema/SemaCoroutine.cpp =================================================================== --- clang/lib/Sema/SemaCoroutine.cpp +++ clang/lib/Sema/SemaCoroutine.cpp @@ -994,26 +994,15 @@ E = R.get(); } - // Move the return value if we can - if (E) { - const VarDecl *NRVOCandidate = this->getCopyElisionCandidate( - E->getType(), E, CES_ImplicitlyMovableCXX20); - if (NRVOCandidate) { - InitializedEntity Entity = - InitializedEntity::InitializeResult(Loc, E->getType(), NRVOCandidate); - ExprResult MoveResult = this->PerformMoveOrCopyInitialization( - Entity, NRVOCandidate, E->getType(), E); - if (MoveResult.get()) - E = MoveResult.get(); - } - } - - // FIXME: If the operand is a reference to a variable that's about to go out - // of scope, we should treat the operand as an xvalue for this overload - // resolution. VarDecl *Promise = FSI->CoroutinePromise; ExprResult PC; if (E && (isa<InitListExpr>(E) || !E->getType()->isVoidType())) { + if (const VarDecl *NRVOCandidate = getCopyElisionCandidate( + E->getType(), E, CES_ImplicitlyMovableCXX20)) { + E = ImplicitCastExpr::Create( + Context, NRVOCandidate->getType().getNonReferenceType(), CK_NoOp, E, + nullptr, VK_XValue, FPOptionsOverride()); + } PC = buildPromiseCall(*this, Promise, Loc, "return_value", E); } else { E = MakeFullDiscardedValueExpr(E).get(); @@ -1570,7 +1559,8 @@ // Trigger a nice error message. InitializedEntity Entity = InitializedEntity::InitializeResult(Loc, FnRetType, false); - S.PerformMoveOrCopyInitialization(Entity, nullptr, FnRetType, ReturnValue); + S.PerformMoveOrCopyInitialization(Entity, nullptr, FnRetType, ReturnValue, + /*AllowNRVO=*/false); noteMemberDeclaredHere(S, ReturnValue, Fn); return false; } @@ -1587,7 +1577,8 @@ InitializedEntity Entity = InitializedEntity::InitializeVariable(GroDecl); ExprResult Res = S.PerformMoveOrCopyInitialization(Entity, nullptr, GroType, - this->ReturnValue); + this->ReturnValue, + /*AllowNRVO=*/false); if (Res.isInvalid()) return false; Index: clang/lib/Sema/Sema.cpp =================================================================== --- clang/lib/Sema/Sema.cpp +++ clang/lib/Sema/Sema.cpp @@ -1954,7 +1954,7 @@ new (S.Context) DeclRefExpr(S.Context, VD, false, T, VK_LValue, Loc); ExprResult Result = S.PerformMoveOrCopyInitialization( InitializedEntity::InitializeBlock(Loc, T, false), VD, VD->getType(), - VarRef, /*AllowNRVO=*/true); + VarRef, /*AllowNRVO=*/!S.getLangOpts().CPlusPlus2b); if (!Result.isInvalid()) { Result = S.MaybeCreateExprWithCleanups(Result); Expr *Init = Result.getAs<Expr>(); Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -3428,9 +3428,8 @@ ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity, const VarDecl *NRVOCandidate, - QualType ResultType, - Expr *Value, - bool AllowNRVO = true); + QualType ResultType, Expr *Value, + bool AllowNRVO); bool CanPerformAggregateInitializationForOverloadResolution( const InitializedEntity &Entity, InitListExpr *From);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits