API: Add `sem::TyKind::implements_trait` (Draft)
This PR allows users to check if a given semantic type implements a specific trait. The implementation is inspired by Clippy's implementation of clippy_utils::ty::implements_trait. It already works for simple traits, without generics.
I'm stuck on how to allow users to specify generic arguments for the trait. My idea for Marker's function was that users can construct a TestTraitRef, which specifies a trait with its potential generics. I think this would make it a bit cleaner than Clippy's approach of taking a trait_id: DefId and args: &[GenericArg<'tcx>]. It also makes the interface more extendable. The problem is, that I can't figure out how to let the user specify generics in this way...
First some context. Generic arguments for the type check can be constructed from rustc's semantic types. Clippy constructs new semantic types for the type check, if needed. This is something which we'll also have to do in Marker, to support generics in types. Clippy doesn't distinguish between real types from the compiler and fake types constructed for this check. For Marker, I don't think this is a real option. If we allow users to construct sem::TyKinds I see numerous problems coming our way. Instead, I considered having a simplified type representation, that users can use to construct types for testing, something like this:
#[derive(Debug)]
enum TestType<'a> {
Ref(&'a Test<'a>),
Tuple(&'a [&'a Test<'a>]),
Type(u32)
}
Then we could construct semantic types in the backend for the type check. And this is where the issue begins. I can't figure out a way to deal with the references in a good way, since I would like to store the thing inside the TestTraitRef. For that, we would have to allocate memory somewhere, as 'a wouldn't live long enough otherwise.
Does anyone have a suggestion on how to best do this?
I can thing of three ways, which all feel a bit complicated:
- Simply use an allocator in
marker_apito extend the lifetime. This would add a dependency, but allow us to use these kinds of references. - Have a
Vec<TestType>in theTestTraitRefthat stores the nodes, extending their lifetimes to the life ofTestTraitRef. This feels hacky and also doesn't solve all problems, as slices etc would need to be handled separately. - Use
Box<>instead of references. Here I fear that we lock us in into a design, which can't be changed later. We could hide the usage of Boxes, by denying the user access to theTestTypeenum with its variants directly, and instead require them to use a builder of some kind. Using a builder is probably smart in either case. It still feels weird to me.
@Veetaha, do you maybe have an idea, how could this be solved? If you have the time, I'd appreciate it, if you could also take a look at the interface I currently have. Maybe you have some early feedback, which I can incorporate.
cc: rust-marker/marker#141