Detect use after move during compilation? - c++

The clang-tidy static analyzer detects uses of variables after being moved.
class a_class {
std::unique_ptr<int> p_;
public:
auto p() -> auto& {return p_;}
void f() const {}
};
int main() {
auto aa = a_class{};
[[maybe_unused]] auto bb = std::move(aa);
aa.f();
}
error: Method called on moved-from object 'aa' [clang-analyzer-cplusplus.Move,-warnings-as-errors]
This great! ©.
How can I make the compiler, clang or GCC detect the same issue too? Either by activating some warning option or by some (non-standard?) attribute?
I tried using -Wmove in clang and the [[consumed]] attribute but they didn't help.
Perhaps I used them incorrectly.
The code is here: https://godbolt.org/z/18hr4vn7x (the lower panel is clang-tidy and the mid panel on the right is the [empty] compiler output)
Is there a chance a compiler will warn about this or it is just too costly for the compiler to check for this pattern?

I found one way to do it, using attributes in clang. .
(A GCC or a more standard solution is still welcome.)
needs clang 6 or higher
mark the class as "consumable"
mark the method(s) "callable-when-unconsumed" (not sure how to make this the default)
class [[clang::consumable(unconsumed)]] a_class {
std::unique_ptr<int> p_;
public:
[[clang::callable_when(unconsumed)]]
void f() {}
// private: [[clang::set_typestate(consumed)]] void invalidate() {} // not needed but good to know
};
https://godbolt.org/z/45q8vzdnc
The recipe is simplified from https://awesomekling.github.io/Catching-use-after-move-bugs-with-Clang-consumed-annotations/ .
I couldn't find detailed documentation on how to use the these features.
It is simplified because:
a) it seems that "clang-consumable" moved object becomes "consumed" by default when moved-from, so it is not necessary to write a special function to invalidate if not necessary (no need for [[clang::set_typestate(consumed)]]).
b) constructors seem to leave the object in an unconsumed state by default (no need for [[clang::return_typestate(unconsumed)]]);

If a compiler isn't built with a setting to do this, then you can't make it do this. Use-after-move is a legitimate thing in C++, so no compiler is obligated to consider it an error.
These kinds of things are what static analyzers are for.

Related

Clang++ Fails To Detect Use of Uninitialized Member Variable

Using clang++ version 11 and C++ 17 with the compiler flag -Wall, clang will normally complain if you use a variable before it is initialized. However, it does not detect the following case:
struct Bar
{
bool b1;
};
class Foo {
public:
Foo()
: b2(Bar{b2}.b1) // We are using b2 here before it is initialized, but clang doesn't complain
{ }
bool b2;
};
This is the simplest example that I can create. It seems to only happen when initializing a member variable in the constructor (b2 in this case) with a member variable (b1) of an object (Bar). Does anyone know why clang fails to detect the problem here?
I recognize that this is a contrived example, but it actually caused a problem for me and I'd like to understand it.
It is not possible to detect if you used a member variable before it is initialized in the general case. Doing so for general programs violates Rice's theorem.
Compilers do not try.
Instead, they have some simple and cheap heuristics that catch common cases.
You cannot rely on your compiler to detect every case where you use uninitialized variables.
In this particular case, you are passing b2 to another class prior to initialization, but only using it to initialize a temporary variable. That is then used to initialize the variable that was originally initialized.
If that is your simplest case, clang is doing a pretty good job. Compilers tend to be worse at this when you use a variable as part of its own initialization statement.
Here is an even simpler case:
class Foo {
public:
Foo()
: b2((bool const&)b2) // We are using b2 here before it is initialized, but clang doesn't complain
{ }
bool b2;
};
another case:
struct Bob {
bool b;
operator bool() const{ return b; }
};
class Foo {
public:
Foo()
: b2(Bob{b2}) // We are using b2 here before it is initialized, but clang doesn't complain
{ }
bool b2;
};
another
Bob bob = {Bob{bob.b}.b};
I can go on.
Clang does not claim to detect all uninitialized variable uses. So it failing to detect one is not a "bug". Rather, getting them to detect another uninitialized case is a new feature.

C++11 usage of delete specifier vs private functions

