Skip to content

Conversation

@alexreinking
Copy link
Member

@alexreinking alexreinking commented Nov 19, 2025

This follows the implementation of std::visit, using a struct to accumulate function overloads and then dispatching to the correct one at compile time (using std::is_invocable_v). The result is the ability to define mutators that capture local state. I convert a few visitors in the codebase as a means to achieve some test coverage.

For example, this is the code for expand_expr inside StorageFlattening.cpp:

// Perform all the substitutions in a scope
static Expr expand_expr(const Expr &e, const Scope<Expr> &scope) {
    Expr result = mutate_with(
        e,
        [&](const Variable *var, auto *self) -> Expr {
            if (const Expr *value = scope.find(var->name)) {
                // Mutate the expression, so lets can get replaced recursively.
                Expr expr = self->mutate(*value);
                debug(4) << "Fully expanded " << var->name << " -> " << expr << "\n";
                return expr;
            }
            return var;
        });
    debug(4) << "Expanded " << e << " into " << result << "\n";
    return result;
}

We need the pointer to the mutator as the second argument for be able to call self->visit_base, which grants the lambdas access to the IRMutator::visit function.

We also introduce helpers mutate_with and visit_with. Both accept a Stmt or Expr as the first argument. The remaining arguments are a list of lambdas from which to build a mutator or visitor. Both accept lists of visit overloads. The mutate_with helper additionally accepts a list of mutate overloads (which it detects via an is_invocable check on const Expr & or const Stmt & -- mixing mutate and visit overloads is unsupported).

@alexreinking alexreinking requested a review from abadams November 19, 2025 19:58
@alexreinking
Copy link
Member Author

If we like this direction, we can make a version for visitors, too.

@alexreinking
Copy link
Member Author

We can also consider adding mutate_with and mutate_with_generic helpers that immediately construct the LambdaMutator and call .mutate on it, which will simplify some call sites.

@alexreinking
Copy link
Member Author

We can also consider adding mutate_with and mutate_with_generic helpers that immediately construct the LambdaMutator and call .mutate on it, which will simplify some call sites.

I went ahead and did this -- clang-format decides to add a line, but I prefer how the functional form reads.

@alexreinking
Copy link
Member Author

Failures look like an upstream WASM breakage inside one of the rfactor cases -- not related to this PR

@alexreinking alexreinking requested a review from abadams December 5, 2025 14:51
@alexreinking alexreinking merged commit e9e167b into main Dec 6, 2025
15 of 19 checks passed
@alexreinking alexreinking deleted the lambda-mutator branch December 6, 2025 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants