about summary refs log tree commit diff
path: root/tests/ui/layout/normalization-failure.rs
blob: c0f8710c03cb372b5074719987106abd49b447a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//! This test demonstrates how `LayoutError::NormalizationFailure` can happen and why
//! it is necessary.
//!
//! This code does not cause an immediate normalization error in typeck, because we
//! don't reveal the hidden type returned by `opaque<T>` in the analysis typing mode.
//! Instead, `<{opaque} as Project2>::Assoc2` is a *rigid projection*, because we know
//! that `{opaque}: Project2` holds, due to the opaque type's `impl Project2` bound,
//! but cannot normalize `<{opaque} as Project2>::Assoc2` any further.
//!
//! However, in the post-analysis typing mode, which is used for the layout computation,
//! the opaque's hidden type is revealed to be `PhantomData<T>`, and now we fail to
//! normalize `<PhantomData<T> as Project2>::Assoc2` if there is a `T: Project1` bound
//! in the param env! This happens, because `PhantomData<T>: Project2` only holds if
//! `<T as Project1>::Assoc1 == ()` holds. This would usually be satisfied by the
//! blanket `impl<T> Project1 for T`, but due to the `T: Project1` bound we do not
//! normalize `<T as Project1>::Assoc1` via the impl and treat it as rigid instead.
//! Therefore, `PhantomData<T>: Project2` does NOT hold and normalizing
//! `<PhantomData<T> as Project2>::Assoc2` fails.
//!
//! Note that this layout error can only happen when computing the layout in a generic
//! context, which is not required for codegen, but may happen for lints, MIR optimizations,
//! and the transmute check.

use std::marker::PhantomData;

trait Project1 {
    type Assoc1;
}

impl<T> Project1 for T {
    type Assoc1 = ();
}

trait Project2 {
    type Assoc2;
    fn get(self) -> Self::Assoc2;
}

impl<T: Project1<Assoc1 = ()>> Project2 for PhantomData<T> {
    type Assoc2 = ();
    fn get(self) -> Self::Assoc2 {}
}

fn opaque<T>() -> impl Project2 {
    PhantomData::<T>
}

fn check<T: Project1>() {
    unsafe {
        std::mem::transmute::<_, ()>(opaque::<T>().get());
        //~^ ERROR: cannot transmute
        //~| NOTE: (unable to determine layout for `<impl Project2 as Project2>::Assoc2` because `<impl Project2 as Project2>::Assoc2` cannot be normalized)
        //~| NOTE: (0 bits)
    }
}

fn main() {}