I am in the process of boning up on my C++ (as in, attempting to get into more modern-style coding) and am looking at the delete specifier. It is my understanding that it is used to make sure that certain functionality cannot be defined or called. If I understand it correctly, this is primarily within the domain of assignment and copy. I am not quite sure what the difference is between using the delete specifier and just making those functions private.
For instance, what is the difference between:
class Foo {
private:
Foo& operator(const Foo&);
Foo(const Foo&);
};
And
class Bar {
public:
Bar& operator(const Bar&) = delete;
Bar(const Bar&) = delete;
};
In other words: what does using the delete specifier gain? Is it just to make things look nicer?
One obvious difference is that if you make the function private, then it is still accessible from within the class and any friends.
An explicitly deleted function is not usable anywhere, so you know simply from that one line that it's never used, without having to inspect the implementation.
You can make the function both private and deleted: then its participation in overload resolution is more consistent.
It's simpler.
This way, your intent is clearly stated and your compiler can outright say "calling this function is prohibited".
Otherwise you're relying on:
access control (for attempted calls from outside of the class), or
the linker giving you an "undefined reference" near the end of your build process. That's kind of okay for small programs where you can quickly find out what's going on, but for deep class hierarchies where some many-times-over encapsulated object cannot be copied but has not been deleted, good luck debugging that.
Short answer is: uses of a deleted function make the program ill-formed and you are notified at compile-time, uses of a function that is not defined end in an odd error that comes out of the linker.
As an example, there is a relevant part of the standard that states:
A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.
Therefore, the following compiles just fine:
struct S {
void f();
};
template<typename T, void (T::*M)() = &T::f>
void g() {}
int main() {
g<S>();
}
While the code below does not:
struct S {
void f() = delete;
};
template<typename T, void (T::*M)() = &T::f>
void g() {}
int main() {
g<S>();
}
That's because in the second case the code is ill-formed and you have a compile-time error in any case, no matter if you are going to use or not M. In the second case, you get an error out of the linker only if you try to use it:
template<typename T, void (T::*M)() = &T::f>
void g() {
T t;
(t.*M)();
}
Of course, compile-time errors are much better to prevent issues. The example uses public functions, but making them private doesn't prevent from using them within the class in similar ways. That's just a toy example to show a possible difference.

Template class instantiation in default parameter not allowed in MSVC12?

I just extracted the following problem in our project. The following code just compiles fine with g++
#include <vector>
class A {};
typedef std::vector<A*> vec_t;
class bar {
public:
bar(vec_t) {};
};
class foo
{
public:
foo(bar* a = new bar(vec_t())) {};
};
class B
{};
int main()
{
return 0;
}
However, the Visual Studio Compiler (VC12, but I presume all others too) doesn't understand that in the default argument for the c'tor of foo the c'tor of bar is called which takes an instance of a vector as an argument. This causes an error for every class/struct declared after this expression:
error C2462: 'B' : cannot define a type in a 'new-expression'
I don't want to discuss the software design of the c'tor, but is this a compiler issue or just not allowed in standard C++ and the g++ just not being strict about that?
First, I thought that a template-instantiation in a default parameter may be not allowed or nested c'tors in a default argument. However, if I use another c'tor of the vector:
foo(bar* a = new bar(vec_t(0))) {}
it compiles with MSVC. I just can't see why the upper version shouldn't compile? Any thoughts on that?
It looks like this is an issue with the "most vexing parse" (see the Wikipedia article on it for more info). One way to disambiguate the new expression is to add parentheses around the constructor like this
foo(bar* a = new bar((vec_t()))) {};
When it comes to standards compliance I'm not sure. I skimmed section 6.8 (Ambiguity Resolution) and 5.3.4 (New) of N3690 and without thinking about it too hard nothing stood out either way. Maybe a real language lawyer will need to step in to give an answer.

c++ how to create my own warning in compilation time

I want to create my own warning in compilation time and not in pre-processor (as I've seen a few answers to)
Let's say we have:
class A
{
private:
explicit A(A const& other);
};
now if the user does:
A first;
and then:
A second(first);
he'll get an error that copy constructed is not implemented or whatever.. bare in mind that my code has a lot of inheritances in it... as well as referring me to the H file A is implemented in and not where I tried to use copy constructor...
so.. instead of the compiler's default warning I'd like to create my own....
something like.. "You cannot use copy constructor"
Help?
Thanks!
Using a static_assert with a user-define message will trigger this error message during compilation
class A
{
private:
A() {}
explicit A(A const& /* other */)
{
static_assert(false, "You cannot use copy constructor");
}
};
int main()
{
A first;
A second(first); // compile error
}
Output on LiveWorkSpace
Note this will produce an error and not a warning. However, it is almost always best to use a "warnings as errors" compiler option and to explicity (i.e. documented with a comment) disable warnings that you know are innocuous.
Without preprocessor, using only standard C++, it's unreal. You can use static_assert, but it's not warning.

Find uninitialized variables in C&C++

I really mean C and C++. This project is using a lib in C which is calling my functions in C++. However the functions are in extern "C" because the lib expects it.
Anyways, in these functions I do new Blah. When a specific function is called (end_tree) i expect all my variables to be initialized. Using Visual Studios, GCC or any other compiler is there a way i can check? I just notice some bools are TRUE which shouldn't be... why, because it wasn't initialized. Is there some kind of _VS_CheckThisMemory(mytree) function or magic I can use?
Don't know it this is what you want but gcc has -Wmaybe-uninitialized and -Wuninitialized. There may be more on the warning options page.
Use valgrind (on linux)
./valgrind myprogram
Especially easy when myprogram was compiled with debug info (gcc -g), but not required. Valgrind will notify where memory is being used that was uninitialzed, and where it was allocated from. If it has debug info, valgrind will report exactly at which file:linenumber things happened. (it can even attach a debugger on the fly for you to insepct things)
It will also detect access beyond allocation boundaries and access after freeing. This is incredibly useful.
Here endeth the useful answer
Edit because it wasn't exactly clear why I was posting the following, as became clear from the comment, let me introduce the remainder of this answer:
When starting to use valgrind with existing codebases, it is almost inevitable that you'll get 'false' positives, i.e. reports that aren't really problems (yet). I include one example of what might trigger such a report, and how you'd typically fix those.
I'm just including this to raise awareness of how to tackle or recognize (semi-)false positives.
Another way of wording it (with reference to Matthieu's convincing reasoning in the comments) is to treat even the 'not-actually-killing' Valgrind warnings as critical: get them fixed, not forgotten.
It is possible that valgrind will report uninitialized access when it is not really a problem. Like, e.g.
char buf[1024];
strcpy(buf, "hello");
char clone[1024];
memcpy(clone, buf, 1024);
You should fix that by doing something smarter like
memcpy(clone, buf, strlen(buf));
To make sure there are no uninitialzed 'parts' in buf (or at least not in the area accessed)
Use a self-initializing class to cover those annoying primitives.
template<typename T> class always_initialized {
T t;
public:
always_initialized()
: t(T()) {}
always_initialized(const T& ref) {
: t(ref) {}
operator T&() { return t; }
operator const T&() const { return t; }
T& operator=(const T& ref) { return t = ref; }
};
In response to the code linked in the comment, RAIIIA (Resource Acquisition is Initialization In Action (R))
class OtherClass;
class MyClass : public SomeBase {
public:
// note I got rid of your default constructor, which leaves values unitialized
MyClass(Var* name, OtherClass* loop)
: m_name(name), m_loop(loop) // this right here
{ }
virtual ~MyClass(); // no implementation needed here
void save();
// made the members protected, other classes have no business accessing them directly
protected:
Var* m_name;
OtherClass* m_loop;
};
Your default constructor left the values unitialized, and goes against RAII in its pure form. It's OK to do that, but as you're having problems with uninitialized variables, I would recomment removing default constructors.
EDIT: storing unknown pointers as class members without newing and deleteing them in the class constructor/destructor isn't really RAII, but I hope you do that somewhere.
I solved this by using a solution with templates to act like properties. I used Property like features in C++? but there are other examples for other things like passing in a get/setter.
Essentially generated most of this code bc i was able to and i kept track if a variable was set or not through the property. At the end i just check members and had asserts to tell me if i set a variable or not. I also assert when i 'get' just in case.