0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-03 09:56:38 -05:00

miniscript: make operator_mst consteval

It seems modern compilers don't realize that all invocations of operator""_mst
can be evaluated at compile time, despite the constexpr keyword.

Since C++20, we can force them to evaluate at compile time, turning all the
miniscript type constants into actual compile-time constants.

It appears that MSVC does not support consteval operator"" when used inside
certain expressions. For the few places where this happens, define a
constant outside the operator call.

Co-Authored-By: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com>
This commit is contained in:
Pieter Wuille 2023-10-16 10:17:12 -04:00
parent 70e4d6ff1d
commit 63317103c9
3 changed files with 38 additions and 31 deletions

View file

@ -231,7 +231,8 @@ Type ComputeType(Fragment fragment, Type x, Type y, Type z, const std::vector<Ty
Type acc_tl = "k"_mst; Type acc_tl = "k"_mst;
for (size_t i = 0; i < sub_types.size(); ++i) { for (size_t i = 0; i < sub_types.size(); ++i) {
Type t = sub_types[i]; Type t = sub_types[i];
if (!(t << (i ? "Wdu"_mst : "Bdu"_mst))) return ""_mst; // Require Bdu, Wdu, Wdu, ... static constexpr auto WDU{"Wdu"_mst}, BDU{"Bdu"_mst};
if (!(t << (i ? WDU : BDU))) return ""_mst; // Require Bdu, Wdu, Wdu, ...
if (!(t << "e"_mst)) all_e = false; if (!(t << "e"_mst)) all_e = false;
if (!(t << "m"_mst)) all_m = false; if (!(t << "m"_mst)) all_m = false;
if (t << "s"_mst) num_s += 1; if (t << "s"_mst) num_s += 1;

View file

@ -123,12 +123,12 @@ class Type {
//! Internal bitmap of properties (see ""_mst operator for details). //! Internal bitmap of properties (see ""_mst operator for details).
uint32_t m_flags; uint32_t m_flags;
//! Internal constructor used by the ""_mst operator. //! Internal constructor.
explicit constexpr Type(uint32_t flags) : m_flags(flags) {} explicit constexpr Type(uint32_t flags) noexcept : m_flags(flags) {}
public: public:
//! The only way to publicly construct a Type is using this literal operator. //! Construction function used by the ""_mst operator.
friend constexpr Type operator"" _mst(const char* c, size_t l); static consteval Type Make(uint32_t flags) noexcept { return Type(flags); }
//! Compute the type with the union of properties. //! Compute the type with the union of properties.
constexpr Type operator|(Type x) const { return Type(m_flags | x.m_flags); } constexpr Type operator|(Type x) const { return Type(m_flags | x.m_flags); }
@ -150,11 +150,11 @@ public:
}; };
//! Literal operator to construct Type objects. //! Literal operator to construct Type objects.
inline constexpr Type operator"" _mst(const char* c, size_t l) { inline consteval Type operator"" _mst(const char* c, size_t l) {
Type typ{0}; Type typ{Type::Make(0)};
for (const char *p = c; p < c + l; p++) { for (const char *p = c; p < c + l; p++) {
typ = typ | Type( typ = typ | Type::Make(
*p == 'B' ? 1 << 0 : // Base type *p == 'B' ? 1 << 0 : // Base type
*p == 'V' ? 1 << 1 : // Verify type *p == 'V' ? 1 << 1 : // Verify type
*p == 'K' ? 1 << 2 : // Key type *p == 'K' ? 1 << 2 : // Key type
@ -548,7 +548,8 @@ private:
for (const auto& sub : subs) { for (const auto& sub : subs) {
subsize += sub->ScriptSize(); subsize += sub->ScriptSize();
} }
Type sub0type = subs.size() > 0 ? subs[0]->GetType() : ""_mst; static constexpr auto NONE_MST{""_mst};
Type sub0type = subs.size() > 0 ? subs[0]->GetType() : NONE_MST;
return internal::ComputeScriptLen(fragment, sub0type, subsize, k, subs.size(), keys.size(), m_script_ctx); return internal::ComputeScriptLen(fragment, sub0type, subsize, k, subs.size(), keys.size(), m_script_ctx);
} }
@ -712,9 +713,10 @@ private:
for (const auto& sub : subs) sub_types.push_back(sub->GetType()); for (const auto& sub : subs) sub_types.push_back(sub->GetType());
} }
// All other nodes than THRESH can be computed just from the types of the 0-3 subexpressions. // All other nodes than THRESH can be computed just from the types of the 0-3 subexpressions.
Type x = subs.size() > 0 ? subs[0]->GetType() : ""_mst; static constexpr auto NONE_MST{""_mst};
Type y = subs.size() > 1 ? subs[1]->GetType() : ""_mst; Type x = subs.size() > 0 ? subs[0]->GetType() : NONE_MST;
Type z = subs.size() > 2 ? subs[2]->GetType() : ""_mst; Type y = subs.size() > 1 ? subs[1]->GetType() : NONE_MST;
Type z = subs.size() > 2 ? subs[2]->GetType() : NONE_MST;
return SanitizeType(ComputeType(fragment, x, y, z, sub_types, k, data.size(), subs.size(), keys.size(), m_script_ctx)); return SanitizeType(ComputeType(fragment, x, y, z, sub_types, k, data.size(), subs.size(), keys.size(), m_script_ctx));
} }

View file

@ -391,6 +391,7 @@ std::optional<NodeInfo> ConsumeNodeStable(MsCtx script_ctx, FuzzedDataProvider&
bool allow_K = (type_needed == ""_mst) || (type_needed << "K"_mst); bool allow_K = (type_needed == ""_mst) || (type_needed << "K"_mst);
bool allow_V = (type_needed == ""_mst) || (type_needed << "V"_mst); bool allow_V = (type_needed == ""_mst) || (type_needed << "V"_mst);
bool allow_W = (type_needed == ""_mst) || (type_needed << "W"_mst); bool allow_W = (type_needed == ""_mst) || (type_needed << "W"_mst);
static constexpr auto B{"B"_mst}, K{"K"_mst}, V{"V"_mst}, W{"W"_mst};
switch (provider.ConsumeIntegral<uint8_t>()) { switch (provider.ConsumeIntegral<uint8_t>()) {
case 0: case 0:
@ -440,22 +441,22 @@ std::optional<NodeInfo> ConsumeNodeStable(MsCtx script_ctx, FuzzedDataProvider&
} }
case 11: case 11:
if (!(allow_B || allow_K || allow_V)) return {}; if (!(allow_B || allow_K || allow_V)) return {};
return {{{"B"_mst, type_needed, type_needed}, Fragment::ANDOR}}; return {{{B, type_needed, type_needed}, Fragment::ANDOR}};
case 12: case 12:
if (!(allow_B || allow_K || allow_V)) return {}; if (!(allow_B || allow_K || allow_V)) return {};
return {{{"V"_mst, type_needed}, Fragment::AND_V}}; return {{{V, type_needed}, Fragment::AND_V}};
case 13: case 13:
if (!allow_B) return {}; if (!allow_B) return {};
return {{{"B"_mst, "W"_mst}, Fragment::AND_B}}; return {{{B, W}, Fragment::AND_B}};
case 15: case 15:
if (!allow_B) return {}; if (!allow_B) return {};
return {{{"B"_mst, "W"_mst}, Fragment::OR_B}}; return {{{B, W}, Fragment::OR_B}};
case 16: case 16:
if (!allow_V) return {}; if (!allow_V) return {};
return {{{"B"_mst, "V"_mst}, Fragment::OR_C}}; return {{{B, V}, Fragment::OR_C}};
case 17: case 17:
if (!allow_B) return {}; if (!allow_B) return {};
return {{{"B"_mst, "B"_mst}, Fragment::OR_D}}; return {{{B, B}, Fragment::OR_D}};
case 18: case 18:
if (!(allow_B || allow_K || allow_V)) return {}; if (!(allow_B || allow_K || allow_V)) return {};
return {{{type_needed, type_needed}, Fragment::OR_I}}; return {{{type_needed, type_needed}, Fragment::OR_I}};
@ -472,25 +473,25 @@ std::optional<NodeInfo> ConsumeNodeStable(MsCtx script_ctx, FuzzedDataProvider&
} }
case 20: case 20:
if (!allow_W) return {}; if (!allow_W) return {};
return {{{"B"_mst}, Fragment::WRAP_A}}; return {{{B}, Fragment::WRAP_A}};
case 21: case 21:
if (!allow_W) return {}; if (!allow_W) return {};
return {{{"B"_mst}, Fragment::WRAP_S}}; return {{{B}, Fragment::WRAP_S}};
case 22: case 22:
if (!allow_B) return {}; if (!allow_B) return {};
return {{{"K"_mst}, Fragment::WRAP_C}}; return {{{K}, Fragment::WRAP_C}};
case 23: case 23:
if (!allow_B) return {}; if (!allow_B) return {};
return {{{"V"_mst}, Fragment::WRAP_D}}; return {{{V}, Fragment::WRAP_D}};
case 24: case 24:
if (!allow_V) return {}; if (!allow_V) return {};
return {{{"B"_mst}, Fragment::WRAP_V}}; return {{{B}, Fragment::WRAP_V}};
case 25: case 25:
if (!allow_B) return {}; if (!allow_B) return {};
return {{{"B"_mst}, Fragment::WRAP_J}}; return {{{B}, Fragment::WRAP_J}};
case 26: case 26:
if (!allow_B) return {}; if (!allow_B) return {};
return {{{"B"_mst}, Fragment::WRAP_N}}; return {{{B}, Fragment::WRAP_N}};
case 27: { case 27: {
if (!allow_B || !IsTapscript(script_ctx)) return {}; if (!allow_B || !IsTapscript(script_ctx)) return {};
const auto k = provider.ConsumeIntegral<uint16_t>(); const auto k = provider.ConsumeIntegral<uint16_t>();
@ -528,20 +529,23 @@ struct SmartInfo
{ {
/* Construct a set of interesting type requirements to reason with (sections of BKVWzondu). */ /* Construct a set of interesting type requirements to reason with (sections of BKVWzondu). */
std::vector<Type> types; std::vector<Type> types;
static constexpr auto B_mst{"B"_mst}, K_mst{"K"_mst}, V_mst{"V"_mst}, W_mst{"W"_mst};
static constexpr auto d_mst{"d"_mst}, n_mst{"n"_mst}, o_mst{"o"_mst}, u_mst{"u"_mst}, z_mst{"z"_mst};
static constexpr auto NONE_mst{""_mst};
for (int base = 0; base < 4; ++base) { /* select from B,K,V,W */ for (int base = 0; base < 4; ++base) { /* select from B,K,V,W */
Type type_base = base == 0 ? "B"_mst : base == 1 ? "K"_mst : base == 2 ? "V"_mst : "W"_mst; Type type_base = base == 0 ? B_mst : base == 1 ? K_mst : base == 2 ? V_mst : W_mst;
for (int zo = 0; zo < 3; ++zo) { /* select from z,o,(none) */ for (int zo = 0; zo < 3; ++zo) { /* select from z,o,(none) */
Type type_zo = zo == 0 ? "z"_mst : zo == 1 ? "o"_mst : ""_mst; Type type_zo = zo == 0 ? z_mst : zo == 1 ? o_mst : NONE_mst;
for (int n = 0; n < 2; ++n) { /* select from (none),n */ for (int n = 0; n < 2; ++n) { /* select from (none),n */
if (zo == 0 && n == 1) continue; /* z conflicts with n */ if (zo == 0 && n == 1) continue; /* z conflicts with n */
if (base == 3 && n == 1) continue; /* W conflicts with n */ if (base == 3 && n == 1) continue; /* W conflicts with n */
Type type_n = n == 0 ? ""_mst : "n"_mst; Type type_n = n == 0 ? NONE_mst : n_mst;
for (int d = 0; d < 2; ++d) { /* select from (none),d */ for (int d = 0; d < 2; ++d) { /* select from (none),d */
if (base == 2 && d == 1) continue; /* V conflicts with d */ if (base == 2 && d == 1) continue; /* V conflicts with d */
Type type_d = d == 0 ? ""_mst : "d"_mst; Type type_d = d == 0 ? NONE_mst : d_mst;
for (int u = 0; u < 2; ++u) { /* select from (none),u */ for (int u = 0; u < 2; ++u) { /* select from (none),u */
if (base == 2 && u == 1) continue; /* V conflicts with u */ if (base == 2 && u == 1) continue; /* V conflicts with u */
Type type_u = u == 0 ? ""_mst : "u"_mst; Type type_u = u == 0 ? NONE_mst : u_mst;
Type type = type_base | type_zo | type_n | type_d | type_u; Type type = type_base | type_zo | type_n | type_d | type_u;
types.push_back(type); types.push_back(type);
} }
@ -683,7 +687,7 @@ struct SmartInfo
/* Find which types are useful. The fuzzer logic only cares about constructing /* Find which types are useful. The fuzzer logic only cares about constructing
* B,V,K,W nodes, so any type that isn't needed in any recipe (directly or * B,V,K,W nodes, so any type that isn't needed in any recipe (directly or
* indirectly) for the construction of those is uninteresting. */ * indirectly) for the construction of those is uninteresting. */
std::set<Type> useful_types{"B"_mst, "V"_mst, "K"_mst, "W"_mst}; std::set<Type> useful_types{B_mst, V_mst, K_mst, W_mst};
// Find the transitive closure by adding types until the set of types does not change. // Find the transitive closure by adding types until the set of types does not change.
while (true) { while (true) {
size_t set_size = useful_types.size(); size_t set_size = useful_types.size();