Detect use after move during compilation?
Asked Answered
R

2

2

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?

Raoul answered 30/10, 2022 at 0:39 Comment(2)
Why not just incorporate this clang-tidy check (and others) into your CI pipeline?Mucker
I actually do that already. It is just that I am writing a library that depends a lot (for correctness) on following these rules with move and I want to indicate how to enforce them with the compiler itself.Raoul
R
3

I found one way to do it, using attributes in clang. . (A GCC or a more standard solution is still welcome.)

  1. needs clang 6 or higher
  2. mark the class as "consumable"
  3. 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)]]);

Raoul answered 30/10, 2022 at 4:47 Comment(0)
P
1

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.

Pamper answered 30/10, 2022 at 1:1 Comment(1)
I understand that it is legitimate in general, but there are other things that are legitimate and the compilers can still warn about. I wonder if in this case, the problem is that it is too difficult to detect or that there is no general way to do it (I am thinking for example in terms of following all the conditional paths in which this the use after move can happen).Raoul

© 2022 - 2025 — McMap. All rights reserved.