From 05c35c402cc56e7082a48cc073434a40b0bebd8f Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Thu, 23 Jul 2026 14:38:40 +0100 Subject: [PATCH] refactor: Make all `const static` class members `constexpr` If a `static class` member is not inlined or `constexpr`, the linker will fail when attempting to ODR-use the constant (passing as `const T&`). These can be fixed by finding all member variables that are `const` qualified and inlining them with `constexpr`. There is a clang-tidy pull request that would lint these callsites: https://github.com/llvm/llvm-project/pull/162741 A script was used to modify these sites, however it cannot run as a scripted-diff because it uses clang-query and a build folder. The script only queries for integer and enumeration types, as other data members would have to be marked `constexpr` or `inline` from what I understand: https://en.cppreference.com/cpp/language/static#Constant_static_members Removing the ZMQ forward declaration was a clang-tidy lint. The script used to find these sites, LLM assisted: ``` set -uxo pipefail cd "$(git rev-parse --show-toplevel)" BUILD=${BUILD:-build} if [ ! -f "${BUILD}/compile_commands.json" ]; then echo "error: ${BUILD}/compile_commands.json not found. Run cmake -B ${BUILD} first." >&2 exit 1 fi if ! command -v clang-query >/dev/null; then echo "error: clang-query not on PATH. Install clang-tools." >&2 exit 1 fi if ! git diff --quiet || ! git diff --cached --quiet; then echo "error: working tree has uncommitted changes. Commit or stash first." >&2 exit 1 fi MATCHER='match varDecl(hasParent(cxxRecordDecl()), hasType(qualType(isConstQualified(), anyOf(hasCanonicalType(isInteger()), hasDeclaration(enumDecl())))), hasInitializer(expr()), unless(isConstexpr()), isExpansionInFileMatching("/src/"))' RAW=$(mktemp) trap 'rm -f "$RAW"' EXIT echo "Sweeping TUs (batched, may take a few minutes)..." >&2 find src -type d \( -name secp256k1 -o -name leveldb -o -name crc32c \ -o -name minisketch -o -name libmultiprocess -o -name ctaes \) -prune -o \ -name '*.cpp' -print0 \ | xargs -0 -n 50 clang-query -p "${BUILD}" \ -c 'set output diag' \ -c "${MATCHER}" \ >>"$RAW" || true ROOT=$(pwd) LOCS=$(grep -oE "${ROOT}/src/[^:]+:[0-9]+:[0-9]+:" "$RAW" \ | sed -E "s|^${ROOT}/||; s|:[0-9]+:$||" \ | sort -u) if [ -z "$LOCS" ]; then echo "no matches" >&2 exit 0 fi FILTERED="" while IFS=: read -r file line; do case "$file" in src/secp256k1/*|src/leveldb/*|src/crc32c/*|src/minisketch/*|src/ipc/libmultiprocess/*|src/crypto/ctaes/*) continue ;; src/tinyformat.h) continue ;; esac src=$(sed -n "${line}p" "$file") case "$src" in *inline*) continue ;; esac FILTERED+="${file}:${line}"$'\n' done <<<"$LOCS" FILTERED=$(printf '%s' "$FILTERED" | sed '/^$/d') if [ -z "$FILTERED" ]; then echo "no matches after filtering" >&2 exit 0 fi echo "Sites to rewrite ($(echo "$FILTERED" | wc -l)):" >&2 echo "$FILTERED" >&2 declare -A LINES while IFS=: read -r file line; do LINES[$file]+="${line} " done <<<"$FILTERED" for file in "${!LINES[@]}"; do args=() for line in ${LINES[$file]}; do args+=(-e "${line}s/static const /static constexpr /") done sed -i "${args[@]}" "$file" done echo >&2 echo "===== proposed diff =====" >&2 git --no-pager diff ``` --- src/compressor.h | 2 +- src/crypto/hkdf_sha256_32.h | 2 +- src/crypto/hmac_sha256.h | 2 +- src/crypto/hmac_sha512.h | 2 +- src/crypto/ripemd160.h | 2 +- src/crypto/sha1.h | 2 +- src/crypto/sha256.h | 2 +- src/hash.h | 4 ++-- src/key.h | 4 ++-- src/node/utxo_snapshot.h | 2 +- src/policy/fees/block_policy_estimator.h | 2 +- src/primitives/transaction.h | 14 +++++++------- src/qt/recentrequeststablemodel.h | 2 +- src/qt/sendcoinsrecipient.h | 2 +- src/qt/transactionfilterproxy.h | 2 +- src/qt/transactionrecord.h | 2 +- src/script/script.h | 2 +- src/support/lockedpool.h | 4 ++-- src/test/scriptnum10.h | 2 +- src/tinyformat.h | 2 +- src/txmempool.h | 2 +- src/wallet/walletdb.h | 14 +++++++------- src/zmq/zmqabstractnotifier.cpp | 2 -- src/zmq/zmqabstractnotifier.h | 2 +- 24 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/compressor.h b/src/compressor.h index 95490b7bd23..b6b738f6548 100644 --- a/src/compressor.h +++ b/src/compressor.h @@ -59,7 +59,7 @@ struct ScriptCompression * transactions, in which case this value becomes dependent on version * and nHeight of the enclosing transaction. */ - static const unsigned int nSpecialScripts = 6; + static constexpr unsigned int nSpecialScripts{6}; template void Ser(Stream &s, const CScript& script) { diff --git a/src/crypto/hkdf_sha256_32.h b/src/crypto/hkdf_sha256_32.h index 7c5d5a7f859..ec705042787 100644 --- a/src/crypto/hkdf_sha256_32.h +++ b/src/crypto/hkdf_sha256_32.h @@ -13,7 +13,7 @@ class CHKDF_HMAC_SHA256_L32 { private: unsigned char m_prk[32]; - static const size_t OUTPUT_SIZE = 32; + static constexpr size_t OUTPUT_SIZE{32}; public: CHKDF_HMAC_SHA256_L32(const unsigned char* ikm, size_t ikmlen, const std::string& salt); diff --git a/src/crypto/hmac_sha256.h b/src/crypto/hmac_sha256.h index a26947d5e0a..0bf424c9316 100644 --- a/src/crypto/hmac_sha256.h +++ b/src/crypto/hmac_sha256.h @@ -17,7 +17,7 @@ private: CSHA256 inner; public: - static const size_t OUTPUT_SIZE = 32; + static constexpr size_t OUTPUT_SIZE{32}; CHMAC_SHA256(const unsigned char* key, size_t keylen); CHMAC_SHA256& Write(const unsigned char* data, size_t len) diff --git a/src/crypto/hmac_sha512.h b/src/crypto/hmac_sha512.h index dfae8d05e5a..adcfb681acc 100644 --- a/src/crypto/hmac_sha512.h +++ b/src/crypto/hmac_sha512.h @@ -17,7 +17,7 @@ private: CSHA512 inner; public: - static const size_t OUTPUT_SIZE = 64; + static constexpr size_t OUTPUT_SIZE{64}; CHMAC_SHA512(const unsigned char* key, size_t keylen); CHMAC_SHA512& Write(const unsigned char* data, size_t len) diff --git a/src/crypto/ripemd160.h b/src/crypto/ripemd160.h index a06a3255e21..45afc419f36 100644 --- a/src/crypto/ripemd160.h +++ b/src/crypto/ripemd160.h @@ -17,7 +17,7 @@ private: uint64_t bytes{0}; public: - static const size_t OUTPUT_SIZE = 20; + static constexpr size_t OUTPUT_SIZE{20}; CRIPEMD160(); CRIPEMD160& Write(const unsigned char* data, size_t len); diff --git a/src/crypto/sha1.h b/src/crypto/sha1.h index fcb96ee6a72..1327ccfab58 100644 --- a/src/crypto/sha1.h +++ b/src/crypto/sha1.h @@ -17,7 +17,7 @@ private: uint64_t bytes{0}; public: - static const size_t OUTPUT_SIZE = 20; + static constexpr size_t OUTPUT_SIZE = 20; CSHA1(); CSHA1& Write(const unsigned char* data, size_t len); diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h index 3ac771c5d0d..de47991e5b8 100644 --- a/src/crypto/sha256.h +++ b/src/crypto/sha256.h @@ -18,7 +18,7 @@ private: uint64_t bytes{0}; public: - static const size_t OUTPUT_SIZE = 32; + static constexpr size_t OUTPUT_SIZE{32}; CSHA256(); CSHA256& Write(const unsigned char* data, size_t len); diff --git a/src/hash.h b/src/hash.h index b671761fbb6..eabcf18bc89 100644 --- a/src/hash.h +++ b/src/hash.h @@ -33,7 +33,7 @@ class CHash256 { private: CSHA256 sha; public: - static const size_t OUTPUT_SIZE = CSHA256::OUTPUT_SIZE; + static constexpr size_t OUTPUT_SIZE{CSHA256::OUTPUT_SIZE}; void Finalize(std::span output) { assert(output.size() == OUTPUT_SIZE); @@ -58,7 +58,7 @@ class CHash160 { private: CSHA256 sha; public: - static const size_t OUTPUT_SIZE = CRIPEMD160::OUTPUT_SIZE; + static constexpr size_t OUTPUT_SIZE{CRIPEMD160::OUTPUT_SIZE}; void Finalize(std::span output) { assert(output.size() == OUTPUT_SIZE); diff --git a/src/key.h b/src/key.h index cd77dcd0ee2..ae95abea795 100644 --- a/src/key.h +++ b/src/key.h @@ -39,8 +39,8 @@ public: /** * secp256k1: */ - static const unsigned int SIZE = 279; - static const unsigned int COMPRESSED_SIZE = 214; + static constexpr unsigned int SIZE{279}; + static constexpr unsigned int COMPRESSED_SIZE{214}; /** * see www.keylength.com * script supports up to 75 for single byte push diff --git a/src/node/utxo_snapshot.h b/src/node/utxo_snapshot.h index d8b3ca616c8..482edd3e6fc 100644 --- a/src/node/utxo_snapshot.h +++ b/src/node/utxo_snapshot.h @@ -36,7 +36,7 @@ namespace node { //! before being used. Thus, new fields should be added only if needed. class SnapshotMetadata { - inline static const uint16_t VERSION{2}; + static constexpr uint16_t VERSION{2}; const std::set m_supported_versions{VERSION}; const MessageStartChars m_network_magic; public: diff --git a/src/policy/fees/block_policy_estimator.h b/src/policy/fees/block_policy_estimator.h index f015bd5b683..d513f15a858 100644 --- a/src/policy/fees/block_policy_estimator.h +++ b/src/policy/fees/block_policy_estimator.h @@ -157,7 +157,7 @@ private: static constexpr unsigned int LONG_BLOCK_PERIODS = 42; static constexpr unsigned int LONG_SCALE = 24; /** Historical estimates that are older than this aren't valid */ - static const unsigned int OLDEST_ESTIMATE_HISTORY = 6 * 1008; + static constexpr unsigned int OLDEST_ESTIMATE_HISTORY{6 * 1008}; /** Decay of .962 is a half-life of 18 blocks or about 3 hours */ static constexpr double SHORT_DECAY = .962; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 3a7735e149b..17fea5b46b5 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -73,13 +73,13 @@ public: * it set (BIP 65). * It has SEQUENCE_LOCKTIME_DISABLE_FLAG set (BIP 68/112). */ - static const uint32_t SEQUENCE_FINAL = 0xffffffff; + static constexpr uint32_t SEQUENCE_FINAL{0xffffffff}; /** * This is the maximum sequence number that enables both nLockTime and * OP_CHECKLOCKTIMEVERIFY (BIP 65). * It has SEQUENCE_LOCKTIME_DISABLE_FLAG set (BIP 68/112). */ - static const uint32_t MAX_SEQUENCE_NONFINAL{SEQUENCE_FINAL - 1}; + static constexpr uint32_t MAX_SEQUENCE_NONFINAL{SEQUENCE_FINAL - 1}; // Below flags apply in the context of BIP 68. BIP 68 requires the tx // version to be set to 2, or higher. @@ -90,18 +90,18 @@ public: * It fails OP_CHECKSEQUENCEVERIFY/CheckSequence() for any input that has * it set (BIP 112). */ - static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1U << 31); + static constexpr uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG{1U << 31}; /** * If CTxIn::nSequence encodes a relative lock-time and this flag * is set, the relative lock-time has units of 512 seconds, * otherwise it specifies blocks with a granularity of 1. */ - static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22); + static constexpr uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG{1 << 22}; /** * If CTxIn::nSequence encodes a relative lock-time, this mask is * applied to extract that lock-time from the sequence field. */ - static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff; + static constexpr uint32_t SEQUENCE_LOCKTIME_MASK{0x0000ffff}; /** * In order to use the same number of bits to encode roughly the @@ -111,7 +111,7 @@ public: * Converting from CTxIn::nSequence to seconds is performed by * multiplying by 512 = 2^9, or equivalently shifting up by * 9 bits. */ - static const int SEQUENCE_LOCKTIME_GRANULARITY = 9; + static constexpr int SEQUENCE_LOCKTIME_GRANULARITY{9}; CTxIn() { @@ -281,7 +281,7 @@ class CTransaction { public: // Default transaction version. - static const uint32_t CURRENT_VERSION{2}; + static constexpr uint32_t CURRENT_VERSION{2}; // The local variables are made const to prevent unintended modification // without updating the cached hash value. However, CTransaction is not diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h index 31602b81dfe..c52423be473 100644 --- a/src/qt/recentrequeststablemodel.h +++ b/src/qt/recentrequeststablemodel.h @@ -20,7 +20,7 @@ class RecentRequestEntry public: RecentRequestEntry() = default; - static const int CURRENT_VERSION = 1; + static constexpr int CURRENT_VERSION{1}; int nVersion{RecentRequestEntry::CURRENT_VERSION}; int64_t id{0}; QDateTime date; diff --git a/src/qt/sendcoinsrecipient.h b/src/qt/sendcoinsrecipient.h index 496bf689aac..9cc8fab8573 100644 --- a/src/qt/sendcoinsrecipient.h +++ b/src/qt/sendcoinsrecipient.h @@ -37,7 +37,7 @@ public: bool fSubtractFeeFromAmount; // memory only - static const int CURRENT_VERSION = 1; + static constexpr int CURRENT_VERSION{1}; int nVersion; SERIALIZE_METHODS(SendCoinsRecipient, obj) diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h index f8724a9cb3b..ee0dcf98811 100644 --- a/src/qt/transactionfilterproxy.h +++ b/src/qt/transactionfilterproxy.h @@ -21,7 +21,7 @@ public: explicit TransactionFilterProxy(QObject *parent = nullptr); /** Type filter bit field (all types) */ - static const quint32 ALL_TYPES = 0xFFFFFFFF; + static constexpr quint32 ALL_TYPES{0xFFFFFFFF}; static quint32 TYPE(int type) { return 1<& vch, bool fRequireMinimal, const size_t nMaxNumSize = nDefaultMaxNumSize) diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h index c4966bbbbea..dd3c9d4136b 100644 --- a/src/support/lockedpool.h +++ b/src/support/lockedpool.h @@ -131,11 +131,11 @@ public: * allocation and deallocation overhead. Setting it too high allocates * more locked memory from the OS than strictly necessary. */ - static const size_t ARENA_SIZE = 256*1024; + static constexpr size_t ARENA_SIZE{256*1024}; /** Chunk alignment. Another compromise. Setting this too high will waste * memory, setting it too low will facilitate fragmentation. */ - static const size_t ARENA_ALIGN = 16; + static constexpr size_t ARENA_ALIGN{16}; /** Callback when allocation succeeds but locking fails. */ diff --git a/src/test/scriptnum10.h b/src/test/scriptnum10.h index 402606714d4..a49693b3ebf 100644 --- a/src/test/scriptnum10.h +++ b/src/test/scriptnum10.h @@ -31,7 +31,7 @@ public: m_value = n; } - static const size_t nDefaultMaxNumSize = 4; + static constexpr size_t nDefaultMaxNumSize{4}; explicit CScriptNum10(const std::vector& vch, bool fRequireMinimal, const size_t nMaxNumSize = nDefaultMaxNumSize) diff --git a/src/tinyformat.h b/src/tinyformat.h index 29b0f9e3ea3..444cd6ded54 100644 --- a/src/tinyformat.h +++ b/src/tinyformat.h @@ -232,7 +232,7 @@ struct is_convertible // the overload set only if the version taking a T2 doesn't match. // Then we compare the sizes of the return types to check which // function matched. Very neat, in a disgusting kind of way :) - static const bool value = + static constexpr bool value = sizeof(tryConvert(makeT1())) == sizeof(succeed); # ifdef _MSC_VER # pragma warning(pop) diff --git a/src/txmempool.h b/src/txmempool.h index 1a5405d5bf0..48082cbe82f 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -209,7 +209,7 @@ protected: public: - static const int ROLLING_FEE_HALFLIFE = 60 * 60 * 12; // public only for testing + static constexpr int ROLLING_FEE_HALFLIFE{60 * 60 * 12}; // public only for testing using indexed_transaction_set = boost::multi_index_container< CTxMemPoolEntry, diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 8397fff9cc4..76375cd9b79 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -100,9 +100,9 @@ public: int64_t m_next_external_index{0}; // Next index in the keypool to be used. Memory only. int64_t m_next_internal_index{0}; // Next index in the keypool to be used. Memory only. - static const int VERSION_HD_BASE = 1; - static const int VERSION_HD_CHAIN_SPLIT = 2; - static const int CURRENT_VERSION = VERSION_HD_CHAIN_SPLIT; + static constexpr int VERSION_HD_BASE{1}; + static constexpr int VERSION_HD_CHAIN_SPLIT{2}; + static constexpr int CURRENT_VERSION{VERSION_HD_CHAIN_SPLIT}; int nVersion; CHDChain() { SetNull(); } @@ -136,10 +136,10 @@ public: class CKeyMetadata { public: - static const int VERSION_BASIC=1; - static const int VERSION_WITH_HDDATA=10; - static const int VERSION_WITH_KEY_ORIGIN = 12; - static const int CURRENT_VERSION=VERSION_WITH_KEY_ORIGIN; + static constexpr int VERSION_BASIC{1}; + static constexpr int VERSION_WITH_HDDATA{10}; + static constexpr int VERSION_WITH_KEY_ORIGIN{12}; + static constexpr int CURRENT_VERSION{VERSION_WITH_KEY_ORIGIN}; int nVersion; int64_t nCreateTime; // 0 means unknown std::string hdKeypath; //optional HD/bip32 keypath. Still used to determine whether a key is a seed. Also kept for backwards compatibility diff --git a/src/zmq/zmqabstractnotifier.cpp b/src/zmq/zmqabstractnotifier.cpp index 081a2a2c74d..77bb8a7a4be 100644 --- a/src/zmq/zmqabstractnotifier.cpp +++ b/src/zmq/zmqabstractnotifier.cpp @@ -6,8 +6,6 @@ #include -const int CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM; - CZMQAbstractNotifier::~CZMQAbstractNotifier() { assert(!psocket); diff --git a/src/zmq/zmqabstractnotifier.h b/src/zmq/zmqabstractnotifier.h index 77d478a10f7..686a46a1a83 100644 --- a/src/zmq/zmqabstractnotifier.h +++ b/src/zmq/zmqabstractnotifier.h @@ -19,7 +19,7 @@ using CZMQNotifierFactory = std::function( class CZMQAbstractNotifier { public: - static const int DEFAULT_ZMQ_SNDHWM {1000}; + static constexpr int DEFAULT_ZMQ_SNDHWM {1000}; virtual ~CZMQAbstractNotifier();