Hi,
We’re experimenting with using MLIR in the Verona compiler. One of the things we are looking into is using MLIR as early as possible in the pipeline, in particular before monomorphization of generics. All type checking would be operating on the MLIR.
This means we need to encode type parameters in MLIR types. For example, from the following Verona function, we need a way to define the type parameter X, and a way to reference it in types.
fun id[X](a: X): X { return a; }
The first approach I can think of is using indices for type variables, and an attribute to specify the number of type parameters. The type X
becomes !verona.var<0>
.
fun @id(%a: !verona.var<0>) -> !verona.var<0> attributes { type_parameters = 1 } {
return %a : !verona.var<0>
}
I think its a bit unwieldy to manipulate, especially when having multiple levels of generics (eg. a generic class with a generic method), so I’m looking for a better solution.
One of these is to allow SSA values in types, allowing type parameters to be represented as regular parameters. Here, !verona.type
is the “kind of simple types” (also known as * in the Haskell world).
fun @id(%X: !verona.type, %a: !verona.var<%X>) -> !verona.var<%X> {
return %a: !verona.var<%X>
}
This second approach probably requires deep changes to MLIR itself, but could have, I believe, a multitude of use cases.
For example, an other concept in Verona is the support for “regions” (not the MLIR kind). A Verona region is, to some very broad approximation, an arena of objects. All objects allocated in that region are deallocated together. In the IR, we are interested in tracking the region of each pointer, for both type-checking and code generation purposes. If we had SSA values in types, we could express this as follows, !verona.pointer<%x>
is a pointer to an object in the region %x
.
fun @create(%x: !verona.region) -> !verona.pointer<%x> {
%z = !verona.allocate %x : !verona.region -> !verona.pointer<%x>
return %z
}
Unlike the polymorphic functions use case, !verona.region
values do have a runtime value, and thus cannot be encoded with just attributes.
There’s of course many questions raised by this, including how to represent the types without giving up on immutability and uniquing, how substitution works, how to determine type equality, …
I’m curious whether such an extension to MLIR types has been considered before, whether this would be useful to anyone else, and whether there is an alternative approach I could take instead.