Not completely. E.g. it isn't possible to introduce differences in return types, associated types or associated constants based on trait implementation using this magic method. A fair bit of the soundness issues with specialisation comes from the ability to let type checking disagree with codegen on which types are involved.
There's still the problem, that - because codegen works on erased lifetimes - the result of T.implements_trait<…>() cannot be defined if a trait is only implemented for a subset of lifetimes on a type.
E.g.
trait Marker {}
impl Marker for &'static str {}
fn is_marked<T>(value: T) -> bool {
T.implements_trait::<Marker>()
}
// is_marked::<&'static str>(…) must return true
// is_marked::<&'a str>(…) where 'a != 'static must return false
fn main() {
let foo: &'static str = "foo";
println!("static str: {}", is_marked(foo)); // should print true
let bar_buffer = "bar".to_string();
let bar: &str = &*bar_buffer;
println!("non-static str: {}", is_marked(bar)); // should print false
}
but codegen would need to only create one instantiation of is_marked for any reference to str:
restrict T in implements_trait to 'static similar to the Any trait. That circumvents the problem with lifetime-conditional trait implementations but severely restricts where the check could be used.
call it implements_trait_for_all_lifetimes (bikeshedding/syntax discussion needed) and define it to only return true if not only the concrete type behind T but its whole type family (e.g. in the example for<'a> &'a str implements the trait. That way there would only be false negatives (&'static str does implement Marker but not all &str do → returns false) but no false positives.
Yeah, it's very similar. That feature is basically the T: 'static version of this idea, except that you also need a value of that type to perform the call to downcast_trait()
Note that you can emulate the value-less version by proxying through another trait (e.g. you can impl ProxyTrait for PhantomData<T> where T: Trait and then you can try downcasting an instance of PhantomData<T>).
The other direction is not possible to emulate: even if you know that T::implements_trait::<dyn Trait>() is true you can't call Traits methods on T!