rust-lang / rust-lang/rust

A possible regression with -Zpolonius=next

Open
#160,979 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-borrow-checker A-impl-trait C-bug NLL-polonius T-types
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

A possible regression with -Zpolonius=next. Appears to be in how Polonius handles opaque types. These sound like they may be related:

I tried this code:

struct Lifetimed<'a> {
    some_tuple: &'a (),
}
static LIFETIMED: Lifetimed<'static> = Lifetimed { some_tuple: &() };

struct ClosureMaker<'a> {
    some_string: &'a str,
}

impl<'a> ClosureMaker<'a> {
    // Returns an opaque type implementing Fn() -> T where T is &'a Lifetimed<'a>.
    // The closure itself is 'static (no captures).
    fn make_closure_simple(&self) -> impl Fn() -> &'a Lifetimed<'a> + 'static {
        || {
            &LIFETIMED // Coerced from 'static to 'a
        }
    }
}

fn leak_it<T: 'static>(val: T) -> &'static T {
    Box::leak(Box::new(val))
}

fn return_value_with_dangling_lifetime() {
    let s = String::from("");
    let cm = ClosureMaker { some_string: &s };
    let val = cm.make_closure_simple()(); // Type is &'a Lifetimed<'a>
    let leaked = leak_it(val); // REQUIRES &'a Lifetimed<'a>: 'static
}

I expected to see this happen: compilation to fail like it does with the old borrow checker:

% rustc -Zpolonius=off --crate-type=rlib lib.rs
error[E0597]: `s` does not live long enough
  --> lib.rs:26:42
   |
25 |     let s = String::from("");
   |         - binding `s` declared here
26 |     let cm = ClosureMaker { some_string: &s };
   |                                          ^^ borrowed value does not live long enough
27 |     let val = cm.make_closure_simple()(); // Type is &'a Lifetimed<'a>
28 |     let leaked = leak_it(val); // REQUIRES &'a Lifetimed<'a>: 'static
   |                  ------------ argument requires that `s` is borrowed for `'static`
29 | }
   | - `s` dropped here while still borrowed
   |
note: requirement that the value outlives `'static` introduced here
  --> lib.rs:20:15
   |
20 | fn leak_it<T: 'static>(val: T) -> &'static T {
   |               ^^^^^^^

Instead, this happened: compilation succeeded with the new borrow checker:

% rustc -Zpolonius=next --crate-type=rlib lib.rs
warning: unused variable: `leaked`
  --> lib.rs:28:9
   |
28 |     let leaked = leak_it(val); // REQUIRES &'a Lifetimed<'a>: 'static
   |         ^^^^^^ help: if this is intentional, prefix it with an underscore: `_leaked`
   |
   = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default

warning: struct `Lifetimed` is never constructed
 --> lib.rs:1:8
  |
1 | struct Lifetimed<'a> {
  |        ^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default

warning: static `LIFETIMED` is never used
 --> lib.rs:4:8
  |
4 | static LIFETIMED: Lifetimed<'static> = Lifetimed { some_tuple: &() };
  |        ^^^^^^^^^

warning: struct `ClosureMaker` is never constructed
 --> lib.rs:6:8
  |
6 | struct ClosureMaker<'a> {
  |        ^^^^^^^^^^^^

warning: method `make_closure_simple` is never used
  --> lib.rs:13:8
   |
10 | impl<'a> ClosureMaker<'a> {
   | ------------------------- method in this implementation
...
13 |     fn make_closure_simple(&self) -> impl Fn() -> &'a Lifetimed<'a> + 'static {
   |        ^^^^^^^^^^^^^^^^^^^

warning: function `leak_it` is never used
  --> lib.rs:20:4
   |
20 | fn leak_it<T: 'static>(val: T) -> &'static T {
   |    ^^^^^^^

warning: function `return_value_with_dangling_lifetime` is never used
  --> lib.rs:24:4
   |
24 | fn return_value_with_dangling_lifetime() {
   |    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: 7 warnings emitted
Meta

rustc --version --verbose:

% rustc --version --verbose
rustc 1.99.0-nightly (3d6c19bb9 2026-08-11)
binary: rustc
commit-hash: 3d6c19bb9ab4798ecfb2ee943df01a811720fc27
commit-date: 2026-08-11
host: x86_64-unknown-linux-gnu
release: 1.99.0-nightly
LLVM version: 23.1.0
Backtrace

<backtrace>

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

Reproduce the issue from the supplied lib.rs example with rustc -Zpolonius=off and -Zpolonius=next, comparing the lifetime diagnostics and successful compilation. Then trace Polonius handling of opaque types and review the linked discussion and pull request. Done means the regression is covered by a test and the new borrow checker rejects the dangling-lifetime case.

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
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.