Skip to content

param-env shadowing can cause goals to fail #23

@lcnr

Description

@lcnr

https://github.com/rust-lang/rust/blob/672388edbee9e93c35e5fdf7dac818a6612a5103/tests/ui/layout/normalization-failure.rs

//! 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());
    }
}

fn main() {}

This means inlining, CTFE, and accessing struct fields in an non-empty environment have to all handle trait goals failing entirely

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions