diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 985cf264876e9..262963f9919d1 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -308,6 +308,8 @@ New Compiler Flags - New option ``-ftime-report-json`` added which outputs the same timing data as ``-ftime-report`` but formatted as JSON. +- New option ``-Wnrvo`` added and disabled by default to warn about missed NRVO opportunites. + Deprecated Compiler Flags ------------------------- diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f0bd5a1174020..d78a757c72e4a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12445,6 +12445,10 @@ def warn_zero_as_null_pointer_constant : Warning< "zero as null pointer constant">, InGroup>, DefaultIgnore; +def warn_not_eliding_copy_on_return : Warning< + "not eliding copy on return">, + InGroup>, DefaultIgnore; + def err_nullability_cs_multilevel : Error< "nullability keyword %0 cannot be applied to multi-level pointer type %1">; def note_nullability_type_specifier : Note< diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a7d59ec232b64..6dae243b520f0 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16093,8 +16093,11 @@ void Sema::computeNRVO(Stmt *Body, FunctionScopeInfo *Scope) { for (unsigned I = 0, E = Scope->Returns.size(); I != E; ++I) { if (const VarDecl *NRVOCandidate = Returns[I]->getNRVOCandidate()) { - if (!NRVOCandidate->isNRVOVariable()) + if (!NRVOCandidate->isNRVOVariable()) { + Diag(Returns[I]->getRetValue()->getExprLoc(), + diag::warn_not_eliding_copy_on_return); Returns[I]->setNRVOCandidate(nullptr); + } } } } diff --git a/clang/test/SemaCXX/warn-nrvo.cpp b/clang/test/SemaCXX/warn-nrvo.cpp new file mode 100644 index 0000000000000..55bbdbd3e6e40 --- /dev/null +++ b/clang/test/SemaCXX/warn-nrvo.cpp @@ -0,0 +1,73 @@ +// RUN: %clang_cc1 -fsyntax-only -Wnrvo -Wno-return-mismatch -verify %s +struct MyClass { + int value; + int c; + MyClass(int v) : value(v), c(0) {} + MyClass(const MyClass& other) : value(other.value) { c++; } +}; + +MyClass create_object(bool condition) { + MyClass obj1(1); + MyClass obj2(2); + if (condition) { + return obj1; // expected-warning{{not eliding copy on return}} + } + return obj2; // expected-warning{{not eliding copy on return}} +} + +MyClass create_object2(){ + MyClass obj(1); + return obj; // no warning +} + +template +T create_object3(){ + T obj(1); + return obj; // no warning +} + +// Known issue: if a function template uses a +// deduced return type (i.e. auto or decltype(auto)), +// then NRVO is not applied for any instantiation of +// that function template +// (see https://github.com/llvm/llvm-project/issues/95280). +template +auto create_object4(){ + T obj(1); + return obj; // expected-warning{{not eliding copy on return}} +} + +template +MyClass create_object5(){ + MyClass obj1(1); + if constexpr (F){ + MyClass obj2(2); + return obj2; // no warning + } + // Missed NRVO optimization by clang + return obj1; // expected-warning{{not eliding copy on return}} +} + +constexpr bool Flag = false; + +MyClass create_object6(){ + MyClass obj1(1); + if constexpr (Flag){ + MyClass obj2(2); + return obj2; // expected-warning{{not eliding copy on return}} + } + return obj1; // no warning +} + +void create_object7(){ + if constexpr (Flag){ + MyClass obj1(1); + return obj1; // no warning + } +} + +void init_templates(){ + create_object3(); // no warning + create_object4(); // expected-note {{in instantiation of function template specialization 'create_object4' requested here}} + create_object5(); // expected-note {{in instantiation of function template specialization 'create_object5' requested here}} +}