-
Notifications
You must be signed in to change notification settings - Fork 37.7k
build: Enable -Wunreachable-code #28999
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
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code CoverageFor detailed information about the code coverage, see the test coverage report. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
Concept ACK |
@@ -1499,17 +1499,19 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas | |||
if (format == DatabaseFormat::SQLITE) { | |||
#ifdef USE_SQLITE | |||
return MakeSQLiteDatabase(path, options, status, error); | |||
#endif | |||
#else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @achow101
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't functionally change anything, it just removes dead code if there's an earlier return statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Sjors The previous code was correct as well. However, this refactoring change is required if the warning is enabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK fa8adbe
At least on my machine, this does't catch #28830 (comment) though. I tried changing the Assert
(and inline_assertion_check
) definition/implementation but can't seem to get that to work. Would be pretty helpful to fix, but even without it, this is still an improvement?
@@ -1499,17 +1499,19 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas | |||
if (format == DatabaseFormat::SQLITE) { | |||
#ifdef USE_SQLITE | |||
return MakeSQLiteDatabase(path, options, status, error); | |||
#endif | |||
#else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't functionally change anything, it just removes dead code if there's an earlier return statement.
You'll have to use |
I'm using clang, and it also caught the walletdb issue you fixed. It also catches e.g. // ...and construct a CTxMemPool from it
std::optional<bilingual_str> error{};
return CTxMemPool{mempool_opts, error};
error.reset(); // just to add unreachable code
Assert(!error);
|
I thought I tested this, but it looks like you are right. Any code from macros won't trigger the warning. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK fa8adbe tested with arm64 clang 17.0.6
Could alternatively use -Wunreachable-code-aggressive -- it catches a few additional unreachable break
statements in the current code.
They seem harmless, so if the author wants them and reviewers are fine with them, it seems fine? |
Similar rationale as in your OP -- it seems confusing to have unreachable code. Happy re-review if you agree otherwise NBD. |
Concept ACK |
Ok, but that'd require touching leveldb, which is a subtree, so I can't touch it here. |
|
I did some earlier testing, and I think generally any code injected from macros will be ignored by this warning. Adding a no-op verbatim should warn, IIRC. |
I did some testing too. Adding a git diff on fa8adbediff --git a/src/test/fuzz/package_eval.cpp b/src/test/fuzz/package_eval.cpp
index 5a08d0ff44..2369c2ee0d 100644
--- a/src/test/fuzz/package_eval.cpp
+++ b/src/test/fuzz/package_eval.cpp
@@ -126,6 +126,7 @@ CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeConte
// ...and construct a CTxMemPool from it
return CTxMemPool{mempool_opts};
+ std::cout << "Hello\n";
}
FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
Replacing it with a macro then compiles fine: git diff on fa8adbediff --git a/src/test/fuzz/package_eval.cpp b/src/test/fuzz/package_eval.cpp
index 5a08d0ff44..1e616272d9 100644
--- a/src/test/fuzz/package_eval.cpp
+++ b/src/test/fuzz/package_eval.cpp
@@ -106,6 +106,8 @@ void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chains
SetMockTime(time);
}
+#define Print(val) std::cout << val;
+
CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)
{
// Take the default options for tests...
@@ -126,6 +128,7 @@ CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeConte
// ...and construct a CTxMemPool from it
return CTxMemPool{mempool_opts};
+ Print("Hello\n");
}
FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
Wrapping the macro in a function then again results in a warning: git diff on fa8adbediff --git a/src/test/fuzz/package_eval.cpp b/src/test/fuzz/package_eval.cpp
index 5a08d0ff44..ec8b8fd4fb 100644
--- a/src/test/fuzz/package_eval.cpp
+++ b/src/test/fuzz/package_eval.cpp
@@ -106,6 +106,9 @@ void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chains
SetMockTime(time);
}
+#define Print(val) std::cout << val;
+void PrintFn(const std::string& val) { Print(val); }
+
CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)
{
// Take the default options for tests...
@@ -126,6 +129,7 @@ CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeConte
// ...and construct a CTxMemPool from it
return CTxMemPool{mempool_opts};
+ PrintFn("Hello\n");
}
FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
git diff on fa8adbediff --git a/src/test/fuzz/package_eval.cpp b/src/test/fuzz/package_eval.cpp
index 5a08d0ff44..58d7b6feb2 100644
--- a/src/test/fuzz/package_eval.cpp
+++ b/src/test/fuzz/package_eval.cpp
@@ -126,6 +126,7 @@ CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeConte
// ...and construct a CTxMemPool from it
return CTxMemPool{mempool_opts};
+ Assert(false);
}
FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
diff --git a/src/util/check.h b/src/util/check.h
index a02a1de8dc..4d37edeca4 100644
--- a/src/util/check.h
+++ b/src/util/check.h
@@ -73,8 +73,13 @@ T&& inline_assertion_check(LIFETIMEBOUND T&& val, [[maybe_unused]] const char* f
#define CHECK_NONFATAL(condition) \
inline_check_non_fatal(condition, __FILE__, __LINE__, __func__, #condition)
+#define AssertImpl(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val)
+
/** Identity function. Abort if the value compares equal to zero */
-#define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val)
+template <typename T>
+T&& Assert(T&& val) { return AssertImpl(std::forward<T>(val)); }
+
+#define AssumeImpl(val) inline_assertion_check<false>(val, __FILE__, __LINE__, __func__, #val)
/**
* Assume is the identity function.
@@ -86,7 +91,8 @@ T&& inline_assertion_check(LIFETIMEBOUND T&& val, [[maybe_unused]] const char* f
* - For non-fatal errors in interactive sessions (e.g. RPC or command line
* interfaces), CHECK_NONFATAL() might be more appropriate.
*/
-#define Assume(val) inline_assertion_check<false>(val, __FILE__, __LINE__, __func__, #val)
+template <typename T>
+T&& Assume(T&& val) { return AssumeImpl(std::forward<T>(val)); }
/**
* NONFATAL_UNREACHABLE() is a macro that is used to mark unreachable code. It throws a NonFatalCheckError.
|
I don't think this works, because it will replace |
C++20 fixed that, see https://en.cppreference.com/w/cpp/utility/source_location |
Hopefully we'll be able to use this at some point, however Apple Clang does not support this at all yet, and it looks like it will also come with a LLVM 15, if not 16+ requirement as well. |
Jup, it is GCC libstdc++11, or Clang libc++16, or later |
} | ||
|
||
#ifdef USE_BDB | ||
return MakeBerkeleyDatabase(path, options, status, error); | ||
#endif | ||
#else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "build: Enable -Wunreachable-code" (fa8adbe)
Not a big deal, but IMO, this change is not an improvement. The unreachable code warning seems helpful to catch cases where code is unintentionally unreachable. Or to encourage rewriting code where code is intentionally reachable but written in a hard to understand way.
But this code was intentionally unreachable in obvious way with two return statements right next to each other. It was written this way so changes to code could be checked at compile time without developers needing rebuild with --without-bdb and --with-sqlite=yes options. So this seems like a case where the compiler warning is unhelpful, and it would be nice if there was a way to disable it.
Also, I might have missed it but it doesn't seem like we have CI builds that test building with wallet enabled and sqlite or BDB disabled. So now maybe bugs in this code could be unnoticed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was written this way so changes to code could be checked at compile time without developers needing rebuild with --without-bdb and --with-sqlite=yes options. So this seems like a case where the compiler warning is unhelpful, and it would be nice if there was a way to disable it.
I think it can be disabled via the verbose pragma clang diagnostic ignored
, but that is ugly and verbose. A better workaround could be to use if constexpr(...USE_BDB...) { return MakeBDB();} else { return error; }
? This should ensure the error
code is compile-able, even though it is never run.
Also, I might have missed it but it doesn't seem like we have CI builds that test building with wallet enabled and sqlite or BDB disabled. So now maybe bugs in this code could be unnoticed?
Seems unrelated to the changes here, because if a piece of code compiles does not mean it has no bugs. If a test is truly missing, I have no objection to adding one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if a piece of code compiles does not mean it has no bugs
I've noticed that sometimes. And it would be unreasonable to expect our CI to run every line of code, but I think it's reasonable to expect our CI to at least compile every line of code and not leave blind spots like this. So I really like your constexpr idea, and can try it out in #25722 after I finish rebasing it. Thanks for suggesting that.
When unreachable code isn't compiled, compile failures are not detected. Fix this by leaving it unreachable, but compiling it. Fixes bitcoin#28999 (comment) Can be reviewed with --ignore-all-space --patience
When unreachable code isn't compiled, compile failures are not detected. Fix this by leaving it unreachable, but compiling it. Fixes bitcoin#28999 (comment) Can be reviewed with --ignore-all-space
When unreachable code isn't compiled, compile failures are not detected. Fix this by leaving it unreachable, but compiling it. Fixes bitcoin#28999 (comment) Can be reviewed with --ignore-all-space
fa3373d refactor: Compile unreachable code (MarcoFalke) Pull request description: When unreachable code isn't compiled, compile failures are not detected. Fix this by leaving it unreachable, but compiling it. Fixes #28999 (comment) ACKs for top commit: achow101: ACK fa3373d ryanofsky: Code review ACK fa3373d. This looks good, and should prevent code in the else blocks from accidentally breaking. Tree-SHA512: 3a3764915dfc935bf5d7a48f1ca151dcbac340c1cbdce8236b24ae9b4f04d6ee9771ed058ca60bcbca6e19d13671de3517f828a8f7ab6444c7cc4e3538d1ba4e
When unreachable code isn't compiled, compile failures are not detected. Fix this by leaving it unreachable, but compiling it. Fixes bitcoin/bitcoin#28999 (comment) Can be reviewed with --ignore-all-space
fa3373d refactor: Compile unreachable code (MarcoFalke) Pull request description: When unreachable code isn't compiled, compile failures are not detected. Fix this by leaving it unreachable, but compiling it. Fixes bitcoin#28999 (comment) ACKs for top commit: achow101: ACK fa3373d ryanofsky: Code review ACK fa3373d. This looks good, and should prevent code in the else blocks from accidentally breaking. Tree-SHA512: 3a3764915dfc935bf5d7a48f1ca151dcbac340c1cbdce8236b24ae9b4f04d6ee9771ed058ca60bcbca6e19d13671de3517f828a8f7ab6444c7cc4e3538d1ba4e
fa3373d refactor: Compile unreachable code (MarcoFalke) Pull request description: When unreachable code isn't compiled, compile failures are not detected. Fix this by leaving it unreachable, but compiling it. Fixes bitcoin#28999 (comment) ACKs for top commit: achow101: ACK fa3373d ryanofsky: Code review ACK fa3373d. This looks good, and should prevent code in the else blocks from accidentally breaking. Tree-SHA512: 3a3764915dfc935bf5d7a48f1ca151dcbac340c1cbdce8236b24ae9b4f04d6ee9771ed058ca60bcbca6e19d13671de3517f828a8f7ab6444c7cc4e3538d1ba4e
fa3373d refactor: Compile unreachable code (MarcoFalke) Pull request description: When unreachable code isn't compiled, compile failures are not detected. Fix this by leaving it unreachable, but compiling it. Fixes bitcoin#28999 (comment) ACKs for top commit: achow101: ACK fa3373d ryanofsky: Code review ACK fa3373d. This looks good, and should prevent code in the else blocks from accidentally breaking. Tree-SHA512: 3a3764915dfc935bf5d7a48f1ca151dcbac340c1cbdce8236b24ae9b4f04d6ee9771ed058ca60bcbca6e19d13671de3517f828a8f7ab6444c7cc4e3538d1ba4e
fa3373d refactor: Compile unreachable code (MarcoFalke) Pull request description: When unreachable code isn't compiled, compile failures are not detected. Fix this by leaving it unreachable, but compiling it. Fixes bitcoin#28999 (comment) ACKs for top commit: achow101: ACK fa3373d ryanofsky: Code review ACK fa3373d. This looks good, and should prevent code in the else blocks from accidentally breaking. Tree-SHA512: 3a3764915dfc935bf5d7a48f1ca151dcbac340c1cbdce8236b24ae9b4f04d6ee9771ed058ca60bcbca6e19d13671de3517f828a8f7ab6444c7cc4e3538d1ba4e
fa3373d refactor: Compile unreachable code (MarcoFalke) Pull request description: When unreachable code isn't compiled, compile failures are not detected. Fix this by leaving it unreachable, but compiling it. Fixes bitcoin#28999 (comment) ACKs for top commit: achow101: ACK fa3373d ryanofsky: Code review ACK fa3373d. This looks good, and should prevent code in the else blocks from accidentally breaking. Tree-SHA512: 3a3764915dfc935bf5d7a48f1ca151dcbac340c1cbdce8236b24ae9b4f04d6ee9771ed058ca60bcbca6e19d13671de3517f828a8f7ab6444c7cc4e3538d1ba4e
It seems a bit confusing to write code after a
return
. This can even lead to bugs, or incorrect code, such as https://github.com/bitcoin/bitcoin/pull/28830/files#r1415372320 . (Edit: The linked instance is not found by clang's-Wunreachable-code
).Fix all issues by enabling
-Wunreachable-code
.This flag also enables
-Wunreachable-code-loop-increment
, according to https://clang.llvm.org/docs/DiagnosticsReference.html#wunreachable-code, so remove that.