Builder and OpBuilder distinction

I’m curious as to why there is a distinction between these two classes. OpBuilder can be created with just a context, so I’m not sure in what situation you would want to use a vanilla Builder. Would there ever be a reason to have another subclass of Builder?

The difference is explained in the class definitions:
_https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/IR/Builders.h#L48-L49

This class is a general helper class for creating context-global objects like types, attributes, and affine expressions.

_https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/IR/Builders.h#L204-L205

This class helps build Operations. Operations that are created are automatically inserted at an insertion point. The builder is copyable.

Also: class OpBuilder : public Builder ; since OpBuilder extends a Builder, the difference is really in the extra functionalities offered by OpBuilder on top of what the Builder provides. The builder class is about the immutable entities that exist MLIRContext-wise, and the OpBuilder adds the “operation-specific” part, which includes some more state in the builder itself to keep track of the insertion point. The only “state” in the Builder is the MLIRContext, so that is also the only thing that can be mutated here, the IR can’t be modified.
(That means also that the Builder is actually “smaller” in terms of footprint).