Restricting a pattern by the values it captures

bronto::where(pred) is a statement written in a function annotated with BRONTO_BEFORE(). It constant-evaluates pred against the expressions a candidate site binds and rejects the match when pred does not evaluate to true.

Example
struct RewritePositiveArgument : bronto::rewrite_expr {
  BRONTO_BEFORE()
  void find(int n) {
    bronto::where(n > 0);
    foo(n);
  }

  BRONTO_AFTER()
  void repl(int n) { rewritten(n); }
};

This rule rewrites foo(3), but leaves foo(0) and foo(-3) alone.

Requirements

Reading a parameter's value requires the expression bound at the site to be a constant expression.

constexpr int kThree = 3;

void sites(int x) {
  foo(3);        // Rewritten. A literal is a constant expression.
  foo(kThree);   // Rewritten. A `constexpr` variable is a constant expression.
  foo(x);        // Not rewritten. `x` has no value at compile time.
}

A site whose predicate cannot be evaluated does not match. That non-match is silent with no diagnostic.

Placement

Every bronto::where must be a statement of its own, written among the leading statements of the BRONTO_BEFORE() body and ahead of the statement describing the pattern. Written anywhere else, a call to where is an error.

Multiple bronto::where statements are conjoined and evaluated in order, stopping at the first expression that does not evaluate to true, so the following is well defined even at a site binding 0.

bronto::where(n != 0);
bronto::where(100 % n == 0);

Short circuit applies within a predicate too. In bronto::where(k == 0 or n > 0) a site where k is 0 matches even when n binds a runtime expression, because the predicate never reads n.

What a predicate may reference

A predicate may reference the pattern's function parameters and its template parameters, in any combination.

struct RewriteWideAndPositive : bronto::rewrite_expr {
  template <typename T>
  BRONTO_BEFORE()
  void find(T n) {
    bronto::where(sizeof(T) > 2);
    bronto::where(n > 0);
    foo(n);
  }

  template <typename T>
  BRONTO_AFTER()
  void repl(T n) {
    rewritten(n);
  }
};

A predicate that is ill-formed for a candidate's deduced types rejects that candidate rather than producing an error, so bronto::where(n > 0) simply skips a potential match if the type of the expression bound to n has no operator>.

Caveat about negation

A where predicate will always fail if it is not a constant expression. A top level negation will not match all the things that the operand does not. There is no way for a where predicate to require that an expression be non-constant.

Other ways evaluation fails

A predicate that would be well formed can still fail to constant-evaluate, and every such failure is a silent non-match.

  • Undefined behavior inside the predicate, such as signed overflow.
  • Side effects in the bound expression.
  • Exceeding the compiler's limit on constexpr evaluation steps.

On this page