Description
#74614 discovered a regression in polymorphisation, fixed by disabling polymorphisation in #74633. This issue will track the fix for the regression, based on the discussion in Zulip (with thanks to @lcnr, @oli-obk and @eddyb).
#74614 can be reduced to the following MCVE (src/test/ui/issues/issue-74614.rs
after #74633):
fn test<T>() { std::mem::size_of::<T>(); }
pub fn foo<T>(_: T) -> &'static fn() {
&(test::<T> as fn())
}
fn outer<T>() {
foo(|| ());
}
fn main() {
outer::<u8>();
}
outer
is polymorphized because T
is not used in outer
or outer::{{closure}}#0
. outer::{{closure}}#0
therefore inherits T
from outer
and that ends up being T
in foo<T>
.
Without a transitive analysis (which isn't possible without hitting cycle errors), outer
cannot consider T
used based on how outer::{{closure}}#0
is used in other functions.
There are two issues that this can cause - when that closure is used in a cast or when that closure is used in reflection.
When used in a cast (as in the example above), the following lines will be hit:
rust/src/librustc_mir/interpret/cast.rs
Lines 49 to 52 in e22b61b
rust/src/librustc_mir/interpret/cast.rs
Lines 91 to 94 in e22b61b
Using still_further_specializable
instead of needs_subst
here fixes the issue, as that flag isn't set for the parent substitutions of closures.
When used in reflection (see the example below), then this will ICE once #74538 lands, but doesn't right now.
use std::any::TypeId;
pub fn foo<T: 'static>(_: T) -> TypeId {
TypeId::of::<T>()
}
fn outer<T: 'static>() {
foo(|| ());
}
fn main() {
outer::<u8>();
}
Reflection is more complicated because it makes the result of polymorphisation observable.