From f53535abfc56796fd81a6dbe001a2d1b3d301055 Mon Sep 17 00:00:00 2001 From: Richard Trieu Date: Tue, 26 Aug 2014 04:30:55 +0000 Subject: [PATCH] Passing a variable to std::move now counts as a use for -Wuninitialized git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@216438 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Analysis/UninitializedValues.cpp | 10 +++++ lib/Sema/SemaDecl.cpp | 15 ++++++++ lib/Sema/SemaDeclCXX.cpp | 15 ++++++++ test/SemaCXX/uninitialized.cpp | 57 +++++++++++++++++++++++++++- 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp index 0f93fe8981..da4d072807 100644 --- a/lib/Analysis/UninitializedValues.cpp +++ b/lib/Analysis/UninitializedValues.cpp @@ -341,6 +341,16 @@ void ClassifyRefs::VisitUnaryOperator(UnaryOperator *UO) { } void ClassifyRefs::VisitCallExpr(CallExpr *CE) { + // Classify arguments to std::move as used. + if (CE->getNumArgs() == 1) { + if (FunctionDecl *FD = CE->getDirectCallee()) { + if (FD->getIdentifier() && FD->getIdentifier()->isStr("move")) { + classify(CE->getArg(0), Use); + return; + } + } + } + // If a value is passed by const reference to a function, we should not assume // that it is initialized by the call, and we conservatively do not assume // that it is used. diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 5293188214..0079c4925a 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -8288,6 +8288,21 @@ namespace { Inherited::VisitCXXConstructExpr(E); } + void VisitCallExpr(CallExpr *E) { + // Treat std::move as a use. + if (E->getNumArgs() == 1) { + if (FunctionDecl *FD = E->getDirectCallee()) { + if (FD->getIdentifier() && FD->getIdentifier()->isStr("move")) { + if (DeclRefExpr *DRE = dyn_cast(E->getArg(0))) { + HandleDeclRefExpr(DRE); + } + } + } + } + + Inherited::VisitCallExpr(E); + } + void HandleDeclRefExpr(DeclRefExpr *DRE) { Decl* ReferenceDecl = DRE->getDecl(); if (OrigDecl != ReferenceDecl) return; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 82d06d126b..ac70f4b900 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -2337,6 +2337,21 @@ namespace { Inherited::VisitCXXMemberCallExpr(E); } + void VisitCallExpr(CallExpr *E) { + // Treat std::move as a use. + if (E->getNumArgs() == 1) { + if (FunctionDecl *FD = E->getDirectCallee()) { + if (FD->getIdentifier() && FD->getIdentifier()->isStr("move")) { + if (MemberExpr *ME = dyn_cast(E->getArg(0))) { + HandleMemberExpr(ME, false /*CheckReferenceOnly*/); + } + } + } + } + + Inherited::VisitCallExpr(E); + } + void VisitBinaryOperator(BinaryOperator *E) { // If a field assignment is detected, remove the field from the // uninitiailized field set. diff --git a/test/SemaCXX/uninitialized.cpp b/test/SemaCXX/uninitialized.cpp index 7b89e5adc9..8adb93a509 100644 --- a/test/SemaCXX/uninitialized.cpp +++ b/test/SemaCXX/uninitialized.cpp @@ -1,9 +1,21 @@ // RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -Wno-unused-value -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&& move(T&& t); +} +} + int foo(int x); int bar(int* x); int boo(int& x); int far(const int& x); +int moved(int&& x); // Test self-references within initializers which are guaranteed to be // uninitialized. @@ -24,6 +36,9 @@ int k = __alignof__(k); int l = k ? l : l; // expected-warning 2{{variable 'l' is uninitialized when used within its own initialization}} int m = 1 + (k ? m : m); // expected-warning 2{{variable 'm' is uninitialized when used within its own initialization}} int n = -n; // expected-warning {{variable 'n' is uninitialized when used within its own initialization}} +int o = std::move(o); // expected-warning {{variable 'o' is uninitialized when used within its own initialization}} +const int p = std::move(p); // expected-warning {{variable 'p' is uninitialized when used within its own initialization}} +int q = moved(std::move(q)); // expected-warning {{variable 'q' is uninitialized when used within its own initialization}} void test_stuff () { int a = a; // no-warning: used to signal intended lack of initialization. @@ -44,6 +59,9 @@ void test_stuff () { int l = k ? l : l; // expected-warning {{variable 'l' is uninitialized when used within its own initialization}} int m = 1 + (k ? m : m); // expected-warning {{'m' is uninitialized when used within its own initialization}} int n = -n; // expected-warning {{variable 'n' is uninitialized when used within its own initialization}} + int o = std::move(o); // expected-warning {{variable 'o' is uninitialized when used within its own initialization}} + const int p = std::move(p); // expected-warning {{variable 'p' is uninitialized when used within its own initialization}} + int q = moved(std::move(q)); // expected-warning {{variable 'q' is uninitialized when used within its own initialization}} for (;;) { int a = a; // no-warning: used to signal intended lack of initialization. @@ -64,6 +82,9 @@ void test_stuff () { int l = k ? l : l; // expected-warning {{variable 'l' is uninitialized when used within its own initialization}} int m = 1 + (k ? m : m); // expected-warning {{'m' is uninitialized when used within its own initialization}} int n = -n; // expected-warning {{variable 'n' is uninitialized when used within its own initialization}} + int o = std::move(o); // expected-warning {{variable 'o' is uninitialized when used within its own initialization}} + const int p = std::move(p); // expected-warning {{variable 'p' is uninitialized when used within its own initialization}} + int q = moved(std::move(q)); // expected-warning {{variable 'q' is uninitialized when used within its own initialization}} } } @@ -84,6 +105,7 @@ class A { A(int x) {} A(int *x) {} A(A *a) {} + A(A &&a) {} ~A(); }; @@ -91,6 +113,7 @@ A getA() { return A(); } A getA(int x) { return A(); } A getA(A* a) { return A(); } A getA(A a) { return A(); } +A moveA(A&& a) { return A(); } void setupA(bool x) { A a1; @@ -130,6 +153,11 @@ void setupA(bool x) { const A a29(a29); // expected-warning {{variable 'a29' is uninitialized when used within its own initialization}} const A a30 = a30; // expected-warning {{variable 'a30' is uninitialized when used within its own initialization}} + + A a31 = std::move(a31); // expected-warning {{variable 'a31' is uninitialized when used within its own initialization}} + A a32 = moveA(std::move(a32)); // expected-warning {{variable 'a32' is uninitialized when used within its own initialization}} + A a33 = A(std::move(a33)); // expected-warning {{variable 'a33' is uninitialized when used within its own initialization}} + A a34(std::move(a34)); // expected-warning {{variable 'a34' is uninitialized when used within its own initialization}} } bool x; @@ -169,6 +197,10 @@ A *a28 = new A(a28->num); // expected-warning {{variable 'a28' is uninitialized const A a29(a29); // expected-warning {{variable 'a29' is uninitialized when used within its own initialization}} const A a30 = a30; // expected-warning {{variable 'a30' is uninitialized when used within its own initialization}} +A a31 = std::move(a31); // expected-warning {{variable 'a31' is uninitialized when used within its own initialization}} +A a32 = moveA(std::move(a32)); // expected-warning {{variable 'a32' is uninitialized when used within its own initialization}} +A a33 = A(std::move(a33)); // expected-warning {{variable 'a33' is uninitialized when used within its own initialization}} +A a34(std::move(a34)); // expected-warning {{variable 'a34' is uninitialized when used within its own initialization}} struct B { // POD struct. int x; @@ -179,6 +211,7 @@ B getB() { return B(); }; B getB(int x) { return B(); }; B getB(int *x) { return B(); }; B getB(B *b) { return B(); }; +B moveB(B &&b) { return B(); }; B* getPtrB() { return 0; }; B* getPtrB(int x) { return 0; }; @@ -219,6 +252,9 @@ void setupB() { const B b19 = b19; // expected-warning {{variable 'b19' is uninitialized when used within its own initialization}} const B b20(b20); // expected-warning {{variable 'b20' is uninitialized when used within its own initialization}} + B b21 = std::move(b21); // expected-warning {{variable 'b21' is uninitialized when used within its own initialization}} + B b22 = moveB(std::move(b22)); // expected-warning {{variable 'b22' is uninitialized when used within its own initialization}} + B b23 = B(std::move(b23)); // expected-warning {{variable 'b23' is uninitialized when used within its own initialization}} } B b1; @@ -247,6 +283,10 @@ B b18 = { b18.x + 1, b18.y }; // expected-warning 2{{variable 'b18' is uninitia const B b19 = b19; // expected-warning {{variable 'b19' is uninitialized when used within its own initialization}} const B b20(b20); // expected-warning {{variable 'b20' is uninitialized when used within its own initialization}} +B b21 = std::move(b21); // expected-warning {{variable 'b21' is uninitialized when used within its own initialization}} +B b22 = moveB(std::move(b22)); // expected-warning {{variable 'b22' is uninitialized when used within its own initialization}} +B b23 = B(std::move(b23)); // expected-warning {{variable 'b23' is uninitialized when used within its own initialization}} + // Also test similar constructs in a field's initializer. struct S { int x; @@ -558,6 +598,7 @@ namespace record_fields { A const_ref(const A&); A pointer(A*); A normal(A); + A rref(A&&); struct B { A a; @@ -570,9 +611,12 @@ namespace record_fields { B(char (*)[7]) : a(const_ref(a)) {} B(char (*)[8]) : a(pointer(&a)) {} B(char (*)[9]) : a(normal(a)) {} // expected-warning {{uninitialized}} + B(char (*)[10]) : a(std::move(a)) {} // expected-warning {{uninitialized}} + B(char (*)[11]) : a(A(std::move(a))) {} // expected-warning {{uninitialized}} + B(char (*)[12]) : a(rref(std::move(a))) {} // expected-warning {{uninitialized}} }; struct C { - C() {} // expected-note5{{in this constructor}} + C() {} // expected-note8{{in this constructor}} A a1 = a1; // expected-warning {{uninitialized}} A a2 = a2.get(); // expected-warning {{uninitialized}} A a3 = a3.num(); @@ -583,8 +627,11 @@ namespace record_fields { A a8 = pointer(&a8); A a9 = normal(a9); // expected-warning {{uninitialized}} const A a10 = a10; // expected-warning {{uninitialized}} + A a11 = std::move(a11); // expected-warning {{uninitialized}} + A a12 = A(std::move(a12)); // expected-warning {{uninitialized}} + A a13 = rref(std::move(a13)); // expected-warning {{uninitialized}} }; - struct D { // expected-note5{{in the implicit default constructor}} + struct D { // expected-note8{{in the implicit default constructor}} A a1 = a1; // expected-warning {{uninitialized}} A a2 = a2.get(); // expected-warning {{uninitialized}} A a3 = a3.num(); @@ -595,6 +642,9 @@ namespace record_fields { A a8 = pointer(&a8); A a9 = normal(a9); // expected-warning {{uninitialized}} const A a10 = a10; // expected-warning {{uninitialized}} + A a11 = std::move(a11); // expected-warning {{uninitialized}} + A a12 = A(std::move(a12)); // expected-warning {{uninitialized}} + A a13 = rref(std::move(a13)); // expected-warning {{uninitialized}} }; D d; struct E { @@ -608,6 +658,9 @@ namespace record_fields { A a8 = pointer(&a8); A a9 = normal(a9); const A a10 = a10; + A a11 = std::move(a11); + A a12 = A(std::move(a12)); + A a13 = rref(std::move(a13)); }; } -- 2.40.0