Improve Wrange-loop-analyses for rvalue reference
authorMark de Wever <koraq@xs4all.nl>
Wed, 1 Jan 2020 16:23:20 +0000 (17:23 +0100)
committerMark de Wever <koraq@xs4all.nl>
Wed, 1 Jan 2020 19:02:18 +0000 (20:02 +0100)
The Wrange-loop-analyses warns if a copy is made. Suppress this warning when
a temporary is bound to a rvalue reference.

While fixing this issue also found a copy-paste error in test6, which is also
fixed.

Differential Revision: https://reviews.llvm.org/D71806

clang/lib/Sema/SemaStmt.cpp
clang/test/SemaCXX/warn-range-loop-analysis.cpp

index 959adbf..1e7cc50 100644 (file)
@@ -2764,9 +2764,11 @@ static void DiagnoseForRangeReferenceVariableCopies(Sema &SemaRef,
     SemaRef.Diag(VD->getBeginLoc(), diag::note_use_type_or_non_reference)
         << NonReferenceType << NewReferenceType << VD->getSourceRange()
         << FixItHint::CreateRemoval(VD->getTypeSpecEndLoc());
-  } else {
+  } else if (!VariableType->isRValueReferenceType()) {
     // The range always returns a copy, so a temporary is always created.
     // Suggest removing the reference from the loop variable.
+    // If the type is a rvalue reference do not warn since that changes the
+    // semantic of the code.
     SemaRef.Diag(VD->getLocation(), diag::warn_for_range_variable_always_copy)
         << VD << RangeInitType;
     QualType NonReferenceType = VariableType.getNonReferenceType();
index a4db9e7..21c5747 100644 (file)
@@ -84,34 +84,46 @@ void test0() {
 void test1() {
   Container<int> A;
 
+  for (const int &&x : A) {}
+  // No warning, rvalue-reference to the temporary
   for (const int &x : A) {}
   // expected-warning@-1 {{always a copy}}
   // expected-note@-2 {{'int'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:19}:""
   for (const int x : A) {}
   // No warning, non-reference type indicates copy is made
+  for (int&& x : A) {}
+  // No warning, rvalue-reference to the temporary
   //for (int &x : A) {}
   // Binding error
   for (int x : A) {}
   // No warning, non-reference type indicates copy is made
 
+  for (const double &&x : A) {}
+  // No warning, rvalue-reference to the temporary
   for (const double &x : A) {}
   // expected-warning@-1 {{always a copy}}
   // expected-note@-2 {{'double'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:21-[[@LINE-3]]:22}:""
   for (const double x : A) {}
   // No warning, non-reference type indicates copy is made
+  for (double &&x : A) {}
+  // No warning, rvalue-reference to the temporary
   //for (double &x : A) {}
   // Binding error
   for (double x : A) {}
   // No warning, non-reference type indicates copy is made
 
+  for (const Bar &&x : A) {}
+  // No warning, rvalue-reference to the temporary
   for (const Bar &x : A) {}
   // expected-warning@-1 {{always a copy}}
   // expected-note@-2 {{'Bar'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:19}:""
   for (const Bar x : A) {}
   // No warning, non-reference type indicates copy is made
+  for (Bar &&x : A) {}
+  // No warning, rvalue-reference to the temporary
   //for (Bar &x : A) {}
   // Binding error
   for (Bar x : A) {}
@@ -121,30 +133,50 @@ void test1() {
 void test2() {
   Container<int&> B;
 
+  //for (const int &&x : B) {}
+  // Binding error
   for (const int &x : B) {}
   // No warning, this reference is not a temporary
   for (const int x : B) {}
   // No warning on POD copy
+  //for (int &x : B) {}
+  // Binding error
   for (int &x : B) {}
   // No warning
   for (int x : B) {}
   // No warning
 
+  for (const double &&x : B) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'double'{{.*}}'const int &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:21-[[@LINE-3]]:23}:""
   for (const double &x : B) {}
   // expected-warning@-1 {{resulting in a copy}}
   // expected-note-re@-2 {{'double'{{.*}}'const int &'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:21-[[@LINE-3]]:22}:""
   for (const double x : B) {}
+  for (double &&x : B) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'double'{{.*}}'const int &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:15-[[@LINE-3]]:17}:""
   //for (double &x : B) {}
   // Binding error
   for (double x : B) {}
   // No warning
 
+  for (const Bar &&x : B) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note@-2 {{'Bar'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:20}:""
   for (const Bar &x : B) {}
   // expected-warning@-1 {{resulting in a copy}}
   // expected-note@-2 {{'Bar'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:19}:""
   for (const Bar x : B) {}
+  for (Bar &&x : B) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note@-2 {{'Bar'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:""
   //for (Bar &x : B) {}
   // Binding error
   for (Bar x : B) {}
@@ -154,23 +186,31 @@ void test2() {
 void test3() {
   Container<Bar> C;
 
+  for (const Bar &&x : C) {}
+  // No warning, rvalue-reference to the temporary
   for (const Bar &x : C) {}
   // expected-warning@-1 {{always a copy}}
   // expected-note@-2 {{'Bar'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:19}:""
   for (const Bar x : C) {}
   // No warning, non-reference type indicates copy is made
+  for (Bar &&x : C) {}
+  // No warning, rvalue-reference to the temporary
   //for (Bar &x : C) {}
   // Binding error
   for (Bar x : C) {}
   // No warning, non-reference type indicates copy is made
 
+  for (const int &&x : C) {}
+  // No warning, rvalue-reference to the temporary
   for (const int &x : C) {}
   // expected-warning@-1 {{always a copy}}
   // expected-note@-2 {{'int'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:19}:""
   for (const int x : C) {}
   // No warning, copy made
+  for (int &&x : C) {}
+  // No warning, rvalue-reference to the temporary
   //for (int &x : C) {}
   // Binding error
   for (int x : C) {}
@@ -180,23 +220,35 @@ void test3() {
 void test4() {
   Container<Bar&> D;
 
+  //for (const Bar &&x : D) {}
+  // Binding error
   for (const Bar &x : D) {}
   // No warning, this reference is not a temporary
   for (const Bar x : D) {}
   // expected-warning@-1 {{creates a copy}}
   // expected-note@-2 {{'const Bar &'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:18}:"&"
+  //for (Bar &&x : D) {}
+  // Binding error
   for (Bar &x : D) {}
   // No warning
   for (Bar x : D) {}
   // No warning
 
+  for (const int &&x : D) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'int'{{.*}}'const Bar &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:20}:""
   for (const int &x : D) {}
   // expected-warning@-1 {{resulting in a copy}}
   // expected-note-re@-2 {{'int'{{.*}}'const Bar &'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:19}:""
   for (const int x : D) {}
   // No warning
+  for (int &&x : D) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'int'{{.*}}'const Bar &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:""
   //for (int &x : D) {}
   // Binding error
   for (int x : D) {}
@@ -206,12 +258,16 @@ void test4() {
 void test5() {
   Container<Foo> E;
 
+  for (const Bar &&x : E) {}
+  // No warning, rvalue-reference to the temporary
   for (const Bar &x : E) {}
   // expected-warning@-1 {{always a copy}}
   // expected-note@-2 {{'Bar'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:19}:""
   for (const Bar x : E) {}
   // No warning, non-reference type indicates copy is made
+  for (Bar &&x : E) {}
+  // No warning, rvalue-reference to the temporary
   //for (Bar &x : E) {}
   // Binding error
   for (Bar x : E) {}
@@ -221,12 +277,20 @@ void test5() {
 void test6() {
   Container<Foo&> F;
 
+  for (const Bar &&x : F) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'Bar'{{.*}}'const Foo &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:20}:""
   for (const Bar &x : F) {}
   // expected-warning@-1 {{resulting in a copy}}
   // expected-note-re@-2 {{'Bar'{{.*}}'const Foo &'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:19}:""
   for (const Bar x : F) {}
   // No warning.
+  for (Bar &&x : F) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'Bar'{{.*}}'const Foo &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:""
   //for (Bar &x : F) {}
   // Binding error
   for (Bar x : F) {}
@@ -236,56 +300,88 @@ void test6() {
 void test7() {
   double G[2];
 
+  //for (const double &&x : G) {}
+  // Binding error
   for (const double &x : G) {}
   // No warning
   for (const double x : G) {}
   // No warning on POD copy
+  //for (double &&x : G) {}
+  // Binding error
   for (double &x : G) {}
   // No warning
   for (double x : G) {}
   // No warning
 
+  for (const int &&x : G) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'int'{{.*}}'const double &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:20}:""
   for (const int &x : G) {}
   // expected-warning@-1 {{resulting in a copy}}
   // expected-note-re@-2 {{'int'{{.*}}'const double &'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:19}:""
   for (const int x : G) {}
   // No warning
+  for (int &&x : G) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'int'{{.*}}'const double &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:""
   //for (int &x : G) {}
   // Binding error
   for (int x : G) {}
   // No warning
 
+  for (const Bar &&x : G) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'Bar'{{.*}}'const double &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:20}:""
   for (const Bar &x : G) {}
   // expected-warning@-1 {{resulting in a copy}}
   // expected-note-re@-2 {{'Bar'{{.*}}'const double &'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:19}:""
   for (const Bar x : G) {}
   // No warning
-  //for (int &Bar : G) {}
+  for (Bar &&x : G) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'Bar'{{.*}}'const double &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:""
+  //for (Bar &x : G) {}
   // Binding error
-  for (int Bar : G) {}
+  for (Bar x : G) {}
   // No warning
 }
 
 void test8() {
   Foo H[2];
 
+  //for (const Foo &&x : H) {}
+  // Binding error
   for (const Foo &x : H) {}
   // No warning
   for (const Foo x : H) {}
   // No warning on POD copy
+  //for (Foo &&x : H) {}
+  // Binding error
   for (Foo &x : H) {}
   // No warning
   for (Foo x : H) {}
   // No warning
 
+  for (const Bar &&x : H) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'Bar'{{.*}}'const Foo &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:20}:""
   for (const Bar &x : H) {}
   // expected-warning@-1 {{resulting in a copy}}
   // expected-note-re@-2 {{'Bar'{{.*}}'const Foo &'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:19}:""
   for (const Bar x : H) {}
   // No warning
+  for (Bar &&x: H) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'Bar'{{.*}}'const Foo &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:""
   //for (Bar &x: H) {}
   // Binding error
   for (Bar x: H) {}
@@ -295,23 +391,35 @@ void test8() {
 void test9() {
   Bar I[2] = {1,2};
 
+  //for (const Bar &&x : I) {}
+  // Binding error
   for (const Bar &x : I) {}
   // No warning
   for (const Bar x : I) {}
   // expected-warning@-1 {{creates a copy}}
   // expected-note@-2 {{'const Bar &'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:18}:"&"
+  //for (Bar &&x : I) {}
+  // Binding error
   for (Bar &x : I) {}
   // No warning
   for (Bar x : I) {}
   // No warning
 
+  for (const int &&x : I) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'int'{{.*}}'const Bar &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:20}:""
   for (const int &x : I) {}
   // expected-warning@-1 {{resulting in a copy}}
   // expected-note-re@-2 {{'int'{{.*}}'const Bar &'}}
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:19}:""
   for (const int x : I) {}
   // No warning
+  for (int &&x : I) {}
+  // expected-warning@-1 {{resulting in a copy}}
+  // expected-note-re@-2 {{'int'{{.*}}'const Bar &'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:12-[[@LINE-3]]:14}:""
   //for (int &x : I) {}
   // Binding error
   for (int x : I) {}