rust-lang / rust-lang/rust

[-Znext-solver] Outlive clauses in param env are incorrectly skipped in liveness analysis

Open
#158,461 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

C-bug E-needs-test T-compiler T-types WG-trait-system-refactor
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

I tried this code:

trait Id {
    type SelfType;
}

impl<T> Id for T {
    type SelfType = T;
}

trait Foo {
    type Assoc<'a>
    where
        Self: 'a;

    fn assoc(&mut self) -> Self::Assoc<'_>;
}

fn overlapping_mut<T>(mut t: T)
where
    T: Foo,
    for<'a> <T::Assoc<'a> as Id>::SelfType: 'static,
{
    let a = t.assoc();
    let b = t.assoc();
}

fn live_past_borrow<T>(mut t: T)
where
    T: Foo,
    for<'a> <T::Assoc<'a> as Id>::SelfType: 'static {
    let x = t.assoc();
    drop(t);
    drop(x);
}


I expected to see this happen: It compiles like with the old solver.

Instead, this happened:

error[E0499]: cannot borrow `t` as mutable more than once at a time
  --> counter_examples/unnormalized-into-outlive.rs:25:13
   |
24 |     let a = t.assoc();
   |             - first mutable borrow occurs here
25 |     let b = t.assoc();
   |             ^ second mutable borrow occurs here
26 | }
   | - first borrow might be used here, when `a` is dropped and runs the destructor for type `<T as Foo>::Assoc<'_>`

error[E0505]: cannot move out of `t` because it is borrowed
  --> counter_examples/unnormalized-into-outlive.rs:33:10
   |
28 | fn live_past_borrow<T>(mut t: T)
   |                        ----- binding `t` declared here
...
32 |     let x = t.assoc();
   |             - borrow of `t` occurs here
33 |     drop(t);
   |          ^ move out of `t` occurs here
34 |     drop(x);
   |          - borrow later used here

error: aborting due to 2 previous errors
Meta

rustc --version --verbose:

rustc 1.98.0-dev (2026--6-26)
binary: rustc
commit-hash: unknown
commit-date: unknown
host: x86_64-unknown-linux-gnu
release: 1.98.0-dev
LLVM version: 22.1.8
Analysis

We try to record free regions from TypeOutlives bounds.
We only care about the region if the outlive ty is equal to current alias ty.
But the outlive ty from param env is unnormalized and fails the equality check.

Related tests

These can be adapted into regression tests for the fix:
tests/ui/borrowck/alias-liveness/gat-static.rs - just use the example code above.
tests/ui/borrowck/alias-liveness/rtn-static.rs - exploiting RTN is harder, if possible. We can only use it in specific positions.

cc @lcnr

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 in compiler/rustc_infer/src/infer/outlives/for_liveness.rs, focusing on the TypeOutlives handling described in the issue. Review tests/ui/borrowck/alias-liveness/gat-static.rs and rtn-static.rs, adapt the supplied examples into regression coverage, and verify that the cases compile as expected without the reported borrow errors.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.