diff --git a/include/iris/x4/rule.hpp b/include/iris/x4/rule.hpp index de7c06650..f9577a9b6 100644 --- a/include/iris/x4/rule.hpp +++ b/include/iris/x4/rule.hpp @@ -38,7 +38,7 @@ namespace iris::x4 { -template +template struct rule; namespace detail { @@ -385,12 +385,12 @@ concept RuleAttrCompatible = } // detail -template +template struct rule : parser> { - static_assert(X4Attribute); - static_assert(X4UnusedAttribute || !std::is_const_v, "Rule attribute cannot be const qualified"); - static_assert(!std::is_same_v, unused_container_type>, "`rule` with `unused_container_type` is not supported"); + // This type MUST be constructible with incomplete types. + // Do NOT add `static_assert`s or other constructs that cause eager + // instantiation of `RuleAttr` within this class body. using id = RuleID; using attribute_type = RuleAttr; @@ -419,6 +419,7 @@ struct rule : parser> parse(It& first, Se const& last, Context const& ctx, Exposed& exposed_attr) const // never noexcept; requires very complex implementation details { + check_invariants(); static_assert(has_attribute, "A rule must have an attribute. Check your rule definition."); // Remove the `_rule_var` context. This makes the actual `context` type passed to @@ -476,6 +477,7 @@ struct rule : parser> parse(It& first, Se const& last, Context const& ctx, unused_type const&) const // never noexcept; requires very complex implementation details { + check_invariants(); // make sure we pass exactly the rule attribute type attribute_type no_attr; // default-initialize @@ -497,6 +499,7 @@ struct rule : parser> > ) { + check_invariants(); return {as_parser(std::forward(rhs)), name}; } @@ -511,6 +514,7 @@ struct rule : parser> > ) { + check_invariants(); return {as_parser(std::forward(rhs)), name}; } @@ -530,6 +534,7 @@ struct rule : parser> > ) { + check_invariants(); return {as_parser(std::forward(rhs)), name}; } @@ -544,8 +549,17 @@ struct rule : parser> > ) { + check_invariants(); return {as_parser(std::forward(rhs)), name}; } + +private: + static constexpr void check_invariants() noexcept + { + static_assert(X4Attribute); + static_assert(X4UnusedAttribute || !std::is_const_v, "Rule attribute cannot be const qualified"); + static_assert(!std::is_same_v, unused_container_type>, "`rule` with `unused_container_type` is not supported"); + } }; namespace detail { diff --git a/test/x4/rule1.cpp b/test/x4/rule1.cpp index ebc04f753..dcd73998c 100644 --- a/test/x4/rule1.cpp +++ b/test/x4/rule1.cpp @@ -17,6 +17,13 @@ #include #include +TEST_CASE("rule: constructible with incomplete type") +{ + struct incomplete_type; + [[maybe_unused]] constexpr x4::rule incomplete_type_rule_a{}; + [[maybe_unused]] constexpr x4::rule incomplete_type_rule_b{}; +} + TEST_CASE("rule1") { using namespace x4::standard;