Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<mutex>: lock_guard can emit nodiscard warnings for valid code #1742

Closed
StephanTLavavej opened this issue Mar 16, 2021 · 4 comments · Fixed by #2211
Closed

<mutex>: lock_guard can emit nodiscard warnings for valid code #1742

StephanTLavavej opened this issue Mar 16, 2021 · 4 comments · Fixed by #2211
Labels
bug Something isn't working fixed Something works now, yay!

Comments

@StephanTLavavej
Copy link
Member

Reported as DevCom-1368582, caused by #1495 (unrelated to #1514):

#include<mutex>

struct Mutexed {
  mutable std::mutex m;
  int i;
};

struct MyClass {
  int i;
  explicit MyClass(Mutexed const& m) :
    i{ (std::lock_guard{ m.m }, m.i) } // warning C4834: discarding return value of function with 'nodiscard' attribute
  {}
};

This code is valid and reasonable - it's not doing anything terribly weird. There are other ways to write the code (including ways that don't involve assigning in the constructor body - e.g. a helper function could lock the mutex and read the int), but there's nothing inherently wrong with this way.

Our usual policy for [[nodiscard]] is to strongly prefer avoiding false positives - to the point where if 10% of uses are valid and 90% are bogus, we would prefer to not warn. (unique_ptr::release() is a good example where we don't warn.)

We need to decide what to do in this case. My feeling is that while this use of the comma operator is valid, it doesn't seem common at the 10% level (my general impression is that usage of the comma operator is quite rare, as most programmers are unfamiliar with the rule that temporaries live until the end of the full-expression). I also feel that it is extremely hazardous to mistakenly discard a lock_guard, because the code will appear to work most of the time. (Whereas a discarded unique_ptr::release() is a memory leak, which can be noticed later.) As we don't have a way to tell the compiler to treat LHS comma subexpressions differently from expression-statements, I think we should resolve this as by design.

Fortunately, there is a workaround that allows this code to continue to compile without the need for push-disable-pop. Casting the LHS to (void) is sufficient to avoid the warning: https://godbolt.org/z/b5f6vz

@StephanTLavavej StephanTLavavej added bug Something isn't working decision needed We need to choose something before working on this labels Mar 16, 2021
@AdamBucior
Copy link
Contributor

If casting to void is sufficient to avoid the warning, maybe we should just document it in a message. Ex. "discarding lock guard, if it is intentional cast it to void to suppress this warning"

@CaseyCarter CaseyCarter removed the decision needed We need to choose something before working on this label Mar 18, 2021
@CaseyCarter
Copy link
Member

[[nodisard("with a reason")]] is a C++20 feature, so we need to verify C++14/17 support in our compiler frontends before going with that approach (which everyone does seem to like). It's also pertinent that some but not all of these occurrences are [[nodiscard]] on a constructor which is a C++20 change as well.

@AlexGuteniev
Copy link
Contributor

Compilers happen so support [[nodisard("with a reason")]] in C++14 mode: https://godbolt.org/z/e88xWYosh

@AlexGuteniev
Copy link
Contributor

As we are anyway wrapping [[nodiscard]] with a macro, we can add suppression; from current #2211

#ifdef _NODISCARD_LOCK_SUPPRESS

#define _NODISCARD_LOCK
#define _NODISCARD_CTOR_LOCK

#else // ^^^ defined(_NODISCARD_LOCK_SUPPRESS) ^^^ / vvv !defined(_NODISCARD_LOCK_SUPPRESS) vvv

#define _NODISCARD_LOCK                                                                                              \
    _NODISCARD_MSG(                                                                                                  \
        "A lock should be saved in a variable to protect the scope. (If the intetion is to protect the rest of the " \
        "current statement, using comma operator, please use cast to void to suppress this warning. "                \
        "Alternatively, define _NODISCARD_LOCK_SUPPRESS.)")

#define _NODISCARD_CTOR_LOCK                                                                                         \
    _NODISCARD_CTOR_MSG(                                                                                             \
        "A lock should be saved in a variable to protect the scope. (If the intetion is to protect the rest of the " \
        "current statement, using comma operator, please use cast to void to suppress this warning)"                 \
        "Alternatively, define _NODISCARD_LOCK_SUPPRESS.)")

#endif // ^^^ !defined(_NODISCARD_LOCK_SUPPRESS) ^^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixed Something works now, yay!
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants