Restricting what a pattern parameter binds

A parameter of a function annotated with BRONTO_BEFORE() binds any expression of the appropriate type. BRONTO_BINDS(predicate) written after the parameter name narrows this, so that the parameter binds only expressions in the set described by predicate. Today those sets are categories of literals.

Example
struct RewriteLiteralArgument : bronto::rewrite_expr {
  BRONTO_BEFORE()
  void find(int n BRONTO_BINDS(bronto::literal)) { foo(n); }

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

This rule rewrites foo(1), but leaves foo(x) alone.

BRONTO_BINDS

Requirements

BRONTO_BINDS may only be applied to a parameter of a function annotated with BRONTO_BEFORE(), and a parameter may carry at most one such annotation. predicate must be an integral constant expression built from the bronto:: constants described below.

Semantics

BRONTO_BINDS filters what the parameter would otherwise bind. The parameter still binds only expressions whose type it accepts. The annotation rejects any expression whose category is absent from predicate. A pattern with such a parameter simply does not match at sites where the expression falls outside the set, and nothing is rewritten there.

Categories

Each constant names one category of expression.

ConstantMatchesDoes not match
bronto::integer_literal42, 42ull, +42, -42, ~42x, 3.14, !42
bronto::floating_literal1.f, 1e2, 3.L, +2.a double variable, !2.
bronto::character_literal'a', '\0', L'β'0, +'a', ~'a'
bronto::boolean_literaltrue, false, !false0, -false, ~true
bronto::string_literal"hello", L"world"NULL, a char const* variable
bronto::pointer_literalnullptr, NULL0, 0L, an int* variable
bronto::user_defined_literal1.5_km, 42_bits, "name"_tag, +42_bits42, 1.5, "name", ~42_bits
bronto::non_literalanything in none of the *_literal categories7

bronto::literal names the union of every *_literal category except bronto::non_literal, so it matches any literal expression.

NULL is a pointer literal wherever it expands to __null, which is how most implementations spell it. A literal 0 is not a pointer literal, even in a context where it is a null pointer constant.

Unary operators

An expression stays in its operand's category when a unary operator is meaningful for that category.

  • + and - preserve integer, floating-point, and user-defined literals.
  • ~ preserves integer literals.
  • ! preserves boolean literals.

Every other combination is a bronto::non_literal. This is why !42 does not bind a parameter restricted to integer literals, and +'a' does not bind one restricted to character literals.

Composing predicates

Categories are bitmasks, so predicates compose with the bitwise operators. Write | for the union of two categories, & for their intersection, and ~ for the complement of a category.

Composition Example
struct LogEverythingElse : bronto::rewrite_expr {
  template <typename T>
  BRONTO_BEFORE()
  void find(T x BRONTO_BINDS(~(bronto::integer_literal |
                               bronto::string_literal))) {
    return sink(x);
  }

  template <typename T>
  BRONTO_AFTER()
  void replace(T x) {
    return logged(x);
  }
};

This rule rewrites sink(3.14) and sink(v), but not sink(7) or sink("hi"). Writing bronto::non_literal is equivalent to writing ~bronto::literal.

Restricting by value instead

If you want to restrict a match based on its constant-evaluated value, you can use bronto::where, which is the value-level companion to BRONTO_BINDS. A predicate on what a literal equals, such as being positive or even, can use bronto::where. The two compose, so a parameter can carry a BRONTO_BINDS annotation while the match pattern contains a bronto::where clause.

On this page