-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TypeId
and Polymorphization
#75325
Comments
I actually have a crazy idea on this and am not sure if it would help with the problem... The idea is that we can polymorphize pub fn foo<T: 'static>(_: T) -> (usize, usize, TypeId) {
(size_of::<T>, align_of::<T>, TypeId::of::<T>())
} would be turned into pub fn foo(vtable: &AnyVTable) -> (usize, usize, TypeId) {
// `vtable.type_id_impl` implements virtual method `Any::type_id`
(vtable.size, vtable.align, (vtable.type_id_impl)())
} In case the parameter of type |
I can't speak to how viable this exact suggestion would be, but I think it would run into the same issues that we see when detecting when not to polymorphize functions like Consider the following example, pub fn bar<A>() {
let _ = foo(|| ());
}
pub fn foo<B: 'static>(_: B) -> (usize, usize, TypeId) {
(size_of::<B>, align_of::<B>, TypeId::of::<B>())
}
This is hard to fix because we'd need to know how With that context, given that the caller of |
Ok I kinda got it... So currently the difficulty lies more on the dependency and potential cycle issues of different queries, rather than on how polymorphization could be done theoretically. I do have one more questions though - why would it be possible to polymorphize |
As I understand it, Most cases look something like the example shown below. Calling fn caller<A>(x: A) -> usize {
callee::<A>()
}
fn callee<B>() -> usize {
size_of::<B>()
} It's only when the closures have inherited unused parameters, and those closures are passed to other functions, that polymorphized parameters can end up being provided to intrinsics. However, even in that case, the unused generic parameter in the closure that was passed to the function wouldn't affect the output of
|
If the size of the closure were to depend on the unused generic parameter, that parameter would not be used, would it 😆 |
Couldn't we use a next Rust edition to change the behavior of |
This can't change between editions. No type system changes are possible using editions, only syntactical changes. Even if the |
Would it be possible to enable polymorphization only in leaf functions, if we know that there's no reflection and no other function is called? |
Doing it only for leaf functions would erase most benefit I think. Almost every function in rust calls at least one other function. For fn nop<T>() {}
fn test_the_waters<A: 'static, B: 'static>(a: A, _: B) {
let any: Box<dyn std::any::Any> = Box::new(a);
assert!(any.downcast_ref::<A>().is_some());
assert!(any.downcast_ref::<B>().is_none());
assert!(any.downcast_ref::<fn()>().is_none());
}
fn main() {
test_the_waters(nop::<u32>, nop::<u64>);
} could we pass unpolymorphized use std::any::TypeId;
pub fn foo<T: 'static>(_: T) -> TypeId {
TypeId::of::<T>()
}
fn outer<T: 'static>() -> TypeId {
foo(|| ())
}
fn main() {
assert!(outer::<u8>() != outer::<u16>());
} consider the |
I was thinking more about small functions on generic containers, like |
For context: I believe we should fundamentally change polymorphization. I badly implemented that idea in lcnr#6. I stopped because I didn't understand the type system that well back then and because this is a bit :/ with the old trait solver. Also, my priorities have changed since then to focus on #107374 instead. The tl;dr of the new approach is as follows:
This approach fixes the I don't think it makes sense to keep pushing on polymorphization for now as there are still bigger wins via improvements to the type systems and less involved mir opts. This approach will also be easier to get right once our type system is better 😁 |
I believed that polymorphization is about reducing the size of produced binaries and not about compilation performance |
I think that linkers are nowadays relatively good at removing identical functions from the final binary (not sure if |
When polymorphization is enabled (
-Zpolymorphization=on
), then this can currently be observed byTypeId
. For example, the following code when built with a polymorphization-enabled compiler will fail the asserts (and succeeds normally; playground):This issue was discovered while investigating an earlier regression. After #74538 landed, the example shown below, where a polymorphized closure is indirectly passed to
TypeId
, would result in an ICE (discussion from that investigation):In #74717, the check introduced in #74538 was modified so that this case (and another unrelated case) would no longer ICE for polymorphized functions (discussion regarding that PR). There have been more recent discussion regarding this in a dedicated Zulip topic.
Unresolved questions:
TypeId
seems very challenging.The text was updated successfully, but these errors were encountered: