From b06c28a843f18fa59987aecd3b8f0ee4d220ad58 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sat, 15 Aug 2026 21:08:36 +0000 Subject: [PATCH 1/2] Treat vars created for opaque subtyping as live everywhere --- .../src/type_check/relate_tys.rs | 26 +++++- ...ultiple-defining-uses-160669.legacy.stderr | 82 +++++++++++++++++++ ...-multiple-defining-uses-160669.next.stderr | 69 ++++++++++++++++ ...e-multiple-defining-uses-160669.nll.stderr | 80 ++++++++++++++++++ ...tiple-defining-uses-160669.polonius.stderr | 80 ++++++++++++++++++ .../opaque-multiple-defining-uses-160669.rs | 65 +++++++++++++++ ...-suggestion-in-proper-span-issue-121267.rs | 1 + ...gestion-in-proper-span-issue-121267.stderr | 19 ++++- 8 files changed, 419 insertions(+), 3 deletions(-) create mode 100644 tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.legacy.stderr create mode 100644 tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.next.stderr create mode 100644 tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.nll.stderr create mode 100644 tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.polonius.stderr create mode 100644 tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.rs diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index e99a757b76305..c922d1a9f0f86 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -144,7 +144,31 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> { variance, ty, )?; - Ok(infcx.resolve_vars_if_possible(Ty::new_infer(infcx.tcx, ty::TyVar(ty_vid)))) + let new_var = + infcx.resolve_vars_if_possible(Ty::new_infer(infcx.tcx, ty::TyVar(ty_vid))); + + // Any regions in this new type must be live everywhere, so we mark them as such. + // (It may be that it only needs to be live where the opaque type itself is - which + // includes defining use sites, but we'll be conservative and mark all points as live). + // This is needed for Polonius, which doesn't propagate constraints + // through dead regions. See issue #160669. + // + // We only do this in the root universe. Inside a binder these regions live in a higher + // universe and their values contain placeholders; marking them live at every point + // leaks those placeholders, turning the higher-ranked check into an error which then + // suppresses the deferred opaque type diagnostics. + if infcx.universe() == ty::UniverseIndex::ROOT { + let tcx = infcx.tcx; + let liveness = &mut self.type_checker.constraints.liveness_constraints; + ty::fold_regions(tcx, new_var, |r, _| { + if let ty::ReVar(vid) = r.kind() { + liveness.add_all_points(vid); + } + r + }); + } + + Ok(new_var) }; let (a, b) = match (a.kind(), b.kind()) { diff --git a/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.legacy.stderr b/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.legacy.stderr new file mode 100644 index 0000000000000..c049b48e67f11 --- /dev/null +++ b/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.legacy.stderr @@ -0,0 +1,82 @@ +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:27:16 + | +LL | fn two_uses<'a>(s: &'a String, flag: bool) -> impl Display + use<'a> { + | -- lifetime `'a` defined here +LL | if flag { +LL | let local = String::from("dangling"); + | ----- binding `local` declared here +LL | return &local; + | ^^^^^^ borrowed value does not live long enough +LL | } +LL | s + | - opaque type requires that `local` is borrowed for `'a` +LL | } + | - `local` dropped here while still borrowed + +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:37:16 + | +LL | fn three_uses<'a>(s: &'a String, flag: u8) -> impl Display + use<'a> { + | -- lifetime `'a` defined here +LL | if flag == 0 { +LL | let local = String::from("dangling0"); + | ----- binding `local` declared here +LL | return &local; + | ^^^^^^ borrowed value does not live long enough +... +LL | return &local; + | ------ opaque type requires that `local` is borrowed for `'a` +... +LL | } + | - `local` dropped here while still borrowed + +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:41:16 + | +LL | fn three_uses<'a>(s: &'a String, flag: u8) -> impl Display + use<'a> { + | -- lifetime `'a` defined here +... +LL | let local = String::from("dangling1"); + | ----- binding `local` declared here +LL | return &local; + | ^^^^^^ + | | + | borrowed value does not live long enough + | opaque type requires that `local` is borrowed for `'a` +LL | } + | - `local` dropped here while still borrowed + +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:53:5 + | +LL | fn reversed_order<'a>(s: &'a String, flag: bool) -> impl Display + use<'a> { + | -- lifetime `'a` defined here +... +LL | let local = String::from("dangling"); + | ----- binding `local` declared here +LL | &local + | ^^^^^^ + | | + | borrowed value does not live long enough + | opaque type requires that `local` is borrowed for `'a` +LL | } + | - `local` dropped here while still borrowed + +error[E0597]: `short` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:60:16 + | +LL | fn minimal(short: (), out: &'static ()) -> impl Sized { + | ----- binding `short` declared here +LL | if true { +LL | return &short; + | ^^^^^^ borrowed value does not live long enough +LL | } +LL | out + | --- opaque type requires that `short` is borrowed for `'static` +LL | } + | - `short` dropped here while still borrowed + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.next.stderr b/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.next.stderr new file mode 100644 index 0000000000000..c249429b5d508 --- /dev/null +++ b/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.next.stderr @@ -0,0 +1,69 @@ +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:27:16 + | +LL | let local = String::from("dangling"); + | ----- binding `local` declared here +LL | return &local; + | ^^^^^^ borrowed value does not live long enough +LL | } + | - `local` dropped here while still borrowed +LL | s +LL | } + | - borrow later used here + +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:37:16 + | +LL | let local = String::from("dangling0"); + | ----- binding `local` declared here +LL | return &local; + | ^^^^^^ borrowed value does not live long enough +LL | } + | - `local` dropped here while still borrowed +... +LL | } + | - borrow later used here + +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:41:16 + | +LL | let local = String::from("dangling1"); + | ----- binding `local` declared here +LL | return &local; + | ^^^^^^ borrowed value does not live long enough +LL | } + | - `local` dropped here while still borrowed +LL | s +LL | } + | - borrow later used here + +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:53:5 + | +LL | let local = String::from("dangling"); + | ----- binding `local` declared here +LL | &local + | ^^^^^^ borrowed value does not live long enough +LL | } + | -- borrow later used here + | | + | `local` dropped here while still borrowed + +error[E0597]: `short` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:60:16 + | +LL | fn minimal(short: (), out: &'static ()) -> impl Sized { + | ----- binding `short` declared here +LL | if true { +LL | return &short; + | ^^^^^^ borrowed value does not live long enough +... +LL | } + | - + | | + | `short` dropped here while still borrowed + | borrow later used here + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.nll.stderr b/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.nll.stderr new file mode 100644 index 0000000000000..cb6a726f4514b --- /dev/null +++ b/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.nll.stderr @@ -0,0 +1,80 @@ +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:27:16 + | +LL | fn two_uses<'a>(s: &'a String, flag: bool) -> impl Display + use<'a> { + | -- lifetime `'a` defined here +LL | if flag { +LL | let local = String::from("dangling"); + | ----- binding `local` declared here +LL | return &local; + | ^^^^^^ borrowed value does not live long enough +LL | } + | - `local` dropped here while still borrowed +LL | s + | - opaque type requires that `local` is borrowed for `'a` + +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:37:16 + | +LL | fn three_uses<'a>(s: &'a String, flag: u8) -> impl Display + use<'a> { + | -- lifetime `'a` defined here +LL | if flag == 0 { +LL | let local = String::from("dangling0"); + | ----- binding `local` declared here +LL | return &local; + | ^^^^^^ borrowed value does not live long enough +LL | } + | - `local` dropped here while still borrowed +... +LL | return &local; + | ------ opaque type requires that `local` is borrowed for `'a` + +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:41:16 + | +LL | fn three_uses<'a>(s: &'a String, flag: u8) -> impl Display + use<'a> { + | -- lifetime `'a` defined here +... +LL | let local = String::from("dangling1"); + | ----- binding `local` declared here +LL | return &local; + | ^^^^^^ + | | + | borrowed value does not live long enough + | opaque type requires that `local` is borrowed for `'a` +LL | } + | - `local` dropped here while still borrowed + +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:53:5 + | +LL | fn reversed_order<'a>(s: &'a String, flag: bool) -> impl Display + use<'a> { + | -- lifetime `'a` defined here +... +LL | let local = String::from("dangling"); + | ----- binding `local` declared here +LL | &local + | ^^^^^^ + | | + | borrowed value does not live long enough + | opaque type requires that `local` is borrowed for `'a` +LL | } + | - `local` dropped here while still borrowed + +error[E0597]: `short` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:60:16 + | +LL | fn minimal(short: (), out: &'static ()) -> impl Sized { + | ----- binding `short` declared here +LL | if true { +LL | return &short; + | ^^^^^^ borrowed value does not live long enough +LL | } +LL | out + | --- opaque type requires that `short` is borrowed for `'static` +LL | } + | - `short` dropped here while still borrowed + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.polonius.stderr b/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.polonius.stderr new file mode 100644 index 0000000000000..cb6a726f4514b --- /dev/null +++ b/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.polonius.stderr @@ -0,0 +1,80 @@ +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:27:16 + | +LL | fn two_uses<'a>(s: &'a String, flag: bool) -> impl Display + use<'a> { + | -- lifetime `'a` defined here +LL | if flag { +LL | let local = String::from("dangling"); + | ----- binding `local` declared here +LL | return &local; + | ^^^^^^ borrowed value does not live long enough +LL | } + | - `local` dropped here while still borrowed +LL | s + | - opaque type requires that `local` is borrowed for `'a` + +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:37:16 + | +LL | fn three_uses<'a>(s: &'a String, flag: u8) -> impl Display + use<'a> { + | -- lifetime `'a` defined here +LL | if flag == 0 { +LL | let local = String::from("dangling0"); + | ----- binding `local` declared here +LL | return &local; + | ^^^^^^ borrowed value does not live long enough +LL | } + | - `local` dropped here while still borrowed +... +LL | return &local; + | ------ opaque type requires that `local` is borrowed for `'a` + +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:41:16 + | +LL | fn three_uses<'a>(s: &'a String, flag: u8) -> impl Display + use<'a> { + | -- lifetime `'a` defined here +... +LL | let local = String::from("dangling1"); + | ----- binding `local` declared here +LL | return &local; + | ^^^^^^ + | | + | borrowed value does not live long enough + | opaque type requires that `local` is borrowed for `'a` +LL | } + | - `local` dropped here while still borrowed + +error[E0597]: `local` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:53:5 + | +LL | fn reversed_order<'a>(s: &'a String, flag: bool) -> impl Display + use<'a> { + | -- lifetime `'a` defined here +... +LL | let local = String::from("dangling"); + | ----- binding `local` declared here +LL | &local + | ^^^^^^ + | | + | borrowed value does not live long enough + | opaque type requires that `local` is borrowed for `'a` +LL | } + | - `local` dropped here while still borrowed + +error[E0597]: `short` does not live long enough + --> $DIR/opaque-multiple-defining-uses-160669.rs:60:16 + | +LL | fn minimal(short: (), out: &'static ()) -> impl Sized { + | ----- binding `short` declared here +LL | if true { +LL | return &short; + | ^^^^^^ borrowed value does not live long enough +LL | } +LL | out + | --- opaque type requires that `short` is borrowed for `'static` +LL | } + | - `short` dropped here while still borrowed + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.rs b/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.rs new file mode 100644 index 0000000000000..c4a4f29a415f9 --- /dev/null +++ b/tests/ui/nll/polonius/opaque-multiple-defining-uses-160669.rs @@ -0,0 +1,65 @@ +// Regression test for #160669: an opaque type has a single hidden type for the whole +// typeck root, but the opaque type storage only keeps one hidden type per key, so a second +// defining use replaces the first. The goals equating the previous hidden type with the new +// one were registered at `Locations::Single` of the second use, while only the surviving +// hidden type gets tied to the definition site at `Locations::All`. +// +// With `-Zpolonius=next` that made the only edge out of the first defining use's hidden type +// exist at a single point of a sibling branch, which the loan of `local` can never reach. The +// loan died at the first return and the dangling reference escaped. +// +// The bug is order-dependent: only the *last* defining use in MIR traversal order stays +// anchored, so `three_uses` below loses an error too. + +//@ ignore-compare-mode-polonius (explicit revisions) +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ revisions: nll polonius legacy next +//@ [nll] compile-flags: -Z polonius=off +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy +//@ [next] compile-flags: -Z next-solver -Z polonius=next + +use std::fmt::Display; + +fn two_uses<'a>(s: &'a String, flag: bool) -> impl Display + use<'a> { + if flag { + let local = String::from("dangling"); + return &local; //~ ERROR `local` does not live long enough + } + s +} + +// The same, with a borrow of a local in *every* branch, to check that we don't only report +// the defining use which happens to be last in MIR order. +fn three_uses<'a>(s: &'a String, flag: u8) -> impl Display + use<'a> { + if flag == 0 { + let local = String::from("dangling0"); + return &local; //~ ERROR `local` does not live long enough + } + if flag == 1 { + let local = String::from("dangling1"); + return &local; //~ ERROR `local` does not live long enough + } + s +} + +// Control for the order dependence: here the bad defining use is the last one in MIR order, +// so it stayed anchored and was caught even before the fix. +fn reversed_order<'a>(s: &'a String, flag: bool) -> impl Display + use<'a> { + if flag { + return s; + } + let local = String::from("dangling"); + &local //~ ERROR `local` does not live long enough +} + +// This is a much more minimal MIR representation of the same bug. Useful for +// debugging. +fn minimal(short: (), out: &'static ()) -> impl Sized { + if true { + return &short; //~ ERROR `short` does not live long enough + } + out +} + +fn main() {} diff --git a/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs b/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs index 2011d4e01205e..05653667ac758 100644 --- a/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs +++ b/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs @@ -8,6 +8,7 @@ fn bar(src: &crate::Foo) -> impl Iterator { [0].into_iter() //~^ ERROR hidden type for `impl Iterator` captures lifetime that does not appear in bounds .filter_map(|_| foo(src)) + //~^ ERROR `src` does not live long enough } struct Foo<'a>(&'a str); diff --git a/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr b/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr index e7751de6f51ab..441adf4856b5d 100644 --- a/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr +++ b/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr @@ -10,6 +10,21 @@ LL | | .filter_map(|_| foo(src)) | = note: hidden type `FilterMap, {closure@...}>` captures lifetime `'_` -error: aborting due to 1 previous error +error[E0597]: `src` does not live long enough + --> $DIR/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs:10:29 + | +LL | fn bar(src: &crate::Foo) -> impl Iterator { + | --- binding `src` declared here +... +LL | .filter_map(|_| foo(src)) + | --- ^^^ borrowed value does not live long enough + | | + | value captured here +LL | +LL | } + | - `src` dropped here while still borrowed + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0700`. +Some errors have detailed explanations: E0597, E0700. +For more information about an error, try `rustc --explain E0597`. From b97b2a3b50238fb5193f74eb4cbdb6a64688b8d6 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sun, 16 Aug 2026 02:59:57 +0000 Subject: [PATCH 2/2] Treat generics in trait and projection bounds as live always --- Cargo.lock | 1 + compiler/rustc_trait_selection/Cargo.toml | 1 + .../src/traits/outlives_for_liveness.rs | 48 ++++++++++++ .../alias-static-interface-lifetime-151861.rs | 77 +++++++++++++++++++ ...as-static-interface-lifetime-151861.stderr | 29 +++++++ ...tatic-interface-lifetime-160979.nll.stderr | 22 ++++++ ...-interface-lifetime-160979.polonius.stderr | 22 ++++++ ...losure-static-interface-lifetime-160979.rs | 40 ++++++++++ .../alias-liveness/higher-ranked-use-bound.rs | 12 +++ .../borrowck/alias-liveness/higher-ranked.rs | 5 +- .../alias-liveness/higher-ranked.stderr | 19 +++++ .../opaque-capture-use-bound.rs | 11 +++ .../borrowck/alias-liveness/opaque-capture.rs | 6 +- .../alias-liveness/opaque-capture.stderr | 36 +++++++++ ...pit-shorten-through-impl-161038.nll.stderr | 16 ++++ ...horten-through-impl-161038.polonius.stderr | 16 ++++ .../rpit-shorten-through-impl-161038.rs | 41 ++++++++++ .../rpit-static-interface-lifetime-151861.rs | 27 +++++++ ...it-static-interface-lifetime-151861.stderr | 16 ++++ .../rpit-static-raw-ptr-160979.nll.stderr | 35 +++++++++ ...rpit-static-raw-ptr-160979.polonius.stderr | 35 +++++++++ .../rpit-static-raw-ptr-160979.rs | 33 ++++++++ .../alias-liveness/rpit-static-use-bound.rs | 19 +++++ .../ui/borrowck/alias-liveness/rpit-static.rs | 6 +- .../alias-liveness/rpit-static.stderr | 27 +++++++ tests/ui/coroutine/resume-arg-outlives.rs | 1 + tests/ui/coroutine/resume-arg-outlives.stderr | 21 ++++- 27 files changed, 609 insertions(+), 13 deletions(-) create mode 100644 tests/ui/borrowck/alias-liveness/alias-static-interface-lifetime-151861.rs create mode 100644 tests/ui/borrowck/alias-liveness/alias-static-interface-lifetime-151861.stderr create mode 100644 tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.nll.stderr create mode 100644 tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.polonius.stderr create mode 100644 tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.rs create mode 100644 tests/ui/borrowck/alias-liveness/higher-ranked-use-bound.rs create mode 100644 tests/ui/borrowck/alias-liveness/higher-ranked.stderr create mode 100644 tests/ui/borrowck/alias-liveness/opaque-capture-use-bound.rs create mode 100644 tests/ui/borrowck/alias-liveness/opaque-capture.stderr create mode 100644 tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.nll.stderr create mode 100644 tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.polonius.stderr create mode 100644 tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.rs create mode 100644 tests/ui/borrowck/alias-liveness/rpit-static-interface-lifetime-151861.rs create mode 100644 tests/ui/borrowck/alias-liveness/rpit-static-interface-lifetime-151861.stderr create mode 100644 tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.nll.stderr create mode 100644 tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.polonius.stderr create mode 100644 tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.rs create mode 100644 tests/ui/borrowck/alias-liveness/rpit-static-use-bound.rs create mode 100644 tests/ui/borrowck/alias-liveness/rpit-static.stderr diff --git a/Cargo.lock b/Cargo.lock index 5fc3a5ac71722..3ac86d9676474 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4858,6 +4858,7 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_hir", + "rustc_index", "rustc_infer", "rustc_macros", "rustc_middle", diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index ae231c217977d..4444a433f3579 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -11,6 +11,7 @@ rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } +rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs b/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs index 5cba32d742f62..4f1091b236dfa 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs @@ -1,6 +1,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::ty::{ self, Flags, ImplTraitInTraitData, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, @@ -465,6 +466,49 @@ fn live_args_for_outlives_clause<'tcx>( } } +/// For a given alias, return the set of args that show up in its trait or projection bounds. +/// For something like `type Foo<'a, 'b>: Trait<'a> + 'b`, this is `{0}` (`'a`). +#[tracing::instrument(level = "debug", skip(tcx), ret)] +fn args_in_alias_bounds<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> DenseBitSet { + struct Collector { + positions: DenseBitSet, + } + impl<'tcx> TypeVisitor> for Collector { + fn visit_region(&mut self, r: ty::Region<'tcx>) { + if let ty::ReEarlyParam(ebr) = r.kind() { + self.positions.insert(ebr.index); + } + } + fn visit_ty(&mut self, ty: Ty<'tcx>) { + if let ty::Param(pt) = *ty.kind() { + self.positions.insert(pt.index); + } + ty.super_visit_with(self); + } + } + + let mut collector = + Collector { positions: DenseBitSet::new_empty(tcx.generics_of(def_id).count()) }; + for clause in tcx.item_bounds(def_id).instantiate_identity().skip_norm_wip() { + match clause.kind().skip_binder() { + ty::ClauseKind::Trait(trait_pred) => { + for arg in &trait_pred.trait_ref.args[1..] { + arg.visit_with(&mut collector); + } + } + ty::ClauseKind::Projection(proj_pred) => { + for arg in &proj_pred.projection_term.args[1..] { + arg.visit_with(&mut collector); + } + proj_pred.term.visit_with(&mut collector); + } + ty::ClauseKind::TypeOutlives(_) | ty::ClauseKind::RegionOutlives(_) => {} + _ => {} + } + } + collector.positions +} + /// Visits free regions in the type that are relevant for liveness computation. /// These regions are passed to `OP`. /// @@ -566,6 +610,10 @@ where match capturable { Some(capturable_args) => { + for idx in args_in_alias_bounds(tcx, def_id).iter() { + let arg = args[idx as usize]; + arg.visit_with(self); + } for arg in capturable_args { let arg = arg.instantiate(tcx, args).skip_norm_wip(); arg.visit_with(self); diff --git a/tests/ui/borrowck/alias-liveness/alias-static-interface-lifetime-151861.rs b/tests/ui/borrowck/alias-liveness/alias-static-interface-lifetime-151861.rs new file mode 100644 index 0000000000000..3645de4283206 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/alias-static-interface-lifetime-151861.rs @@ -0,0 +1,77 @@ +trait Produce<'r> { + fn produce(self) -> &'r str; +} + +mod hidden { + pub struct Wrapper(pub std::ptr::NonNull); + + impl<'r> super::Produce<'r> for Wrapper { + fn produce(self) -> &'r str { + unsafe { + // SAFETY: a `Wrapper` is only ever built from a `&'r str` by + // the `construct` methods below, and callers can only reach + // this impl through `Ty<'r>: Produce<'r>` for that same `'r`. + self.0.as_ref() + } + } + } +} + +/// The `'static` bound is left to the user to state as a where-clause. +trait Gat { + type Ty<'r>: Produce<'r>; + fn construct<'r>(r: &'r str) -> Self::Ty<'r>; +} + +/// The `'static` bound is an item bound on the associated type. +trait ItemBound<'r> { + type Ty: 'static + Produce<'r>; + fn construct(r: &'r str) -> Self::Ty; +} + +struct Def; + +impl Gat for Def { + type Ty<'r> = hidden::Wrapper; + fn construct<'r>(r: &'r str) -> Self::Ty<'r> { + hidden::Wrapper(r.into()) + } +} + +impl<'r> ItemBound<'r> for Def { + type Ty = hidden::Wrapper; + fn construct(r: &'r str) -> Self::Ty { + hidden::Wrapper(r.into()) + } +} + +// A generic is used in both cases so that `G::Ty` cannot be normalized. +fn item_bound ItemBound<'r>>() { + let a; + { + let s = String::from("huh"); + a = ::construct(&s); //~ ERROR `s` does not live long enough + } + let _unrelated = String::from("UB!"); + let dangling: &str = a.produce(); + println!("{dangling}"); +} + +fn where_clause() +where + for<'r> G::Ty<'r>: 'static, +{ + let a; + { + let s = String::from("huh"); + a = G::construct(&s); //~ ERROR `s` does not live long enough + } + let _unrelated = String::from("UB!"); + let dangling: &str = a.produce(); + println!("{dangling}"); +} + +fn main() { + item_bound::(); + where_clause::(); +} diff --git a/tests/ui/borrowck/alias-liveness/alias-static-interface-lifetime-151861.stderr b/tests/ui/borrowck/alias-liveness/alias-static-interface-lifetime-151861.stderr new file mode 100644 index 0000000000000..bbcac4b9f1674 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/alias-static-interface-lifetime-151861.stderr @@ -0,0 +1,29 @@ +error[E0597]: `s` does not live long enough + --> $DIR/alias-static-interface-lifetime-151861.rs:53:41 + | +LL | let s = String::from("huh"); + | - binding `s` declared here +LL | a = ::construct(&s); + | ^^ borrowed value does not live long enough +LL | } + | - `s` dropped here while still borrowed +LL | let _unrelated = String::from("UB!"); +LL | let dangling: &str = a.produce(); + | - borrow later used here + +error[E0597]: `s` does not live long enough + --> $DIR/alias-static-interface-lifetime-151861.rs:67:26 + | +LL | let s = String::from("huh"); + | - binding `s` declared here +LL | a = G::construct(&s); + | ^^ borrowed value does not live long enough +LL | } + | - `s` dropped here while still borrowed +LL | let _unrelated = String::from("UB!"); +LL | let dangling: &str = a.produce(); + | - borrow later used here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.nll.stderr b/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.nll.stderr new file mode 100644 index 0000000000000..71cce8da35088 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.nll.stderr @@ -0,0 +1,22 @@ +error[E0597]: `s` does not live long enough + --> $DIR/closure-static-interface-lifetime-160979.rs:31:42 + | +LL | let s = String::from(""); + | - binding `s` declared here +LL | let cm = ClosureMaker { some_string: &s }; + | ^^ borrowed value does not live long enough +... +LL | let _leaked = leak_it(val); + | ------------ argument requires that `s` is borrowed for `'static` +LL | } + | - `s` dropped here while still borrowed + | +note: requirement that the value outlives `'static` introduced here + --> $DIR/closure-static-interface-lifetime-160979.rs:25:15 + | +LL | fn leak_it(val: T) -> &'static T { + | ^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.polonius.stderr b/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.polonius.stderr new file mode 100644 index 0000000000000..71cce8da35088 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.polonius.stderr @@ -0,0 +1,22 @@ +error[E0597]: `s` does not live long enough + --> $DIR/closure-static-interface-lifetime-160979.rs:31:42 + | +LL | let s = String::from(""); + | - binding `s` declared here +LL | let cm = ClosureMaker { some_string: &s }; + | ^^ borrowed value does not live long enough +... +LL | let _leaked = leak_it(val); + | ------------ argument requires that `s` is borrowed for `'static` +LL | } + | - `s` dropped here while still borrowed + | +note: requirement that the value outlives `'static` introduced here + --> $DIR/closure-static-interface-lifetime-160979.rs:25:15 + | +LL | fn leak_it(val: T) -> &'static T { + | ^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.rs b/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.rs new file mode 100644 index 0000000000000..bd91ca07cbbc6 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/closure-static-interface-lifetime-160979.rs @@ -0,0 +1,40 @@ +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius +//@ [nll] compile-flags: -Zpolonius=off +//@ [polonius] compile-flags: -Zpolonius=next + +struct Lifetimed<'a> { + #[allow(dead_code)] + some_tuple: &'a (), +} +static LIFETIMED: Lifetimed<'static> = Lifetimed { some_tuple: &() }; + +struct ClosureMaker<'a> { + #[allow(dead_code)] + some_string: &'a str, +} + +impl<'a> ClosureMaker<'a> { + // The closure itself is `'static` (it captures nothing), but its return + // type mentions `'a`. + fn make_closure_simple(&self) -> impl Fn() -> &'a Lifetimed<'a> + 'static { + || &LIFETIMED + } +} + +fn leak_it(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 }; + //~^ ERROR `s` does not live long enough + let val = cm.make_closure_simple()(); + // requires `&'a Lifetimed<'a>: 'static` + let _leaked = leak_it(val); +} + +fn main() { + return_value_with_dangling_lifetime(); +} diff --git a/tests/ui/borrowck/alias-liveness/higher-ranked-use-bound.rs b/tests/ui/borrowck/alias-liveness/higher-ranked-use-bound.rs new file mode 100644 index 0000000000000..fa1ab5f97d10e --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/higher-ranked-use-bound.rs @@ -0,0 +1,12 @@ +//@ check-pass + +trait Outlives<'a>: 'a {} +impl<'a, T: 'a> Outlives<'a> for T {} + +fn test<'o>(v: &'o Vec) -> impl use<'o> + for<'a> Outlives<'a> {} + +fn opaque_doesnt_use_temporary() { + let a = test(&vec![]); +} + +fn main() {} diff --git a/tests/ui/borrowck/alias-liveness/higher-ranked.rs b/tests/ui/borrowck/alias-liveness/higher-ranked.rs index 3af125299829b..7f4e042c3dcff 100644 --- a/tests/ui/borrowck/alias-liveness/higher-ranked.rs +++ b/tests/ui/borrowck/alias-liveness/higher-ranked.rs @@ -1,16 +1,13 @@ -//@ check-pass - trait Captures<'a> {} impl Captures<'_> for T {} trait Outlives<'a>: 'a {} impl<'a, T: 'a> Outlives<'a> for T {} -// Test that we treat `for<'a> Opaque: 'a` as `Opaque: 'static` fn test<'o>(v: &'o Vec) -> impl Captures<'o> + for<'a> Outlives<'a> {} fn opaque_doesnt_use_temporary() { - let a = test(&vec![]); + let a = test(&vec![]); //~ ERROR temporary value dropped while borrowed } fn main() {} diff --git a/tests/ui/borrowck/alias-liveness/higher-ranked.stderr b/tests/ui/borrowck/alias-liveness/higher-ranked.stderr new file mode 100644 index 0000000000000..760a35eba9173 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/higher-ranked.stderr @@ -0,0 +1,19 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/higher-ranked.rs:10:19 + | +LL | let a = test(&vec![]); + | ^^^^^^ - temporary value is freed at the end of this statement + | | + | creates a temporary value which is freed while still in use +LL | } + | - borrow might be used here, when `a` is dropped and runs the destructor for type `impl Captures<'_> + for<'a> Outlives<'a>` + | +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = vec![]; +LL ~ let a = test(&binding); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/borrowck/alias-liveness/opaque-capture-use-bound.rs b/tests/ui/borrowck/alias-liveness/opaque-capture-use-bound.rs new file mode 100644 index 0000000000000..8f2fb22e038ab --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/opaque-capture-use-bound.rs @@ -0,0 +1,11 @@ +//@ check-pass + +fn captures_temp_late<'a>(x: &'a Vec) -> impl Sized + use<'a> + 'static {} +fn captures_temp_early<'a: 'a>(x: &'a Vec) -> impl Sized + use<'a> + 'static {} + +fn test() { + let x = captures_temp_early(&vec![]); + let y = captures_temp_late(&vec![]); +} + +fn main() {} diff --git a/tests/ui/borrowck/alias-liveness/opaque-capture.rs b/tests/ui/borrowck/alias-liveness/opaque-capture.rs index 35b7d04d2ef39..be8618a13c55f 100644 --- a/tests/ui/borrowck/alias-liveness/opaque-capture.rs +++ b/tests/ui/borrowck/alias-liveness/opaque-capture.rs @@ -1,5 +1,3 @@ -//@ check-pass - // Check that opaques capturing early and late-bound vars correctly mark // regions required to be live using the item bounds. @@ -10,8 +8,8 @@ fn captures_temp_late<'a>(x: &'a Vec) -> impl Sized + Captures<'a> + 'stati fn captures_temp_early<'a: 'a>(x: &'a Vec) -> impl Sized + Captures<'a> + 'static {} fn test() { - let x = captures_temp_early(&vec![]); - let y = captures_temp_late(&vec![]); + let x = captures_temp_early(&vec![]); //~ ERROR temporary value dropped while borrowed + let y = captures_temp_late(&vec![]); //~ ERROR temporary value dropped while borrowed } fn main() {} diff --git a/tests/ui/borrowck/alias-liveness/opaque-capture.stderr b/tests/ui/borrowck/alias-liveness/opaque-capture.stderr new file mode 100644 index 0000000000000..378a5cab17447 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/opaque-capture.stderr @@ -0,0 +1,36 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/opaque-capture.rs:11:34 + | +LL | let x = captures_temp_early(&vec![]); + | ^^^^^^ - temporary value is freed at the end of this statement + | | + | creates a temporary value which is freed while still in use +LL | let y = captures_temp_late(&vec![]); +LL | } + | - borrow might be used here, when `x` is dropped and runs the destructor for type `impl Captures<'_> + 'static` + | +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = vec![]; +LL ~ let x = captures_temp_early(&binding); + | + +error[E0716]: temporary value dropped while borrowed + --> $DIR/opaque-capture.rs:12:33 + | +LL | let y = captures_temp_late(&vec![]); + | ^^^^^^ - temporary value is freed at the end of this statement + | | + | creates a temporary value which is freed while still in use +LL | } + | - borrow might be used here, when `y` is dropped and runs the destructor for type `impl Captures<'_> + 'static` + | +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = vec![]; +LL ~ let y = captures_temp_late(&binding); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.nll.stderr b/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.nll.stderr new file mode 100644 index 0000000000000..934b1ba8f798a --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.nll.stderr @@ -0,0 +1,16 @@ +error[E0597]: `*x` does not live long enough + --> $DIR/rpit-shorten-through-impl-161038.rs:37:27 + | +LL | let x: Box = Box::new(Box::new(1)); + | - binding `x` declared here +LL | let r: &Payload = &*x; + | ^^^ borrowed value does not live long enough +LL | wrong = make_producer(r).produce(); +LL | } + | - `*x` dropped here while still borrowed +LL | println!("{wrong}"); + | ----- borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.polonius.stderr b/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.polonius.stderr new file mode 100644 index 0000000000000..934b1ba8f798a --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.polonius.stderr @@ -0,0 +1,16 @@ +error[E0597]: `*x` does not live long enough + --> $DIR/rpit-shorten-through-impl-161038.rs:37:27 + | +LL | let x: Box = Box::new(Box::new(1)); + | - binding `x` declared here +LL | let r: &Payload = &*x; + | ^^^ borrowed value does not live long enough +LL | wrong = make_producer(r).produce(); +LL | } + | - `*x` dropped here while still borrowed +LL | println!("{wrong}"); + | ----- borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.rs b/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.rs new file mode 100644 index 0000000000000..941f627e457b6 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-shorten-through-impl-161038.rs @@ -0,0 +1,41 @@ +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius +//@ [nll] compile-flags: -Zpolonius=off +//@ [polonius] compile-flags: -Zpolonius=next + +type Payload = Box; + +trait Produce<'a> { + fn produce(self) -> &'a Payload; +} +impl<'a> Produce<'a> for &'a Payload { + fn produce(self) -> &'a Payload { + self + } +} + +trait Outlives<'a, 'b> { + fn shorten(x: impl Produce<'a> + 'a) -> impl Produce<'a> + 'b; +} +impl<'a: 'b, 'b> Outlives<'a, 'b> for () { + fn shorten(x: impl Produce<'a> + 'a) -> impl Produce<'a> + 'b { + x + } +} + +fn make_producer<'a, 'b>(payload: &'a Payload) -> impl Produce<'a> + 'b + use<'a, 'b> +where + (): Outlives<'a, 'b>, +{ + <()>::shorten(payload) +} + +fn main() { + let wrong: &Payload; + { + let x: Box = Box::new(Box::new(1)); + let r: &Payload = &*x; //~ ERROR `*x` does not live long enough + wrong = make_producer(r).produce(); + } + println!("{wrong}"); +} diff --git a/tests/ui/borrowck/alias-liveness/rpit-static-interface-lifetime-151861.rs b/tests/ui/borrowck/alias-liveness/rpit-static-interface-lifetime-151861.rs new file mode 100644 index 0000000000000..bb4a08b03d81e --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-static-interface-lifetime-151861.rs @@ -0,0 +1,27 @@ +fn foo<'r>(r: &'r str) -> impl 'static + Into<&'r str> { + struct Wrapper(std::ptr::NonNull); + + impl<'r> Into<&'r str> for Wrapper { + fn into(self) -> &'r str { + unsafe { + // SAFETY: `Wrapper` becomes an `impl use<'r> + Into<&'r str>`, + // so it cannot yield something with any lifetime other than + // this `'r` (or a covariant shrinkage thereof)... right? + self.0.as_ref() + } + } + } + + Wrapper(r.into()) +} + +fn main() { + let a; + { + let s = String::from("huh"); + a = foo(&s); //~ ERROR `s` does not live long enough + } + let _unrelated = String::from("UB!"); + let dangling: &str = a.into(); + println!("{dangling}"); +} diff --git a/tests/ui/borrowck/alias-liveness/rpit-static-interface-lifetime-151861.stderr b/tests/ui/borrowck/alias-liveness/rpit-static-interface-lifetime-151861.stderr new file mode 100644 index 0000000000000..5f91ba74b2cae --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-static-interface-lifetime-151861.stderr @@ -0,0 +1,16 @@ +error[E0597]: `s` does not live long enough + --> $DIR/rpit-static-interface-lifetime-151861.rs:22:17 + | +LL | let s = String::from("huh"); + | - binding `s` declared here +LL | a = foo(&s); + | ^^ borrowed value does not live long enough +LL | } + | - `s` dropped here while still borrowed +LL | let _unrelated = String::from("UB!"); +LL | let dangling: &str = a.into(); + | - borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.nll.stderr b/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.nll.stderr new file mode 100644 index 0000000000000..afe7bd972c935 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.nll.stderr @@ -0,0 +1,35 @@ +error[E0597]: `x` does not live long enough + --> $DIR/rpit-static-raw-ptr-160979.rs:28:49 + | +LL | let x: Box = Box::new(Box::new(1)); + | - binding `x` declared here +LL | let wrong: &'static Payload = make_producer(&x).produce(); + | ---------------- ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +... +LL | } + | - `x` dropped here while still borrowed + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/rpit-static-raw-ptr-160979.rs:30:10 + | +LL | let x: Box = Box::new(Box::new(1)); + | - binding `x` declared here +LL | let wrong: &'static Payload = make_producer(&x).produce(); + | ---------------- -- borrow of `x` occurs here + | | + | type annotation requires that `x` is borrowed for `'static` +LL | +LL | drop(x); + | ^ move out of `x` occurs here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let wrong: &'static Payload = make_producer(&x.clone()).produce(); + | ++++++++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0505, E0597. +For more information about an error, try `rustc --explain E0505`. diff --git a/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.polonius.stderr b/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.polonius.stderr new file mode 100644 index 0000000000000..afe7bd972c935 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.polonius.stderr @@ -0,0 +1,35 @@ +error[E0597]: `x` does not live long enough + --> $DIR/rpit-static-raw-ptr-160979.rs:28:49 + | +LL | let x: Box = Box::new(Box::new(1)); + | - binding `x` declared here +LL | let wrong: &'static Payload = make_producer(&x).produce(); + | ---------------- ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +... +LL | } + | - `x` dropped here while still borrowed + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/rpit-static-raw-ptr-160979.rs:30:10 + | +LL | let x: Box = Box::new(Box::new(1)); + | - binding `x` declared here +LL | let wrong: &'static Payload = make_producer(&x).produce(); + | ---------------- -- borrow of `x` occurs here + | | + | type annotation requires that `x` is borrowed for `'static` +LL | +LL | drop(x); + | ^ move out of `x` occurs here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let wrong: &'static Payload = make_producer(&x.clone()).produce(); + | ++++++++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0505, E0597. +For more information about an error, try `rustc --explain E0505`. diff --git a/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.rs b/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.rs new file mode 100644 index 0000000000000..458f869fa5af3 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-static-raw-ptr-160979.rs @@ -0,0 +1,33 @@ +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius +//@ [nll] compile-flags: -Zpolonius=off +//@ [polonius] compile-flags: -Zpolonius=next + +type Payload = Box; + +trait Produce<'a> { + fn produce(self) -> &'a Payload; +} + +fn make_producer<'a>(payload: &'a Payload) -> impl Produce<'a> + 'static { + struct Producer { + ptr: *const Payload, + } + impl<'a> Produce<'a> for Producer { + fn produce(self) -> &'a Payload { + unsafe { &*self.ptr } + } + } + + // SAFETY: a user of this can only produce a `&'a Payload` from it... right? + Producer { ptr: payload } +} + +fn main() { + let x: Box = Box::new(Box::new(1)); + let wrong: &'static Payload = make_producer(&x).produce(); + //~^ ERROR `x` does not live long enough + drop(x); + //~^ ERROR cannot move out of `x` because it is borrowed + println!("{wrong}"); +} diff --git a/tests/ui/borrowck/alias-liveness/rpit-static-use-bound.rs b/tests/ui/borrowck/alias-liveness/rpit-static-use-bound.rs new file mode 100644 index 0000000000000..7cd03e1d75305 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-static-use-bound.rs @@ -0,0 +1,19 @@ +//@ check-pass + +fn foo<'a>(x: &'a mut i32) -> impl Sized + use<'a> + 'static {} + +fn overlapping_mut() { + let i = &mut 1; + let x = foo(i); + let y = foo(i); +} + +fn live_past_borrow() { + let y; + { + let x = &mut 1; + y = foo(x); + } +} + +fn main() {} diff --git a/tests/ui/borrowck/alias-liveness/rpit-static.rs b/tests/ui/borrowck/alias-liveness/rpit-static.rs index 98209f5f3b9fd..a6b1def544383 100644 --- a/tests/ui/borrowck/alias-liveness/rpit-static.rs +++ b/tests/ui/borrowck/alias-liveness/rpit-static.rs @@ -1,5 +1,3 @@ -//@ check-pass - trait Captures<'a> {} impl Captures<'_> for T {} @@ -8,13 +6,13 @@ fn foo(x: &mut i32) -> impl Sized + Captures<'_> + 'static {} fn overlapping_mut() { let i = &mut 1; let x = foo(i); - let y = foo(i); + let y = foo(i); //~ ERROR cannot borrow `*i` as mutable more than once at a time } fn live_past_borrow() { let y; { - let x = &mut 1; + let x = &mut 1; //~ ERROR temporary value dropped while borrowed y = foo(x); } } diff --git a/tests/ui/borrowck/alias-liveness/rpit-static.stderr b/tests/ui/borrowck/alias-liveness/rpit-static.stderr new file mode 100644 index 0000000000000..28bf1c288debc --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/rpit-static.stderr @@ -0,0 +1,27 @@ +error[E0499]: cannot borrow `*i` as mutable more than once at a time + --> $DIR/rpit-static.rs:9:17 + | +LL | let x = foo(i); + | - first mutable borrow occurs here +LL | let y = foo(i); + | ^ second mutable borrow occurs here +LL | } + | - first borrow might be used here, when `x` is dropped and runs the destructor for type `impl Captures<'_> + 'static` + +error[E0716]: temporary value dropped while borrowed + --> $DIR/rpit-static.rs:15:22 + | +LL | let x = &mut 1; + | ^ creates a temporary value which is freed while still in use +LL | y = foo(x); +LL | } + | - temporary value is freed at the end of this statement +LL | } + | - borrow might be used here, when `y` is dropped and runs the destructor for type `impl Captures<'_> + 'static` + | + = note: consider using a `let` binding to create a longer lived value + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0499, E0716. +For more information about an error, try `rustc --explain E0499`. diff --git a/tests/ui/coroutine/resume-arg-outlives.rs b/tests/ui/coroutine/resume-arg-outlives.rs index 258be28e0631c..12f635afb70b4 100644 --- a/tests/ui/coroutine/resume-arg-outlives.rs +++ b/tests/ui/coroutine/resume-arg-outlives.rs @@ -22,6 +22,7 @@ fn main() { let local = String::from("..."); let mut coro = demo(&local); drop(local); + //~^ ERROR cannot move out of `local` because it is borrowed let _unrelated = String::from("UAF"); coro.as_mut().resume(""); } diff --git a/tests/ui/coroutine/resume-arg-outlives.stderr b/tests/ui/coroutine/resume-arg-outlives.stderr index 045c77e8d054c..ce390f7ca7b20 100644 --- a/tests/ui/coroutine/resume-arg-outlives.stderr +++ b/tests/ui/coroutine/resume-arg-outlives.stderr @@ -17,5 +17,24 @@ LL - fn demo<'not_static>(s: &'not_static str) -> Pin(s: &'static str) -> Pin + 'static>> { | -error: aborting due to 1 previous error +error[E0505]: cannot move out of `local` because it is borrowed + --> $DIR/resume-arg-outlives.rs:24:10 + | +LL | let local = String::from("..."); + | ----- binding `local` declared here +LL | let mut coro = demo(&local); + | ------ borrow of `local` occurs here +LL | drop(local); + | ^^^^^ move out of `local` occurs here +... +LL | coro.as_mut().resume(""); + | ---- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let mut coro = demo(&local.clone()); + | ++++++++ + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0505`.