rust-lang / rust-lang/rust

generalization incorrectly handled higher-ranked aliases

Open
#161,404 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-associated-items A-coherence A-higher-ranked C-bug I-ICE I-unsound P-medium T-types
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

I've found an unsoundess bug in rustc with some LLM-driven analysis. As discussed on zulip (t-types/trait-system-refactor thread), @theemathas produced the minimization below; filing here at @lcnr's request since the underlying issue also affects stable.

I tried this code:

#![forbid(unsafe_code)]

struct Wrap<T>(T);

trait Id {
    type Out;
}
impl<T> Id for T {
    type Out = u16;
}

trait Probe {}
// impl Probe for &'_ fn(u16) {}
impl<'c> Probe for &'c Wrap<for<'a> fn(<&'a &'c u64 as Id>::Out)> {}

trait Indirect {}
// impl Indirect for &'_ fn(u16) {}
impl<P: Probe> Indirect for P {}

trait Mark {}
impl Mark for fn(u16) {}

trait Select {}
impl<X> Select for X
// where X = fn(u16)
where
    for<'d> &'d Wrap<X>: Indirect,
{
}
// where Z = fn(u16)
impl<Z: Mark> Select for Z {}

I expected to see this happen: rejected with E0119

Instead, this happened: it compiles, on stable, nightly, and nightly with -Znext-solver.

Meta
stable: rustc 1.97.1 (8bab26f4f 2026-07-14)
nightly: rustc 1.100.0-nightly (8fa1c96cf 2026-08-17)
ICE variant (by @theemathas; stable + nightly + -Znext-solver)

#![forbid(unsafe_code)]

struct Wrap<T>(T);

trait Id {
    type Out;
}
impl<T> Id for T {
    type Out = u16;
}

trait Probe {}
impl<'c> Probe for &'c Wrap<for<'a> fn(<&'a &'c u64 as Id>::Out)> {}

trait Indirect {}
impl<P: Probe> Indirect for P {}

trait Mark {}
impl Mark for fn(u16) {}

trait Select {
    type Assoc;
}
impl<X> Select for X
where
    for<'d> &'d Wrap<X>: Indirect,
{
    type Assoc = usize;
}
impl<Z: Mark> Select for Z {
    type Assoc = &'static i32;
}

trait Same<U> {
    fn convert(x: <Self as Select>::Assoc) -> <U as Select>::Assoc
    where
        Self: Select,
        U: Select;
}

impl<T> Same<T> for T {
    fn convert(x: <T as Select>::Assoc) -> <T as Select>::Assoc
    where
        Self: Select,
    {
        x
    }
}

fn put<T>(x: usize) -> <T as Select>::Assoc
where
    for<'a> &'a Wrap<T>: Indirect,
{
    x
}

fn get<T>(x: <T as Select>::Assoc) -> &'static i32
where
    T: Mark,
{
    x
}

fn put_get<T, U>(x: usize) -> &'static i32
where
    for<'a> &'a Wrap<T>: Indirect,
    U: Mark,
    T: Same<U>,
{
    get::<U>(<T as Same<U>>::convert(put::<T>(x)))
}

fn foo<T, U>(x: usize) -> &'static i32
where
    for<'a> &'a Wrap<T>: Indirect,
    T: Mark,
{
    put_get::<T, T>(x)
}

fn main() {
    foo::<fn(u16), fn(u16)>(1_usize);
}
error: internal compiler error: compiler/rustc_middle/src/ty/normalize_erasing_regions.rs:201:13:
Failed to normalize Alias(AliasTy { args: [Binder { value: fn(u16), bound_vars: [] }],
kind: Projection { def_id: DefId(... Select::Assoc) }, .. }) in typing_env=... PostAnalysis ...

Original -Znext-solver soundness reproducer (rejected E0284 by the old solver; accepted by -Znext-solver; compiled program will segfault)

#![forbid(unsafe_code)]

trait Overlap {
    type T;
}
impl<S, V> Overlap for S
where
    S: Select<Pick = V>,
    for<'d> &'d [V]: Indirect,
{
    type T = usize;
}
impl Overlap for fn(u16) {
    type T = &'static u8;
}

trait Id {
    type Out;
}
impl<T> Id for T {
    type Out = u16;
}

trait Probe {}
impl<'c> Probe for &'c [for<'a> fn(<&'a &'c u8 as Id>::Out)] {}

trait Indirect {}
impl<P: Probe> Indirect for P {}

trait Select {
    type Pick;
}
impl<X> Select for X
where
    for<'d> &'d [X]: Indirect,
{
    type Pick = X;
}
impl<Z: Copy> Select for Z {
    type Pick = u8;
}

fn g<X>() -> <X as Overlap>::T
where
    for<'d> &'d [X]: Indirect,
{
    1usize
}

fn main() {
    println!("{}", *g::<fn(u16)>());
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by compiling the minimized reproducer on stable, nightly, and nightly with -Znext-solver, then compare the reported acceptance with the expected E0119 rejection. Use the trait and higher-ranked alias interactions in the reproducer as the entry point; done means the unsound example is rejected without the reported ICE or accepted compilation.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.