-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
rustc seemingly generates invalid IR #55976
Comments
Reduced: fn main() {
type_error(|x| &x);
}
fn type_error<T>(
_selector:
for<'a> fn(
&'a Vec<Box<dyn for<'b> Fn(&'b u8)>>
) -> &'a Vec<Box<dyn Fn(T)>>
) {} |
I thought we already fixed that by adding casts. One possible fix that might be less harmful would be to give all types that are subtyping-equivalent the same LLVM type. They should be having the same layout anyway, and their fields should be subtyping-equivalent (because that's what subtyping-equivalence means). The other option would be to insert casts when subtyping is taking place. |
#47638 was just an instance of the general problem, we just started generating named types for trait objects then, which made the problem show up in more cases. I agree with @arielb1's first proposal, i.e. destroy the distinction ahead of time. We're only wasting time by inserting casts and LLVM wastes time ignoring those casts (insert rant about how counterproductive LLVM's types are). |
Reduced to its essence: pub struct Foo<T>(T, [u8; 64]);
pub fn abc<'a>(x: &Foo<Box<for<'b> Fn(&'b u8)>>) -> &Foo<Box<Fn(&'a u8)>> { x }
fn main() {
abc(&Foo(Box::new(|_x| ()), [0; 64]));
} |
An example that my previous approach will not make compile (but currently gives an ICE outputtypeparametermismatch): // run-pass
// check that we handle subtyping between types with a different binding structure correctly,
// especially in LLVM - see issues #55976 & #47638
pub struct Foo<T> {
t: T,
force_in_memory_layout: [u8; 64]
}
pub trait MirrorT<'a> {
type Image;
}
impl<'a, T: Copy> MirrorT<'a> for T {
type Image = T;
}
type Mirror<'a, T> = <T as MirrorT<'a>>::Image;
// This is the "core problem": this function performs subtyping from
// `&Foo<Box<for<'b> Fn(&'b u8) -> u32>>` to `&Foo<Box<Fn(&'a u8) -> u32>>`
pub fn abc<'a, T: for<'s> MirrorT<'s> + 'static> (
x: &Foo<Box<for<'b> Fn(Mirror<'b, T>) -> u32>>) -> &Foo<Box<Fn(Mirror<'static, T>) -> u32>> {
x
}
// check that it runs
fn main() {
match abc::<u8>(&Foo {
t: Box::new(|_x| 42),
force_in_memory_layout: [1; 64]
}) {
v => {
assert_eq!((v.t)(0u8), 42);
assert_eq!(&v.force_in_memory_layout as &[u8], &[1u8; 64] as &[u8]);
}
};
} |
Nah you can do it without an // run-pass
// check that we handle subtyping between types with a different binding structure correctly,
// especially in LLVM - see issues #55976 & #47638
pub struct Foo<T> {
t: T,
force_in_memory_layout: [u8; 64]
}
pub trait MirrorT<'a> {
type Image;
}
impl<'a, T: Copy> MirrorT<'a> for T {
type Image = T;
}
pub trait H<T> {}
impl<T> H<T> for () {}
// This is the "core problem": this function performs subtyping from
// `&Foo<Box<for<'b> Fn(&'b u8) -> u32>>` to `&Foo<Box<Fn(&'a u8) -> u32>>`
pub fn abc<'a, T: for<'s> MirrorT<'s> + 'static> (
x: &Foo<Box<dyn for<'b> H<<T as MirrorT<'b>>::Image>>>)
-> &Foo<Box<dyn H<<T as MirrorT<'static>>::Image>>>
{
x
}
// check that it runs
fn main() {
match abc::<u8>(&Foo {
t: Box::new(()),
force_in_memory_layout: [1; 64]
}) {
_v => {
// assert_eq!((v.t)(0u8), 42);
// assert_eq!(&v.force_in_memory_layout as &[u8], &[1u8; 64] as &[u8]);
}
};
} |
This is making me suspect, that when we get higher-ranked type bounds, finding a "canonical form for subtyping" will be impossible (note: don't be so sure. You can't? (shouldn't?) have higher-ranged type bounds in trait objects, so life might be better). Might as well insert casts. Note that my old approach has problems with anonymization in invariant types: use std::marker::PhantomData;
pub struct Invariant<T>(T, PhantomData<*mut T>);
pub struct Foo<T>(T, [u8; 64]);
pub fn abc<'a>(x: &Foo<Invariant<Box<for<'b> fn(&'b u8, &'b u8)>>>)
-> &Foo<Invariant<Box<for<'b, 'c> fn(&'b u8, &'c u8)>>> { x }
fn main() {
abc(&Foo(Invariant(Box::new(|_x, _y| ()), PhantomData), [0; 64]));
} |
marking this as p-medium as per the prioritization discussion |
Fixes rust-lang#55976 Previously, we only erased early-bound regions. However, two types that differ only in their regions (including late-bound regions) are indistinguishable to LLVM, so it's safe to erase all regions.
@rustbot labels +T-types This seems like a problem around higher ranked normalization etc, so we are tagging as T-types. We think it may have been fixed as a side-effect of some LLVM change or other (cc @compiler-errors for the details). |
Seems to be fixed since 1.23. |
Signed-off-by: Yuki Okushi <jtitor@2k36.org>
Triage: The issue hasn't been fixed actually, at least on CI (CI stderr from #105691):
|
That's interesting. It doesn't fail in a playground. Is this a debug assert now or something? |
The build says |
Likely, we could use |
Yeah, this is fixed by LLVM using its opaque pointers, so that's why it's possibly broken on LLVM 13? |
Signed-off-by: Yuki Okushi <jtitor@2k36.org>
Re-submitted a PR with |
…iaskrgr Rollup of 2 pull requests Successful merges: - rust-lang#105770 (Rename ConstS to ConstData) - rust-lang#105785 (Add regression test for rust-lang#55976) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
I may have found a bug in the compiler.
Source
The entirety of the code to reproduce this bug is this:
Expected behavior
The program should build successfully, assuming the rustc type checker does not detect any errors at the level of the Rust type system.
Actual behavior
The rustc type checker seems happy with the program. However, presumably after the LLVM IR generation phase, and during some IR verification phase, I get this type error:
The text was updated successfully, but these errors were encountered: