diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index c4d964441b1d2..ba4cd736e8353 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -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 } diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index aeba5ee70cf17..95fbc8095c549 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -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::{ @@ -442,6 +443,46 @@ impl<'tcx> UniversalRegions<'tcx> { pub(crate) fn encountered_re_error(&self) -> Option { 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. + true + } + } + } } struct UniversalRegionsBuilder<'infcx, 'tcx> { diff --git a/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr b/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr index 7b840d54ed038..ba26774814e37 100644 --- a/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr +++ b/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr @@ -10,20 +10,18 @@ LL | force_send(async_load(¬_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(¬_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) {} diff --git a/tests/ui/implied-bounds/implied-bounds-on-external-params-1.rs b/tests/ui/implied-bounds/implied-bounds-on-external-params-1.rs new file mode 100644 index 0000000000000..c21cb5f8e058b --- /dev/null +++ b/tests/ui/implied-bounds/implied-bounds-on-external-params-1.rs @@ -0,0 +1,29 @@ +// A regression test for https://github.com/rust-lang/rust/issues/151637. + +struct Wrap(T); + +fn error1(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(x: T) { + || Wrap(x); + //~^ ERROR: the parameter type `T` may not live long enough +} + +fn no_error1(x: T) { + || Wrap(x); +} + +fn no_error2(x: T, _: &'static T) { + || Wrap(x); +} + +fn main() {} diff --git a/tests/ui/implied-bounds/implied-bounds-on-external-params-1.stderr b/tests/ui/implied-bounds/implied-bounds-on-external-params-1.stderr new file mode 100644 index 0000000000000..abcd947df179f --- /dev/null +++ b/tests/ui/implied-bounds/implied-bounds-on-external-params-1.stderr @@ -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(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(x: T) { + | +++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/implied-bounds/implied-bounds-on-external-params-2.rs b/tests/ui/implied-bounds/implied-bounds-on-external-params-2.rs new file mode 100644 index 0000000000000..fffc4dd8351d9 --- /dev/null +++ b/tests/ui/implied-bounds/implied-bounds-on-external-params-2.rs @@ -0,0 +1,60 @@ +// A regression test for https://github.com/rust-lang/rust/issues/151637. + +type Payload = Box; + +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); + +trait ToStatic { + fn to_static(self) -> &'static Payload; +} +impl ToStatic for Wrap { + fn to_static(self) -> &'static Payload { + self.0.static_to_static() // Legal. T: 'static is implied by Wrap + } +} + +// Trait to allow mentioning FnOnce without mentioning the return type directly +trait MyFnOnce { + type MyOutput; + fn my_call_once(self) -> Self::MyOutput; +} +impl T, T> MyFnOnce for F { + type MyOutput = T; + fn my_call_once(self) -> T { + self() + } +} + +fn call>(f: F) -> &'static Payload { + f.my_call_once().to_static() +} + +fn extend(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}"); +} diff --git a/tests/ui/implied-bounds/implied-bounds-on-external-params-2.stderr b/tests/ui/implied-bounds/implied-bounds-on-external-params-2.stderr new file mode 100644 index 0000000000000..6d70523c962ca --- /dev/null +++ b/tests/ui/implied-bounds/implied-bounds-on-external-params-2.stderr @@ -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(x: T) -> &'static Payload { + | +++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0310`.