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.
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.