Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,12 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
debug!(?bounds, ?constraints);
// Because of #109628, we may have unexpected placeholders. Ignore them!
// FIXME(#109628): panic in this case once the issue is fixed.
let bounds = bounds.into_iter().filter(|bound| !bound.has_placeholders());
let bounds: Vec<_> = bounds
.into_iter()
.filter(|bound| {
!bound.has_placeholders() && self.universal_regions.bound_can_be_implied(*bound)
})
.collect();
self.add_outlives_bounds(bounds);
constraints
}
Expand Down
41 changes: 41 additions & 0 deletions compiler/rustc_borrowck/src/universal_regions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_index::IndexVec;
use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_infer::traits::query::OutlivesBound;
use rustc_macros::extension;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{
Expand Down Expand Up @@ -442,6 +443,46 @@ impl<'tcx> UniversalRegions<'tcx> {
pub(crate) fn encountered_re_error(&self) -> Option<ErrorGuaranteed> {
self.indices.encountered_re_error.get()
}

/// Whether the bound can be added as an implied bound to this function. If the defining type
/// cannot have external regions, i.e. non of {closures, coroutines, inline consts}, every
/// bounds returned from `type_op::ImpliedOutlivesBounds` are allowed.
/// But if the defining type can have them, the bound should contain at least one local region.
pub(crate) fn bound_can_be_implied(&self, bound: OutlivesBound<'tcx>) -> bool {
if !matches!(
self.defining_ty,
DefiningTy::Closure(..)
| DefiningTy::Coroutine(..)
| DefiningTy::CoroutineClosure(..)
| DefiningTy::InlineConst(..)
) {
return true;
}

match bound {
OutlivesBound::RegionSubRegion(r1, r2) => {
self.is_local_free_region(self.to_region_vid(r1))
|| self.is_local_free_region(self.to_region_vid(r2))
}
OutlivesBound::RegionSubParam(r_a, _) => {
self.is_local_free_region(self.to_region_vid(r_a))
}
OutlivesBound::RegionSubAlias(..) => {
// FIXME: We might need to visit regions to check whether there exists
// any local free region here, but that would result in a false positive
// borrowck errors in some cases, such as:
// `fn foo<'a, 'b: 'b, T>(x: &'a T) -> impl Trait + 'a { /* nothing mentions 'b */ }`
// Suppose that we have a local bound to a return type of the above function
// and that local is captured into a closure. If the closure requires some
// outlives bounds on the captured opaque but we refuse the implied outlives bound
// for it, we might fail with the type test for it, which contains the opaque type
// and therefore `'b` as well. The problem is that if the normalized opaque type doesn't
// mentions `'b`, we have no local free region constrained by it, and end up
// emitting a borrowck error instead of propagating the closure requirements.
Copy link
Member Author

@ShoyuVanilla ShoyuVanilla Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filtering external bounds here regresses this

.map(|&n| Container::new(n, &resolver))

true
}
}
}
}

struct UniversalRegionsBuilder<'infcx, 'tcx> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,18 @@ LL | force_send(async_load(&not_static));
error[E0597]: `not_static` does not live long enough
--> $DIR/implementation-not-general-enough-ice-133252.rs:9:31
|
LL | async {
| - return type of async block is &(dyn Owned + '1)
LL | let not_static = 0;
| ---------- binding `not_static` declared here
LL | force_send(async_load(&not_static));
| ----------------------^^^^^^^^^^^--
| | |
| | borrowed value does not live long enough
| argument requires that `not_static` is borrowed for `'1`
| argument requires that `not_static` is borrowed for `'static`
...
LL | }
| - `not_static` dropped here while still borrowed
|
note: requirement that the value outlives `'1` introduced here
note: requirement that the value outlives `'static` introduced here
--> $DIR/implementation-not-general-enough-ice-133252.rs:16:18
|
LL | fn force_send<T: Send>(_: T) {}
Expand Down
29 changes: 29 additions & 0 deletions tests/ui/implied-bounds/implied-bounds-on-external-params-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// A regression test for https://github.com/rust-lang/rust/issues/151637.

struct Wrap<T: 'static>(T);

fn error1<T>(x: T) {
Wrap(x);
//~^ ERROR: the parameter type `T` may not live long enough
}

// We used to add implied bound `T: 'static` from the closure's return type
// when borrow checking the closure, which resulted in allowing non-wf return
// type. Implied bounds which contains only the external regions or type params
// from the parents should not be implied. That would make those bounds in needs
// propagated as closure requirements and either proved or make borrowck error
// from the parent's body.
fn error2<T>(x: T) {
|| Wrap(x);
//~^ ERROR: the parameter type `T` may not live long enough
}

fn no_error1<T: 'static>(x: T) {
|| Wrap(x);
}

fn no_error2<T>(x: T, _: &'static T) {
|| Wrap(x);
}

fn main() {}
31 changes: 31 additions & 0 deletions tests/ui/implied-bounds/implied-bounds-on-external-params-1.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/implied-bounds-on-external-params-1.rs:6:5
|
LL | Wrap(x);
| ^^^^^^^
| |
| the parameter type `T` must be valid for the static lifetime...
| ...so that the type `T` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound
|
LL | fn error1<T: 'static>(x: T) {
| +++++++++

error[E0310]: the parameter type `T` may not live long enough
--> $DIR/implied-bounds-on-external-params-1.rs:17:8
|
LL | || Wrap(x);
| ^^^^^^^
| |
| the parameter type `T` must be valid for the static lifetime...
| ...so that the type `T` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound
|
LL | fn error2<T: 'static>(x: T) {
| +++++++++

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0310`.
60 changes: 60 additions & 0 deletions tests/ui/implied-bounds/implied-bounds-on-external-params-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// A regression test for https://github.com/rust-lang/rust/issues/151637.

type Payload = Box<i32>;

trait StaticToStatic {
fn static_to_static(self) -> &'static Payload
where
Self: 'static;
}
impl<'a> StaticToStatic for &'a Payload {
fn static_to_static(self) -> &'static Payload
where
Self: 'static,
{
self // Legal. 'a must be 'static due to Self: 'static
}
}

struct Wrap<T: StaticToStatic + 'static>(T);

trait ToStatic {
fn to_static(self) -> &'static Payload;
}
impl<T: StaticToStatic> ToStatic for Wrap<T> {
fn to_static(self) -> &'static Payload {
self.0.static_to_static() // Legal. T: 'static is implied by Wrap<T>
}
}

// Trait to allow mentioning FnOnce without mentioning the return type directly
trait MyFnOnce {
type MyOutput;
fn my_call_once(self) -> Self::MyOutput;
}
impl<F: FnOnce() -> T, T> MyFnOnce for F {
type MyOutput = T;
fn my_call_once(self) -> T {
self()
}
}

fn call<F: MyFnOnce<MyOutput: ToStatic>>(f: F) -> &'static Payload {
f.my_call_once().to_static()
}

fn extend<T: StaticToStatic>(x: T) -> &'static Payload {
let c = move || {
// Probably should be illegal, since Wrap requires T: 'static
Wrap(x)
//~^ ERROR: the parameter type `T` may not live long enough
};
call(c)
}

fn main() {
let x = Box::new(Box::new(1));
let y = extend(&*x);
drop(x);
println!("{y}");
}
17 changes: 17 additions & 0 deletions tests/ui/implied-bounds/implied-bounds-on-external-params-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/implied-bounds-on-external-params-2.rs:49:9
|
LL | Wrap(x)
| ^^^^^^^
| |
| the parameter type `T` must be valid for the static lifetime...
| ...so that the type `T` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound
|
LL | fn extend<T: StaticToStatic + 'static>(x: T) -> &'static Payload {
| +++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0310`.
Loading