[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-05-08 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added a comment.

> Can you post the diagnostic exactly as it appears in the compiler output? I 
> am surprised that it would appear here. It should appear here only if the 
> standard library's implicit conversion from std::map<...>::iterator to 
> std::map<...>::const_iterator were implemented as a conversion operator 
> instead of as a converting constructor. I have verified that both libstdc++ 
> trunk and libc++ trunk implement it "correctly" as a converting constructor, 
> and I have verified on Wandbox/Godbolt that no warning is emitted on your 
> sample code when using either of those standard libraries.
> 
> Is it possible that you are using a standard library that is neither libc++ 
> or libstdc++?
> 
> Is it possible that that standard library implements the 
> iterator-to-const_iterator conversion as a conversion operator instead of a 
> converting constructor?

@thakis ping — did you ever resolve this issue to your satisfaction?


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-04-30 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added a comment.

In https://reviews.llvm.org/D43322#1083075, @arthur.j.odwyer wrote:

> Sorry, I responded via email but I guess Phabricator didn't pick it up for
>  some reason.
>  See below.


And then Phab *still* didn't pick up the important part... Okay, I'll try 
pasting it here.




Can you post the diagnostic exactly as it appears in the compiler output? I am 
surprised that it would appear here. It should appear here only if the standard 
library's implicit conversion from std::map<...>::iterator to 
std::map<...>::const_iterator were implemented as a conversion operator instead 
of as a converting constructor. I have verified that both libstdc++ trunk and 
libc++ trunk implement it "correctly" as a converting constructor, and I have 
verified on Wandbox/Godbolt that no warning is emitted on your sample code when 
using either of those standard libraries.

Is it possible that you are using a standard library that is neither libc++ or 
libstdc++?
Is it possible that that standard library implements the 
iterator-to-const_iterator conversion as a conversion operator instead of a 
converting constructor?

> And then someone else pointed out that 
> http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1579 might mean 
> that the code-as is should be fine too in C++14 – I don't know if that's 
> true, but maybe you could comment on that too :-)

CWG1579 is the defect report against ISO C++11 which is mentioned in the 
wording of the off-by-default -Wreturn-std-move-in-c++11. :)  Anything that 
-Wreturn-std-move itself complains about, is stuff that was NOT fixed by 
CWG1579.
Alternative hypothesis: Is it possible that you have manually turned on 
-Wreturn-std-move-in-c++11 (which is deliberately omitted from -Wmove/-Wall)? 
In that case, the warning is absolutely correct, but you are probably wrong to 
care about fixing it in this case. :)

> Maybe the warning could suggest something like "cv qualifiers don't match, 
> make them match" on a note in addition or instead of std::move() for this 
> case?

I would love to see this, but I'm not comfortable trying to add it myself. 
Remember, the problem here isn't core-language cv-qualifiers ("iterator" vs. 
"const iterator"); the problem is library-level type mismatch ("iterator" vs. 
"const_iterator"). You'd have to somehow look at the human-readable name of the 
typedef and determine that it was a match for the regex /const_(.*)/ and the 
target type was a match for /\1/, and then you'd have to generate a meaningful 
note.


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-04-30 Thread Arthur O'Dwyer via Phabricator via cfe-commits
arthur.j.odwyer added a comment.

Sorry, I responded via email but I guess Phabricator didn't pick it up for
some reason.
See below.


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-04-25 Thread Nico Weber via Phabricator via cfe-commits
thakis added a comment.

This is a cool warning, thanks for adding it. We ran into one thing while 
enabling this in Chromium that I'd like to mention here. We have code that 
basically does:

  struct Foo {
using passwords_iterator = std::map::const_iterator;
std::map 
passwords_;
passwords_iterator get(const base::string16& in) {
  auto it = passwords_.lower_bound(in);
  return it;
}
  };

Here, the warning gets emitted because `auto it` becomes a non-const iterator, 
and passwords_iterator is a const_iterator. Maybe the warning could suggest 
something like "cv qualifiers don't match, make them match" on a note in 
addition or instead of std::move() for this case?

And then someone else pointed out that 
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1579 might mean 
that the code-as is should be fine too in C++14 – I don't know if that's true, 
but maybe you could comment on that too :-)

(references:
https://cs.chromium.org/chromium/src/components/password_manager/core/browser/password_reuse_detector.h?type=cs=passwords_iterator=package:chromium=64
https://chromium-review.googlesource.com/c/chromium/src/+/1025435/6/components/password_manager/core/browser/password_reuse_detector.cc
https://chromium-review.googlesource.com/c/chromium/src/+/1025435/8/components/password_manager/core/browser/password_reuse_detector.cc
)


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-04-12 Thread Malcolm Parsons via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rC329914: Diagnose cases of return x that should 
be return std::move(x) for efficiency (authored by malcolm.parsons, 
committed by ).

Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaStmt.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: lib/Sema/SemaStmt.cpp
===
--- lib/Sema/SemaStmt.cpp
+++ lib/Sema/SemaStmt.cpp
@@ -2905,7 +2905,8 @@
   if (VD->getKind() != Decl::Var &&
   !((CESK & CES_AllowParameters) && VD->getKind() == Decl::ParmVar))
 return false;
-  if (VD->isExceptionVariable()) return false;
+  if (!(CESK & CES_AllowExceptionVariables) && VD->isExceptionVariable())
+return false;
 
   // ...automatic...
   if (!VD->hasLocalStorage()) return false;
@@ -2937,6 +2938,11 @@
 /// returned lvalues as rvalues in certain cases (to prefer move construction),
 /// then falls back to treating them as lvalues if that failed.
 ///
+/// \param ConvertingConstructorsOnly If true, follow [class.copy]p32 and reject
+/// resolutions that find non-constructors, such as derived-to-base conversions
+/// or `operator T()&&` member functions. If false, do consider such
+/// conversion sequences.
+///
 /// \param Res We will fill this in if move-initialization was possible.
 /// If move-initialization is not possible, such that we must fall back to
 /// treating the operand as an lvalue, we will leave Res in its original
@@ -2946,6 +2952,7 @@
   const VarDecl *NRVOCandidate,
   QualType ResultType,
   Expr *,
+  bool ConvertingConstructorsOnly,
   ExprResult ) {
   ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(),
 CK_NoOp, Value, VK_XValue);
@@ -2966,22 +2973,39 @@
   continue;
 
 FunctionDecl *FD = Step.Function.Function;
-if (isa(FD)) {
-  // C++14 [class.copy]p32:
-  // [...] If the first overload resolution fails or was not performed,
-  // or if the type of the first parameter of the selected constructor
-  // is not an rvalue reference to the object's type (possibly
-  // cv-qualified), overload resolution is performed again, considering
-  // the object as an lvalue.
-  const RValueReferenceType *RRefType =
-  FD->getParamDecl(0)->getType()->getAs();
-  if (!RRefType)
-break;
-  if (!S.Context.hasSameUnqualifiedType(RRefType->getPointeeType(),
-NRVOCandidate->getType()))
-break;
+if (ConvertingConstructorsOnly) {
+  if (isa(FD)) {
+// C++14 [class.copy]p32:
+// [...] If the first overload resolution fails or was not performed,
+// or if the type of the first parameter of the selected constructor
+// is not an rvalue reference to the object's type (possibly
+// cv-qualified), overload resolution is performed again, considering
+// the object as an lvalue.
+const RValueReferenceType *RRefType =
+FD->getParamDecl(0)->getType()->getAs();
+if (!RRefType)
+  break;
+if (!S.Context.hasSameUnqualifiedType(RRefType->getPointeeType(),
+  NRVOCandidate->getType()))
+  break;
+  } else {
+continue;
+  }
 } else {
-  continue;
+  if (isa(FD)) {
+// Check that overload resolution selected a constructor taking an
+// rvalue reference. If it selected an lvalue reference, then we
+// didn't need to cast this thing to an rvalue in the first place.
+if (!isa(FD->getParamDecl(0)->getType()))
+  break;
+  } else if (isa(FD)) {
+// Check that overload resolution selected a conversion operator
+// taking an rvalue reference.
+if (cast(FD)->getRefQualifier() != RQ_RValue)
+  break;
+  } else {
+continue;
+  }
 }
 
 // Promote "AsRvalue" to the heap, since we now need this
@@ -3019,13 +3043,82 @@
   ExprResult Res = ExprError();
 
   if (AllowNRVO) {
+bool AffectedByCWG1579 = false;
+
 if (!NRVOCandidate) {
   NRVOCandidate = getCopyElisionCandidate(ResultType, Value, CES_Default);
+  if (NRVOCandidate &&
+  !getDiagnostics().isIgnored(diag::warn_return_std_move_in_cxx11,
+  Value->getExprLoc())) {
+const VarDecl *NRVOCandidateInCXX11 =
+getCopyElisionCandidate(ResultType, Value, CES_FormerDefault);
+AffectedByCWG1579 = (!NRVOCandidateInCXX11);
+  }
 }
 
 if (NRVOCandidate) {
   TryMoveInitialization(*this, Entity, NRVOCandidate, 

[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-04-10 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added a comment.

Tuesday ping!  I just need someone to commit this for me.


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-04-05 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone updated this revision to Diff 141174.
Quuxplusone added a comment.

Finally learned how to "make check-clang" and actually run the test in the 
correct environment. Had to add `-fcxx-exceptions` to the command lines at the 
top of that file, because the code uses `throw`.

@rsmith PTAL?


Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaStmt.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: test/SemaCXX/warn-return-std-move.cpp
===
--- /dev/null
+++ test/SemaCXX/warn-return-std-move.cpp
@@ -0,0 +1,334 @@
+// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -Wreturn-std-move-in-c++11 -std=c++14 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -Wreturn-std-move-in-c++11 -std=c++14 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// definitions for std::move
+namespace std {
+inline namespace foo {
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+
+template  typename remove_reference::type &(T &);
+} // namespace foo
+} // namespace std
+
+struct Instrument {
+Instrument() {}
+Instrument(Instrument&&) { /* MOVE */ }
+Instrument(const Instrument&) { /* COPY */ }
+};
+struct ConvertFromBase { Instrument i; };
+struct ConvertFromDerived { Instrument i; };
+struct Base {
+Instrument i;
+operator ConvertFromBase() const& { return ConvertFromBase{i}; }
+operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
+};
+struct Derived : public Base {
+operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
+operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
+};
+struct ConstructFromBase {
+Instrument i;
+ConstructFromBase(const Base& b): i(b.i) {}
+ConstructFromBase(Base&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromDerived {
+Instrument i;
+ConstructFromDerived(const Derived& d): i(d.i) {}
+ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
+};
+
+struct TrivialInstrument {
+int i = 42;
+};
+struct ConvertFromTrivialBase { TrivialInstrument i; };
+struct ConvertFromTrivialDerived { TrivialInstrument i; };
+struct TrivialBase {
+TrivialInstrument i;
+operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
+operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
+};
+struct TrivialDerived : public TrivialBase {
+operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
+operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
+};
+struct ConstructFromTrivialBase {
+TrivialInstrument i;
+ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {}
+ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromTrivialDerived {
+TrivialInstrument i;
+ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {}
+ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {}
+};
+
+Derived test1() {
+Derived d1;
+return d1;  // ok
+}
+Base test2() {
+Derived d2;
+return d2;  // e1
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)"
+}
+ConstructFromDerived test3() {
+Derived d3;
+return d3;  // e2-cxx11
+// expected-warning@-1{{would have been copied despite being returned by name}}
+// expected-note@-2{{to avoid copying on older compilers}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)"
+}
+ConstructFromBase test4() {
+Derived d4;
+return d4;  // e3
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)"
+}
+ConvertFromDerived test5() {
+Derived d5;
+return d5;  // e4
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)"
+}
+ConvertFromBase test6() {
+Derived d6;
+return d6;  // e5
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)"
+}
+
+// These test cases should not produce the warning.
+Derived ok1() { Derived d; return d; }
+Base ok2() { Derived d; return static_cast(d); }
+ConstructFromDerived ok3() { Derived d; return 

[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-04-02 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added a comment.

@rsmith ping?


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-03-28 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone updated this revision to Diff 140103.
Quuxplusone added a comment.

Addressed review comments from @rsmith and rebased on master.
I still need someone else to land this for me, though.


Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaStmt.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: test/SemaCXX/warn-return-std-move.cpp
===
--- /dev/null
+++ test/SemaCXX/warn-return-std-move.cpp
@@ -0,0 +1,334 @@
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// definitions for std::move
+namespace std {
+inline namespace foo {
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+
+template  typename remove_reference::type &(T &);
+} // namespace foo
+} // namespace std
+
+struct Instrument {
+Instrument() {}
+Instrument(Instrument&&) { /* MOVE */ }
+Instrument(const Instrument&) { /* COPY */ }
+};
+struct ConvertFromBase { Instrument i; };
+struct ConvertFromDerived { Instrument i; };
+struct Base {
+Instrument i;
+operator ConvertFromBase() const& { return ConvertFromBase{i}; }
+operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
+};
+struct Derived : public Base {
+operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
+operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
+};
+struct ConstructFromBase {
+Instrument i;
+ConstructFromBase(const Base& b): i(b.i) {}
+ConstructFromBase(Base&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromDerived {
+Instrument i;
+ConstructFromDerived(const Derived& d): i(d.i) {}
+ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
+};
+
+struct TrivialInstrument {
+int i = 42;
+};
+struct ConvertFromTrivialBase { TrivialInstrument i; };
+struct ConvertFromTrivialDerived { TrivialInstrument i; };
+struct TrivialBase {
+TrivialInstrument i;
+operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
+operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
+};
+struct TrivialDerived : public TrivialBase {
+operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
+operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
+};
+struct ConstructFromTrivialBase {
+TrivialInstrument i;
+ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {}
+ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromTrivialDerived {
+TrivialInstrument i;
+ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {}
+ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {}
+};
+
+Derived test1() {
+Derived d1;
+return d1;  // ok
+}
+Base test2() {
+Derived d2;
+return d2;  // e1
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)"
+}
+ConstructFromDerived test3() {
+Derived d3;
+return d3;  // e2-cxx11
+// expected-warning@-1{{would have been copied despite being returned by name}}
+// expected-note@-2{{to avoid copying on older compilers}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)"
+}
+ConstructFromBase test4() {
+Derived d4;
+return d4;  // e3
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)"
+}
+ConvertFromDerived test5() {
+Derived d5;
+return d5;  // e4
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)"
+}
+ConvertFromBase test6() {
+Derived d6;
+return d6;  // e5
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)"
+}
+
+// These test cases should not produce the warning.
+Derived ok1() { Derived d; return d; }
+Base ok2() { Derived d; return static_cast(d); }
+ConstructFromDerived ok3() { Derived d; return static_cast(d); }
+ConstructFromBase ok4() { Derived d; return static_cast(d); }
+ConvertFromDerived ok5() { Derived d; 

[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-03-27 Thread Richard Smith - zygoloid via Phabricator via cfe-commits
rsmith accepted this revision.
rsmith added inline comments.
This revision is now accepted and ready to land.



Comment at: include/clang/Sema/Sema.h:3827
   bool isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
-  bool AllowParamOrMoveConstructible);
+  CopyElisionSemanticsKind CESK);
 

Quuxplusone wrote:
> Q: Is it appropriate for me to be changing the signature of this public 
> method at all? I don't know libclang's rules regarding this kind of change.
For reference, changes here are fine. We provide no API or ABI stability 
guarantees for our C++ ABI; libclang provides an ABI-stable C ABI layered on 
top of our internal API for this reason.



Comment at: include/clang/Sema/Sema.h:3806
 CES_Default = (CES_AllowParameters | CES_AllowDifferentTypes),
+CES_AsIfByStdMove = (CES_AllowParameters | CES_AllowDifferentTypes | 
CES_AllowExceptionVariables),
   };

Please wrap to 80 columns.



Comment at: lib/Sema/SemaStmt.cpp:3075-3076
+  } else {
+// The most common case for this is returning T from a function that
+// returns Expected. This is totally fine in a post-CWG1579 world,
+// but was not fine before.

I've seen this a lot more for returning `unique_ptr` from a function 
with return type `unique_ptr`. Maybe just give this `Expected` case as 
an example rather than saying it's the most common example?


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-03-27 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone updated this revision to Diff 139961.
Quuxplusone edited the summary of this revision.
Quuxplusone added a comment.

Rebased on master.  AFAIK this is ready to go and I just need someone to land 
it for me... @rtrieu ping?

I believe the PR summary would be suitable as a commit message.


Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaStmt.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: test/SemaCXX/warn-return-std-move.cpp
===
--- /dev/null
+++ test/SemaCXX/warn-return-std-move.cpp
@@ -0,0 +1,334 @@
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// definitions for std::move
+namespace std {
+inline namespace foo {
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+
+template  typename remove_reference::type &(T &);
+} // namespace foo
+} // namespace std
+
+struct Instrument {
+Instrument() {}
+Instrument(Instrument&&) { /* MOVE */ }
+Instrument(const Instrument&) { /* COPY */ }
+};
+struct ConvertFromBase { Instrument i; };
+struct ConvertFromDerived { Instrument i; };
+struct Base {
+Instrument i;
+operator ConvertFromBase() const& { return ConvertFromBase{i}; }
+operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
+};
+struct Derived : public Base {
+operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
+operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
+};
+struct ConstructFromBase {
+Instrument i;
+ConstructFromBase(const Base& b): i(b.i) {}
+ConstructFromBase(Base&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromDerived {
+Instrument i;
+ConstructFromDerived(const Derived& d): i(d.i) {}
+ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
+};
+
+struct TrivialInstrument {
+int i = 42;
+};
+struct ConvertFromTrivialBase { TrivialInstrument i; };
+struct ConvertFromTrivialDerived { TrivialInstrument i; };
+struct TrivialBase {
+TrivialInstrument i;
+operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
+operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
+};
+struct TrivialDerived : public TrivialBase {
+operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
+operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
+};
+struct ConstructFromTrivialBase {
+TrivialInstrument i;
+ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {}
+ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromTrivialDerived {
+TrivialInstrument i;
+ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {}
+ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {}
+};
+
+Derived test1() {
+Derived d1;
+return d1;  // ok
+}
+Base test2() {
+Derived d2;
+return d2;  // e1
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)"
+}
+ConstructFromDerived test3() {
+Derived d3;
+return d3;  // e2-cxx11
+// expected-warning@-1{{would have been copied despite being returned by name}}
+// expected-note@-2{{to avoid copying on older compilers}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)"
+}
+ConstructFromBase test4() {
+Derived d4;
+return d4;  // e3
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)"
+}
+ConvertFromDerived test5() {
+Derived d5;
+return d5;  // e4
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)"
+}
+ConvertFromBase test6() {
+Derived d6;
+return d6;  // e5
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)"
+}
+
+// These test cases should not produce the warning.
+Derived ok1() { Derived d; return d; }
+Base ok2() { Derived d; return static_cast(d); }
+ConstructFromDerived ok3() { Derived d; return static_cast(d); }

[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-03-22 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added a comment.

@rtrieu gentle ping!

Action items I need help with, cut-and-pasted from above:

- Ideally, test compiling a bunch of (e.g. Google) code with 
https://reviews.llvm.org/D43322, see if there are any rough edges
- Decide if `-Wmove` should imply `-Wreturn-std-move` (I have preemptively done 
this)
- Review and land https://reviews.llvm.org/D43322
- Ideally, some discussion of whether I should write a paper for San Diego 
proposing that C++ //should// move rather than copy in these cases (my default 
plan is to do this)


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-03-18 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added a comment.

@rtrieu ping! I've rebased this on the pure refactoring you committed for me 
last week.


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-03-15 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone updated this revision to Diff 138594.
Quuxplusone added a comment.

Rebase on master, now that the refactoring https://reviews.llvm.org/D43898 has 
been pushed (thanks @rtrieu!)


Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaStmt.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: test/SemaCXX/warn-return-std-move.cpp
===
--- /dev/null
+++ test/SemaCXX/warn-return-std-move.cpp
@@ -0,0 +1,334 @@
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// definitions for std::move
+namespace std {
+inline namespace foo {
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+
+template  typename remove_reference::type &(T &);
+} // namespace foo
+} // namespace std
+
+struct Instrument {
+Instrument() {}
+Instrument(Instrument&&) { /* MOVE */ }
+Instrument(const Instrument&) { /* COPY */ }
+};
+struct ConvertFromBase { Instrument i; };
+struct ConvertFromDerived { Instrument i; };
+struct Base {
+Instrument i;
+operator ConvertFromBase() const& { return ConvertFromBase{i}; }
+operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
+};
+struct Derived : public Base {
+operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
+operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
+};
+struct ConstructFromBase {
+Instrument i;
+ConstructFromBase(const Base& b): i(b.i) {}
+ConstructFromBase(Base&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromDerived {
+Instrument i;
+ConstructFromDerived(const Derived& d): i(d.i) {}
+ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
+};
+
+struct TrivialInstrument {
+int i = 42;
+};
+struct ConvertFromTrivialBase { TrivialInstrument i; };
+struct ConvertFromTrivialDerived { TrivialInstrument i; };
+struct TrivialBase {
+TrivialInstrument i;
+operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
+operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
+};
+struct TrivialDerived : public TrivialBase {
+operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
+operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
+};
+struct ConstructFromTrivialBase {
+TrivialInstrument i;
+ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {}
+ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromTrivialDerived {
+TrivialInstrument i;
+ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {}
+ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {}
+};
+
+Derived test1() {
+Derived d1;
+return d1;  // ok
+}
+Base test2() {
+Derived d2;
+return d2;  // e1
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)"
+}
+ConstructFromDerived test3() {
+Derived d3;
+return d3;  // e2-cxx11
+// expected-warning@-1{{would have been copied despite being returned by name}}
+// expected-note@-2{{to avoid copying on older compilers}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)"
+}
+ConstructFromBase test4() {
+Derived d4;
+return d4;  // e3
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)"
+}
+ConvertFromDerived test5() {
+Derived d5;
+return d5;  // e4
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)"
+}
+ConvertFromBase test6() {
+Derived d6;
+return d6;  // e5
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)"
+}
+
+// These test cases should not produce the warning.
+Derived ok1() { Derived d; return d; }
+Base ok2() { Derived d; return static_cast(d); }
+ConstructFromDerived ok3() { Derived d; return static_cast(d); }
+ConstructFromBase ok4() { Derived d; return static_cast(d); }
+ConvertFromDerived ok5() { Derived d; return 

[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-03-15 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added inline comments.



Comment at: include/clang/Basic/DiagnosticGroups.td:388
 def PessimizingMove : DiagGroup<"pessimizing-move">;
+def ReturnStdMoveInCXX11 : DiagGroup<"return-std-move-in-c++11">;
+def ReturnStdMove : DiagGroup<"return-std-move">;

>> The backward-compatibility-concerned diagnostic, -Wreturn-std-move-in-c++11, 
>> is *not* appropriate for -Wmove;
> Have you evaluated possibility of adding -Wreturn-std-move-in-c++11 to one of 
> CXX..Compat groups?

Hmm. I've considered it, but I don't know what the appropriate action is...
I suspect that it should *not* be "CXX11Compat" because technically the change 
was a DR against C++11 — sadly this is about portability to "older" compilers, 
not portability to "lesser" C++ standards.
Of course it can't be "CXX98Compat", for several reasons. :)
I'm not sure of the purpose of the "CXX..CompatPedantic" groups. They are all 
identical to the "CXX..Compat" groups, except that CXX98CompatPedantic includes 
one extra warning about binding to lifetime-extended temporaries which don't 
have copy constructors.


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-03-15 Thread Roman Lebedev via Phabricator via cfe-commits
lebedev.ri added a comment.

In https://reviews.llvm.org/D43322#1037889, @Quuxplusone wrote:

> The backward-compatibility-concerned diagnostic, 
> `-Wreturn-std-move-in-c++11`, is *not* appropriate for `-Wmove`;


Have you evaluated possibility of adding `-Wreturn-std-move-in-c++11` to one of 
`CXX..Compat` groups?


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-03-14 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone updated this revision to Diff 138457.
Quuxplusone added a comment.

Boldly add `-Wreturn-std-move` to `-Wmove` (and thus also to `-Wall`).
The backward-compatibility-concerned diagnostic, `-Wreturn-std-move-in-c++11`, 
is *not* appropriate for `-Wmove`; but I believe this main diagnostic is 
appropriate.


Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/SemaStmt.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: test/SemaCXX/warn-return-std-move.cpp
===
--- /dev/null
+++ test/SemaCXX/warn-return-std-move.cpp
@@ -0,0 +1,334 @@
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// definitions for std::move
+namespace std {
+inline namespace foo {
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+
+template  typename remove_reference::type &(T &);
+} // namespace foo
+} // namespace std
+
+struct Instrument {
+Instrument() {}
+Instrument(Instrument&&) { /* MOVE */ }
+Instrument(const Instrument&) { /* COPY */ }
+};
+struct ConvertFromBase { Instrument i; };
+struct ConvertFromDerived { Instrument i; };
+struct Base {
+Instrument i;
+operator ConvertFromBase() const& { return ConvertFromBase{i}; }
+operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
+};
+struct Derived : public Base {
+operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
+operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
+};
+struct ConstructFromBase {
+Instrument i;
+ConstructFromBase(const Base& b): i(b.i) {}
+ConstructFromBase(Base&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromDerived {
+Instrument i;
+ConstructFromDerived(const Derived& d): i(d.i) {}
+ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
+};
+
+struct TrivialInstrument {
+int i = 42;
+};
+struct ConvertFromTrivialBase { TrivialInstrument i; };
+struct ConvertFromTrivialDerived { TrivialInstrument i; };
+struct TrivialBase {
+TrivialInstrument i;
+operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
+operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
+};
+struct TrivialDerived : public TrivialBase {
+operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
+operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
+};
+struct ConstructFromTrivialBase {
+TrivialInstrument i;
+ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {}
+ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromTrivialDerived {
+TrivialInstrument i;
+ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {}
+ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {}
+};
+
+Derived test1() {
+Derived d1;
+return d1;  // ok
+}
+Base test2() {
+Derived d2;
+return d2;  // e1
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)"
+}
+ConstructFromDerived test3() {
+Derived d3;
+return d3;  // e2-cxx11
+// expected-warning@-1{{would have been copied despite being returned by name}}
+// expected-note@-2{{to avoid copying on older compilers}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)"
+}
+ConstructFromBase test4() {
+Derived d4;
+return d4;  // e3
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)"
+}
+ConvertFromDerived test5() {
+Derived d5;
+return d5;  // e4
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)"
+}
+ConvertFromBase test6() {
+Derived d6;
+return d6;  // e5
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)"
+}
+
+// These test cases should not produce the warning.
+Derived ok1() { Derived d; return d; }
+Base ok2() { Derived d; return static_cast(d); }

[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-03-10 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone updated this revision to Diff 137921.
Quuxplusone added a comment.

Rebased on https://reviews.llvm.org/D43898 after addressing @rtrieu's latest 
comments over there.


Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/SemaStmt.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: test/SemaCXX/warn-return-std-move.cpp
===
--- /dev/null
+++ test/SemaCXX/warn-return-std-move.cpp
@@ -0,0 +1,334 @@
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// definitions for std::move
+namespace std {
+inline namespace foo {
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+
+template  typename remove_reference::type &(T &);
+} // namespace foo
+} // namespace std
+
+struct Instrument {
+Instrument() {}
+Instrument(Instrument&&) { /* MOVE */ }
+Instrument(const Instrument&) { /* COPY */ }
+};
+struct ConvertFromBase { Instrument i; };
+struct ConvertFromDerived { Instrument i; };
+struct Base {
+Instrument i;
+operator ConvertFromBase() const& { return ConvertFromBase{i}; }
+operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
+};
+struct Derived : public Base {
+operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
+operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
+};
+struct ConstructFromBase {
+Instrument i;
+ConstructFromBase(const Base& b): i(b.i) {}
+ConstructFromBase(Base&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromDerived {
+Instrument i;
+ConstructFromDerived(const Derived& d): i(d.i) {}
+ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
+};
+
+struct TrivialInstrument {
+int i = 42;
+};
+struct ConvertFromTrivialBase { TrivialInstrument i; };
+struct ConvertFromTrivialDerived { TrivialInstrument i; };
+struct TrivialBase {
+TrivialInstrument i;
+operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
+operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
+};
+struct TrivialDerived : public TrivialBase {
+operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
+operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
+};
+struct ConstructFromTrivialBase {
+TrivialInstrument i;
+ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {}
+ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromTrivialDerived {
+TrivialInstrument i;
+ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {}
+ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {}
+};
+
+Derived test1() {
+Derived d1;
+return d1;  // ok
+}
+Base test2() {
+Derived d2;
+return d2;  // e1
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)"
+}
+ConstructFromDerived test3() {
+Derived d3;
+return d3;  // e2-cxx11
+// expected-warning@-1{{would have been copied despite being returned by name}}
+// expected-note@-2{{to avoid copying on older compilers}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)"
+}
+ConstructFromBase test4() {
+Derived d4;
+return d4;  // e3
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)"
+}
+ConvertFromDerived test5() {
+Derived d5;
+return d5;  // e4
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)"
+}
+ConvertFromBase test6() {
+Derived d6;
+return d6;  // e5
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)"
+}
+
+// These test cases should not produce the warning.
+Derived ok1() { Derived d; return d; }
+Base ok2() { Derived d; return static_cast(d); }
+ConstructFromDerived ok3() { Derived d; return static_cast(d); }
+ConstructFromBase ok4() { Derived d; return 

[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-03-07 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added a comment.

Btw, I'm going to be talking about this patch tonight at 7 in Palo Alto. :)
https://www.meetup.com/ACCU-Bay-Area/events/248040207/
https://docs.google.com/presentation/d/18ZRnedocXSQKn9Eh67gGv-ignReHfRD7vj_dxrra1kc/


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-03-05 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added a comment.

@rtrieu (and perhaps @rsmith) ping?

The action items I need help with are:

- Review and land the refactoring patch https://reviews.llvm.org/D43898 (I 
don't have commit privs)
- Ideally, test compiling a bunch of (e.g. Google) code with 
https://reviews.llvm.org/D43322, see if there are any rough edges
- Decide whether `-Wmove` should imply `-Wreturn-std-move`
- Review and land the new-diagnostic patch https://reviews.llvm.org/D43322
- Ideally, some discussion of whether I should write a paper for San Diego 
proposing that C++ *should* move rather than copy in these cases, and whether 
that possibility should change anything about this patch

Speaking of rough edges, I did find that in my employer's codebase the 
far-and-away-most-common trigger is `return my_shared_ptr_to_derived_class;` in 
a function that is declared to return `shared_ptr`. This does 
indeed generate an extra atomic increment/decrement compared to the optimal 
code, but I could imagine someone considering that a "false positive."
Other than that one questionable scenario, everything else I've found with it 
so far has been a true positive. There are no positives (either true or false) 
in LLVM's codebase.


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-28 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added inline comments.



Comment at: include/clang/Basic/DiagnosticSemaKinds.td:5639
+  "local variable %0 will be copied despite being %select{returned|thrown}1 by 
name">,
+  InGroup, DefaultIgnore;
+def note_add_std_move : Note<

I would like some guidance on whether it would be appropriate to turn this 
warning on as part of `-Wmove`.



Comment at: lib/Sema/SemaStmt.cpp:3083
+Diag(Value->getExprLoc(), diag::warn_return_std_move_in_cxx11)
+<< Value->getSourceRange()
+<< NRVOCandidate->getDeclName() << ResultType << QT;

This source range does the right thing; thanks @rtrieu!


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-28 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone updated this revision to Diff 136383.
Quuxplusone added a comment.

Rename some functions and parameters. Rebase onto 
https://reviews.llvm.org/D43898.


Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/SemaStmt.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: test/SemaCXX/warn-return-std-move.cpp
===
--- /dev/null
+++ test/SemaCXX/warn-return-std-move.cpp
@@ -0,0 +1,334 @@
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// definitions for std::move
+namespace std {
+inline namespace foo {
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+
+template  typename remove_reference::type &(T &);
+} // namespace foo
+} // namespace std
+
+struct Instrument {
+Instrument() {}
+Instrument(Instrument&&) { /* MOVE */ }
+Instrument(const Instrument&) { /* COPY */ }
+};
+struct ConvertFromBase { Instrument i; };
+struct ConvertFromDerived { Instrument i; };
+struct Base {
+Instrument i;
+operator ConvertFromBase() const& { return ConvertFromBase{i}; }
+operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
+};
+struct Derived : public Base {
+operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
+operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
+};
+struct ConstructFromBase {
+Instrument i;
+ConstructFromBase(const Base& b): i(b.i) {}
+ConstructFromBase(Base&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromDerived {
+Instrument i;
+ConstructFromDerived(const Derived& d): i(d.i) {}
+ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
+};
+
+struct TrivialInstrument {
+int i = 42;
+};
+struct ConvertFromTrivialBase { TrivialInstrument i; };
+struct ConvertFromTrivialDerived { TrivialInstrument i; };
+struct TrivialBase {
+TrivialInstrument i;
+operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
+operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
+};
+struct TrivialDerived : public TrivialBase {
+operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
+operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
+};
+struct ConstructFromTrivialBase {
+TrivialInstrument i;
+ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {}
+ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromTrivialDerived {
+TrivialInstrument i;
+ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {}
+ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {}
+};
+
+Derived test1() {
+Derived d1;
+return d1;  // ok
+}
+Base test2() {
+Derived d2;
+return d2;  // e1
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)"
+}
+ConstructFromDerived test3() {
+Derived d3;
+return d3;  // e2-cxx11
+// expected-warning@-1{{would have been copied despite being returned by name}}
+// expected-note@-2{{to avoid copying on older compilers}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)"
+}
+ConstructFromBase test4() {
+Derived d4;
+return d4;  // e3
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)"
+}
+ConvertFromDerived test5() {
+Derived d5;
+return d5;  // e4
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)"
+}
+ConvertFromBase test6() {
+Derived d6;
+return d6;  // e5
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)"
+}
+
+// These test cases should not produce the warning.
+Derived ok1() { Derived d; return d; }
+Base ok2() { Derived d; return static_cast(d); }
+ConstructFromDerived ok3() { Derived d; return static_cast(d); }
+ConstructFromBase ok4() { Derived d; return static_cast(d); }

[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-28 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added inline comments.



Comment at: lib/Sema/SemaStmt.cpp:2937
 
+static void AttemptMoveInitialization(Sema& S,
+  const InitializedEntity ,

rtrieu wrote:
> I have a few concerns about this function.  The code isn't a straight move 
> from the old location to this one, so it is hard to follow along the changes, 
> especially the change to the complex if conditional.  The function should be 
> commented, especially to behavior difference for setting IsFake.
> 
> It looks like when IsFake is set, then the result is discarded and not used, 
> but it is still possible on success for AsRvalue to be copied to the heap.  
> This is wasteful when it is never used again.
> 
> Another issue is the Value in the original code is used again towards the end 
> of the function on line #3013.  In the old code, Value can be updated while 
> in the new code, it does.
> 
> It may be better to split this change in two, the first adding this function 
> and the CopyElisionSemanticsKind enum and the second adding the diagnostic 
> itself.
Hi @rtrieu, and thanks!

I have split out the first half of the patch into a new revision D43898, and 
updated this one with the full patch (both halves together). Is there an easy 
way for me to make "just the second half" reviewable on its own, before the 
first half has been merged to master?

> It looks like when IsFake is set, then the result is discarded and not used, 
> but it is still possible on success for AsRvalue to be copied to the heap. 
> This is wasteful when it is never used again.

I believe you are correct. But I'm not sure if it's safe to use `AsRvalue` as 
input to `Res` (which *is* used outside this function) if it's not moved like 
this; I don't know much about the internals here. You or anyone have a 
suggestion for how to fix that issue?

> In the old code, Value can be updated while in the new code, it does.

I can't parse this, sorry.

FYI, in the patch I'm about to upload, I have renamed `!IsFake` to 
`ConvertingConstructorsOnly`, which should be more pleasing to the eye. :)


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-27 Thread Richard Trieu via Phabricator via cfe-commits
rtrieu added a comment.



> I have one very minor nit that I don't know how to fix:
> 
>   warn-return-std-move.cpp:220:12: warning: local variable 'd' will be copied
> despite being returned by name [-Wreturn-std-move]
>   return (d);  // e17
>  ^
>   warn-return-std-move.cpp:220:12: note: call 'std::move' explicitly to avoid 
> copying
>   return (d);  // e17
>  ^~~
>  std::move(d)
> 
> 
> The warning places a caret directly under the `(`, when I wish it would place 
> `^~~` under the entire expression, the way the fixit does.

You can add extra tildes to any diagnostic by passing a SourceRange to Diag() 
calls the same way you pass FixitHints.




Comment at: lib/Sema/SemaStmt.cpp:2937
 
+static void AttemptMoveInitialization(Sema& S,
+  const InitializedEntity ,

I have a few concerns about this function.  The code isn't a straight move from 
the old location to this one, so it is hard to follow along the changes, 
especially the change to the complex if conditional.  The function should be 
commented, especially to behavior difference for setting IsFake.

It looks like when IsFake is set, then the result is discarded and not used, 
but it is still possible on success for AsRvalue to be copied to the heap.  
This is wasteful when it is never used again.

Another issue is the Value in the original code is used again towards the end 
of the function on line #3013.  In the old code, Value can be updated while in 
the new code, it does.

It may be better to split this change in two, the first adding this function 
and the CopyElisionSemanticsKind enum and the second adding the diagnostic 
itself.


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-26 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added a comment.

@rtrieu please take a look?


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-23 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added a comment.

In https://reviews.llvm.org/D43322#1017190, @lebedev.ri wrote:

> Somewhat related thing i have noticed: https://godbolt.org/g/ow58JV


IIUC, you're asking whether it would be possible to detect instances of

  return take(mysharedptr);

and rewrite them into

  return take(std::move(mysharedptr));

(Notice that the "return" context is relatively important, because if 
`mysharedptr` is used again after this use, then the move-from is unsafe and 
should not be suggested. Of course it's always possible for `mysharedptr` to be 
used again after a `return` or `throw` as well, e.g. during stack 
unwinding/destructors; but there's precedent there for C++ saying we'll take 
that chance.)

If you start treating names as rvalues no matter where they appear in larger 
expressions, then you run into trouble with

  std::string hello = ...;
  std::string  = hello;
  return concatenate(hello, hello2);

where the first use of `hello` on this line moves-out-of it, and then the 
second use reads the moved-from value. My best guess is that things like that 
occur frequently enough in real-world code that (A) the Standard can't specify 
move behavior there because it would quietly break code, and (B) a Clang 
diagnostic would produce mostly false positives (in the sense that accepting 
the fixits would quietly break the user's code).  I admit it would be 
interesting to write the code and find out empirically.

Whereas in this specific limited case of `return x;` and `throw x;`, 
programmers are already trained to expect a move, and there are no (new) 
aliasing issues.

> Where though, as clang-tidy check?

My experience with this patch suggests that when the thing you need to do 
involves "run a hypothetical overload-resolution," it is basically impossible 
to do with clang-tidy, and relatively easy to do in clang proper. (I tried 
writing this check into clang-tidy first, and couldn't figure out how to do the 
overload resolution part. Whereas it was painless in clang, especially with 
`-Wpessimizing-move` to copy off of.)


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-23 Thread Roman Lebedev via Phabricator via cfe-commits
lebedev.ri added a comment.

Somewhat related thing i have noticed: https://godbolt.org/g/ow58JV
I wonder whether that also should be diagnosed? Where though, as clang-tidy 
check?


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-22 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone updated this revision to Diff 135484.
Quuxplusone added a reviewer: rsmith.
Quuxplusone added a subscriber: Rakete.
Quuxplusone added a comment.

Eliminate a couple of `auto` per comment by @Rakete.


Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/SemaStmt.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: test/SemaCXX/warn-return-std-move.cpp
===
--- /dev/null
+++ test/SemaCXX/warn-return-std-move.cpp
@@ -0,0 +1,334 @@
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// definitions for std::move
+namespace std {
+inline namespace foo {
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+
+template  typename remove_reference::type &(T &);
+} // namespace foo
+} // namespace std
+
+struct Instrument {
+Instrument() {}
+Instrument(Instrument&&) { /* MOVE */ }
+Instrument(const Instrument&) { /* COPY */ }
+};
+struct ConvertFromBase { Instrument i; };
+struct ConvertFromDerived { Instrument i; };
+struct Base {
+Instrument i;
+operator ConvertFromBase() const& { return ConvertFromBase{i}; }
+operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
+};
+struct Derived : public Base {
+operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
+operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
+};
+struct ConstructFromBase {
+Instrument i;
+ConstructFromBase(const Base& b): i(b.i) {}
+ConstructFromBase(Base&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromDerived {
+Instrument i;
+ConstructFromDerived(const Derived& d): i(d.i) {}
+ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
+};
+
+struct TrivialInstrument {
+int i = 42;
+};
+struct ConvertFromTrivialBase { TrivialInstrument i; };
+struct ConvertFromTrivialDerived { TrivialInstrument i; };
+struct TrivialBase {
+TrivialInstrument i;
+operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
+operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
+};
+struct TrivialDerived : public TrivialBase {
+operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
+operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
+};
+struct ConstructFromTrivialBase {
+TrivialInstrument i;
+ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {}
+ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromTrivialDerived {
+TrivialInstrument i;
+ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {}
+ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {}
+};
+
+Derived test1() {
+Derived d1;
+return d1;  // ok
+}
+Base test2() {
+Derived d2;
+return d2;  // e1
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)"
+}
+ConstructFromDerived test3() {
+Derived d3;
+return d3;  // e2-cxx11
+// expected-warning@-1{{would have been copied despite being returned by name}}
+// expected-note@-2{{to avoid copying on older compilers}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)"
+}
+ConstructFromBase test4() {
+Derived d4;
+return d4;  // e3
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)"
+}
+ConvertFromDerived test5() {
+Derived d5;
+return d5;  // e4
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)"
+}
+ConvertFromBase test6() {
+Derived d6;
+return d6;  // e5
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)"
+}
+
+// These test cases should not produce the warning.
+Derived ok1() { Derived d; return d; }
+Base ok2() { Derived d; return static_cast(d); }
+ConstructFromDerived ok3() { Derived d; return static_cast(d); }
+ConstructFromBase ok4() { 

[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-19 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone updated this revision to Diff 135005.
Quuxplusone added a comment.

Removed a redundant check for LValueReferenceType in the CWG1579 codepath. (In 
that branch, we know that standard C++ *did* perform the copy-to-move 
transformation, so obviously we can't have had an lvalue reference type 
originally.) I've verified that the check is still needed (not redundant) along 
the other codepath.


Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/SemaStmt.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: test/SemaCXX/warn-return-std-move.cpp
===
--- /dev/null
+++ test/SemaCXX/warn-return-std-move.cpp
@@ -0,0 +1,334 @@
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// definitions for std::move
+namespace std {
+inline namespace foo {
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+
+template  typename remove_reference::type &(T &);
+} // namespace foo
+} // namespace std
+
+struct Instrument {
+Instrument() {}
+Instrument(Instrument&&) { /* MOVE */ }
+Instrument(const Instrument&) { /* COPY */ }
+};
+struct ConvertFromBase { Instrument i; };
+struct ConvertFromDerived { Instrument i; };
+struct Base {
+Instrument i;
+operator ConvertFromBase() const& { return ConvertFromBase{i}; }
+operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
+};
+struct Derived : public Base {
+operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
+operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
+};
+struct ConstructFromBase {
+Instrument i;
+ConstructFromBase(const Base& b): i(b.i) {}
+ConstructFromBase(Base&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromDerived {
+Instrument i;
+ConstructFromDerived(const Derived& d): i(d.i) {}
+ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
+};
+
+struct TrivialInstrument {
+int i = 42;
+};
+struct ConvertFromTrivialBase { TrivialInstrument i; };
+struct ConvertFromTrivialDerived { TrivialInstrument i; };
+struct TrivialBase {
+TrivialInstrument i;
+operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
+operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
+};
+struct TrivialDerived : public TrivialBase {
+operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
+operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
+};
+struct ConstructFromTrivialBase {
+TrivialInstrument i;
+ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {}
+ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromTrivialDerived {
+TrivialInstrument i;
+ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {}
+ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {}
+};
+
+Derived test1() {
+Derived d1;
+return d1;  // ok
+}
+Base test2() {
+Derived d2;
+return d2;  // e1
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)"
+}
+ConstructFromDerived test3() {
+Derived d3;
+return d3;  // e2-cxx11
+// expected-warning@-1{{would have been copied despite being returned by name}}
+// expected-note@-2{{to avoid copying on older compilers}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)"
+}
+ConstructFromBase test4() {
+Derived d4;
+return d4;  // e3
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)"
+}
+ConvertFromDerived test5() {
+Derived d5;
+return d5;  // e4
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)"
+}
+ConvertFromBase test6() {
+Derived d6;
+return d6;  // e5
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)"
+}
+
+// These test cases should not produce the warning.
+Derived ok1() { 

[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-19 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone added a comment.

@rsmith and/or @rtrieu, please take another look?  All my TODOs are done now: 
there are fixits, and the wording of the diagnostic changes if it's a "throw" 
instead of a "return", and the wording has been updated per Richard Smith's 
suggestions.

I have one very minor nit that I don't know how to fix:

  warn-return-std-move.cpp:220:12: warning: local variable 'd' will be copied
despite being returned by name [-Wreturn-std-move]
  return (d);  // e17
 ^
  warn-return-std-move.cpp:220:12: note: call 'std::move' explicitly to avoid 
copying
  return (d);  // e17
 ^~~
 std::move(d)

The warning places a caret directly under the `(`, when I wish it would place 
`^~~` under the entire expression, the way the fixit does.

I also spent a little time looking into whether I could/should diagnose

  auto [x, y] = std::make_pair(Derived(), Derived());
  return x;  // 'x' will be copied despite being returned by name

but I have decided that this is probably too far afield to be rolled into this 
patch, even if I could figure out how to detect it, which to a first 
approximation I cannot. So I am deliberately punting on that one.


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-19 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone updated this revision to Diff 134999.
Quuxplusone edited the summary of this revision.

Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/SemaStmt.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: test/SemaCXX/warn-return-std-move.cpp
===
--- /dev/null
+++ test/SemaCXX/warn-return-std-move.cpp
@@ -0,0 +1,327 @@
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// definitions for std::move
+namespace std {
+inline namespace foo {
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+
+template  typename remove_reference::type &(T &);
+} // namespace foo
+} // namespace std
+
+struct Instrument {
+Instrument() {}
+Instrument(Instrument&&) { /* MOVE */ }
+Instrument(const Instrument&) { /* COPY */ }
+};
+struct ConvertFromBase { Instrument i; };
+struct ConvertFromDerived { Instrument i; };
+struct Base {
+Instrument i;
+operator ConvertFromBase() const& { return ConvertFromBase{i}; }
+operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
+};
+struct Derived : public Base {
+operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
+operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
+};
+struct ConstructFromBase {
+Instrument i;
+ConstructFromBase(const Base& b): i(b.i) {}
+ConstructFromBase(Base&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromDerived {
+Instrument i;
+ConstructFromDerived(const Derived& d): i(d.i) {}
+ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
+};
+
+struct TrivialInstrument {
+int i = 42;
+};
+struct ConvertFromTrivialBase { TrivialInstrument i; };
+struct ConvertFromTrivialDerived { TrivialInstrument i; };
+struct TrivialBase {
+TrivialInstrument i;
+operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
+operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
+};
+struct TrivialDerived : public TrivialBase {
+operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
+operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
+};
+struct ConstructFromTrivialBase {
+TrivialInstrument i;
+ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {}
+ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromTrivialDerived {
+TrivialInstrument i;
+ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {}
+ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {}
+};
+
+Derived test1() {
+Derived d1;
+return d1;  // ok
+}
+Base test2() {
+Derived d2;
+return d2;  // e1
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)"
+}
+ConstructFromDerived test3() {
+Derived d3;
+return d3;  // e2-cxx11
+// expected-warning@-1{{would have been copied despite being returned by name}}
+// expected-note@-2{{to avoid copying on older compilers}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)"
+}
+ConstructFromBase test4() {
+Derived d4;
+return d4;  // e3
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)"
+}
+ConvertFromDerived test5() {
+Derived d5;
+return d5;  // e4
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)"
+}
+ConvertFromBase test6() {
+Derived d6;
+return d6;  // e5
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)"
+}
+
+// These test cases should not produce the warning.
+Derived ok1() { Derived d; return d; }
+Base ok2() { Derived d; return static_cast(d); }
+ConstructFromDerived ok3() { Derived d; return static_cast(d); }
+ConstructFromBase ok4() { Derived d; return static_cast(d); }
+ConvertFromDerived ok5() { Derived d; return static_cast(d); 

[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-16 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone updated this revision to Diff 134779.
Quuxplusone added a comment.

Add fixits.


Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/SemaStmt.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: test/SemaCXX/warn-return-std-move.cpp
===
--- /dev/null
+++ test/SemaCXX/warn-return-std-move.cpp
@@ -0,0 +1,311 @@
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+// definitions for std::move
+namespace std {
+inline namespace foo {
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+
+template  typename remove_reference::type &(T &);
+} // namespace foo
+} // namespace std
+
+struct Instrument {
+Instrument() {}
+Instrument(Instrument&&) { /* MOVE */ }
+Instrument(const Instrument&) { /* COPY */ }
+};
+struct ConvertFromBase { Instrument i; };
+struct ConvertFromDerived { Instrument i; };
+struct Base {
+Instrument i;
+operator ConvertFromBase() const& { return ConvertFromBase{i}; }
+operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
+};
+struct Derived : public Base {
+operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
+operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
+};
+struct ConstructFromBase {
+Instrument i;
+ConstructFromBase(const Base& b): i(b.i) {}
+ConstructFromBase(Base&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromDerived {
+Instrument i;
+ConstructFromDerived(const Derived& d): i(d.i) {}
+ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
+};
+
+struct TrivialInstrument {
+int i = 42;
+};
+struct ConvertFromTrivialBase { TrivialInstrument i; };
+struct ConvertFromTrivialDerived { TrivialInstrument i; };
+struct TrivialBase {
+TrivialInstrument i;
+operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
+operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
+};
+struct TrivialDerived : public TrivialBase {
+operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
+operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
+};
+struct ConstructFromTrivialBase {
+TrivialInstrument i;
+ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {}
+ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromTrivialDerived {
+TrivialInstrument i;
+ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {}
+ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {}
+};
+
+Derived test1() {
+Derived d1;
+return d1;  // ok
+}
+Base test2() {
+Derived d2;
+return d2;  // e1
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d2)"
+}
+ConstructFromDerived test3() {
+Derived d3;
+return d3;  // e2-cxx11
+// expected-warning@-1{{would have been copied despite being returned by name}}
+// expected-note@-2{{to avoid copying on older compilers}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d3)"
+}
+ConstructFromBase test4() {
+Derived d4;
+return d4;  // e3
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d4)"
+}
+ConvertFromDerived test5() {
+Derived d5;
+return d5;  // e4
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d5)"
+}
+ConvertFromBase test6() {
+Derived d6;
+return d6;  // e5
+// expected-warning@-1{{will be copied despite being returned by name}}
+// expected-note@-2{{to avoid copying}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:"std::move(d6)"
+}
+
+// These test cases should not produce the warning.
+Derived ok1() { Derived d; return d; }
+Base ok2() { Derived d; return static_cast(d); }
+ConstructFromDerived ok3() { Derived d; return static_cast(d); }
+ConstructFromBase ok4() { Derived d; return static_cast(d); }
+ConvertFromDerived ok5() { Derived d; return static_cast(d); }

[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-15 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone updated this revision to Diff 134514.
Quuxplusone added a comment.

Reword the diagnostic per rsmith's suggestions.
Still TODO:

- figure out how to detect whether this is a return-stmt or throw-expression
- figure out how to generate the appropriate fixit


Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/SemaStmt.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: test/SemaCXX/warn-return-std-move.cpp
===
--- /dev/null
+++ test/SemaCXX/warn-return-std-move.cpp
@@ -0,0 +1,262 @@
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -verify %s
+
+// definitions for std::move
+namespace std {
+inline namespace foo {
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+
+template  typename remove_reference::type &(T &);
+} // namespace foo
+} // namespace std
+
+struct Instrument {
+Instrument() {}
+Instrument(Instrument&&) { /* MOVE */ }
+Instrument(const Instrument&) { /* COPY */ }
+};
+struct ConvertFromBase { Instrument i; };
+struct ConvertFromDerived { Instrument i; };
+struct Base {
+Instrument i;
+operator ConvertFromBase() const& { return ConvertFromBase{i}; }
+operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
+};
+struct Derived : public Base {
+operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
+operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
+};
+struct ConstructFromBase {
+Instrument i;
+ConstructFromBase(const Base& b): i(b.i) {}
+ConstructFromBase(Base&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromDerived {
+Instrument i;
+ConstructFromDerived(const Derived& d): i(d.i) {}
+ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
+};
+
+struct TrivialInstrument {
+int i = 42;
+};
+struct ConvertFromTrivialBase { TrivialInstrument i; };
+struct ConvertFromTrivialDerived { TrivialInstrument i; };
+struct TrivialBase {
+TrivialInstrument i;
+operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
+operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
+};
+struct TrivialDerived : public TrivialBase {
+operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
+operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
+};
+struct ConstructFromTrivialBase {
+TrivialInstrument i;
+ConstructFromTrivialBase(const TrivialBase& b): i(b.i) {}
+ConstructFromTrivialBase(TrivialBase&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromTrivialDerived {
+TrivialInstrument i;
+ConstructFromTrivialDerived(const TrivialDerived& d): i(d.i) {}
+ConstructFromTrivialDerived(TrivialDerived&& d): i(std::move(d.i)) {}
+};
+
+Derived test1() {
+Derived d1;
+return d1;  // ok
+}
+Base test2() {
+Derived d2;
+return d2;  // e1
+// expected-warning@-1{{will be copied despite being returned by name}}
+}
+ConstructFromDerived test3() {
+Derived d3;
+return d3;  // e2-cxx11
+// expected-warning@-1{{would have been copied despite being returned by name}}
+}
+ConstructFromBase test4() {
+Derived d4;
+return d4;  // e3
+// expected-warning@-1{{will be copied despite being returned by name}}
+}
+ConvertFromDerived test5() {
+Derived d5;
+return d5;  // e4
+// expected-warning@-1{{will be copied despite being returned by name}}
+}
+ConvertFromBase test6() {
+Derived d6;
+return d6;  // e5
+// expected-warning@-1{{will be copied despite being returned by name}}
+}
+
+// These test cases should not produce the warning.
+Derived ok1() { Derived d; return d; }
+Base ok2() { Derived d; return static_cast(d); }
+ConstructFromDerived ok3() { Derived d; return static_cast(d); }
+ConstructFromBase ok4() { Derived d; return static_cast(d); }
+ConvertFromDerived ok5() { Derived d; return static_cast(d); }
+ConvertFromBase ok6() { Derived d; return static_cast(d); }
+
+// If the target is an lvalue reference, assume it's not safe to move from.
+Derived ok_lvalue1(Derived& d) { return d; }
+Base ok_lvalue2(Derived& d) { return d; }
+ConstructFromDerived ok_lvalue3(Derived& d) { return d; }
+ConstructFromBase ok_lvalue4(Derived& d) { return d; }
+ConvertFromDerived ok_lvalue5(Derived& d) { return d; }
+ConvertFromBase ok_lvalue6(Derived& d) { return d; }
+
+// If the target is a global, assume it's not safe to move from.
+static Derived global_d;
+Derived ok_global1() { return global_d; }
+Base 

[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-15 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone marked an inline comment as done.
Quuxplusone added inline comments.



Comment at: include/clang/Basic/DiagnosticSemaKinds.td:23
+def warn_return_std_move : Warning<
+  "adding 'std::move' here would select a better conversion sequence">,
+  InGroup, DefaultIgnore;

rsmith wrote:
> Can we say something like "local variable '%0' will be copied despite being 
> %select{returned|thrown}1 by name; call 'std::move' explicitly to avoid the 
> copy"? (Would that be accurate, given the implementation of the warning?)
> 
> Ideally, we'd move the "call 'std::move' explicitly" hint to a separate note 
> diagnostic and include a FixItHint on that diagnostic to insert the call to 
> `std::move`.
SGTM, except that I don't know how to find out whether we're in the context of 
a `return` or a `throw` from this deep in the guts of Sema. Could someone give 
me a pointer on that?

I also had trouble figuring out how to generate a fixit from `x` to 
`std::move(x)` — it keeps coming out as `std::move( )` — but I expect I'll be 
able to solve that one even without help by banging my head against it a bit.



Comment at: include/clang/Sema/Sema.h:3827
   bool isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
-  bool AllowParamOrMoveConstructible);
+  CopyElisionSemanticsKind CESK);
 

Q: Is it appropriate for me to be changing the signature of this public method 
at all? I don't know libclang's rules regarding this kind of change.



Comment at: lib/Sema/SemaExprCXX.cpp:731
 if (IsThrownVarInScope)
-  NRVOVariable = getCopyElisionCandidate(QualType(), Ex, false);
+  NRVOVariable = getCopyElisionCandidate(QualType(), Ex, CES_Strict);
 

Q: I'm not sure what concept is being represented by "CES_Strict" here. If 
there is a more standardese-appropriate name, I'd like to use it. And if there 
are multiple concepts here that just happen to be identical *coincidentally*, 
I'd probably prefer to use multiple enumerators that all just happen to have 
value `0`. But because there are so many call-sites and because this is not 
directly related to my patch, I have not investigated much.


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-14 Thread Richard Smith - zygoloid via Phabricator via cfe-commits
rsmith added inline comments.



Comment at: include/clang/Basic/DiagnosticSemaKinds.td:23
+def warn_return_std_move : Warning<
+  "adding 'std::move' here would select a better conversion sequence">,
+  InGroup, DefaultIgnore;

Can we say something like "local variable '%0' will be copied despite being 
%select{returned|thrown}1 by name; call 'std::move' explicitly to avoid the 
copy"? (Would that be accurate, given the implementation of the warning?)

Ideally, we'd move the "call 'std::move' explicitly" hint to a separate note 
diagnostic and include a FixItHint on that diagnostic to insert the call to 
`std::move`.



Comment at: include/clang/Basic/DiagnosticSemaKinds.td:27
+def warn_return_std_move_in_cxx11 : Warning<
+  "adding 'std::move' here would have selected a better conversion sequence 
prior to CWG 1579">,
+  InGroup, DefaultIgnore;

I think the wording of this diagnostic could be improved too. It's very 
language-lawyer-friendly right now. Here's one possible starting point for 
alternative wording (though I think maybe we can do better):

"ISO C++11, prior to the resolution of defect reports, would have made a copy 
of local variable '%0' despite it being returned by name due to a type mismatch 
with the return value%diff{ ($ vs $)|}1, 2"

... plus a "note: call 'std::move' explicitly to avoid a copy when using older 
compilers" or similar, with a fixit.


Repository:
  rC Clang

https://reviews.llvm.org/D43322



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D43322: Diagnose cases of "return x" that should be "return std::move(x)" for efficiency

2018-02-14 Thread Arthur O'Dwyer via Phabricator via cfe-commits
Quuxplusone created this revision.
Quuxplusone added a project: clang.
Herald added a subscriber: cfe-commits.

This patch adds two new diagnostics, which are off by default:

**-Wreturn-std-move**

Diagnose cases of `return x` or `throw x`, where `x` is the name of a local 
variable or parameter, in which a copy is made.
The user probably expected a move, but they're not getting a move, perhaps 
because the type of "x" is different from the return type of the function. A 
place where this comes up in the wild is `stdext::inplace_function` 
which implements conversion via a conversion operator rather than a converting 
constructor; see https://github.com/WG21-SG14/SG14/issues/125#issue-297201412
Another place where this has come up in the wild, but where the fix ended up 
being different, was

  try { ... } catch (ExceptionType ex) {
  throw ex;
  }

where the appropriate fix in that case was to replace `throw ex;` with 
`throw;`, and incidentally to catch by reference instead of by value. (But one 
could contrive a scenario where the slicing was intentional, in which case 
throw-by-move would have been the appropriate fix after all.)

**-Wreturn-std-move-in-c++11**

Diagnose cases of "return x;" or "throw x;" which in this version of Clang *do* 
produce moves, but which prior to Clang 3.9 / GCC 5.1 produced copies instead. 
This is useful in codebases which care about portability to those older 
compilers.
The name "-in-c++11" is not technically correct; what caused the 
version-to-version change in behavior here was actually CWG 1579, not C++14. So 
I'm using the CWG issue number in the diagnostic but "C++11" in the name of the 
option. I think it's likely that codebases that need portability to GCC 
4.9-and-earlier may understand "C++11" as a colloquialism for "older compilers."

**Discussion**

I fully expect there to be lots of discussion on this patch, and for my first 
draft to be missing lots of pieces. I do hope that we can hammer it into a 
shape suitable for upstreaming, but milestone number 1 is just to get it out 
there for people to use on their own codebases.

Notice that this patch is kind of a belated negative-space version of Richard 
Trieu's `-Wpessimizing-move`. That diagnostic warns about cases of `return 
std::move(x)` that should be `return x` for speed. These diagnostics warn about 
cases of `return x` that should be `return std::move(x)` for speed. (The two 
diagnostics' bailiwicks do not overlap.)


Repository:
  rC Clang

https://reviews.llvm.org/D43322

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/SemaStmt.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  test/SemaCXX/warn-return-std-move.cpp

Index: test/SemaCXX/warn-return-std-move.cpp
===
--- /dev/null
+++ test/SemaCXX/warn-return-std-move.cpp
@@ -0,0 +1,262 @@
+// RUN: %clang_cc1 -fsyntax-only -Wreturn-std-move -Wreturn-std-move-in-cxx11 -std=c++11 -verify %s
+
+// definitions for std::move
+namespace std {
+inline namespace foo {
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+template  struct remove_reference { typedef T type; };
+
+template  typename remove_reference::type &(T &);
+}
+}
+
+struct Instrument {
+Instrument() {}
+Instrument(Instrument&&) { /* MOVE */ }
+Instrument(const Instrument&) { /* COPY */ }
+};
+struct ConvertFromBase { Instrument i; };
+struct ConvertFromDerived { Instrument i; };
+struct Base {
+Instrument i;
+operator ConvertFromBase() const& { return ConvertFromBase{i}; }
+operator ConvertFromBase() && { return ConvertFromBase{std::move(i)}; }
+};
+struct Derived : public Base {
+operator ConvertFromDerived() const& { return ConvertFromDerived{i}; }
+operator ConvertFromDerived() && { return ConvertFromDerived{std::move(i)}; }
+};
+struct ConstructFromBase {
+Instrument i;
+ConstructFromBase(const Base& b): i(b.i) {}
+ConstructFromBase(Base&& b): i(std::move(b.i)) {}
+};
+struct ConstructFromDerived {
+Instrument i;
+ConstructFromDerived(const Derived& d): i(d.i) {}
+ConstructFromDerived(Derived&& d): i(std::move(d.i)) {}
+};
+
+struct TrivialInstrument {
+int i = 42;
+};
+struct ConvertFromTrivialBase { TrivialInstrument i; };
+struct ConvertFromTrivialDerived { TrivialInstrument i; };
+struct TrivialBase {
+TrivialInstrument i;
+operator ConvertFromTrivialBase() const& { return ConvertFromTrivialBase{i}; }
+operator ConvertFromTrivialBase() && { return ConvertFromTrivialBase{std::move(i)}; }
+};
+struct TrivialDerived : public TrivialBase {
+operator ConvertFromTrivialDerived() const& { return ConvertFromTrivialDerived{i}; }
+operator ConvertFromTrivialDerived() && { return ConvertFromTrivialDerived{std::move(i)}; }
+};
+struct ConstructFromTrivialBase {
+