Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stop replacing bivariant args with 'static when computing closure requirements #133798

Merged
merged 4 commits into from
Dec 4, 2024
Merged
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
51 changes: 8 additions & 43 deletions compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -973,25 +973,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
propagated_outlives_requirements: &mut Vec<ClosureOutlivesRequirement<'tcx>>,
) -> bool {
let tcx = infcx.tcx;

let TypeTest { generic_kind, lower_bound, span: _, verify_bound: _ } = type_test;
let TypeTest { generic_kind, lower_bound, span: blame_span, ref verify_bound } = *type_test;

let generic_ty = generic_kind.to_ty(tcx);
let Some(subject) = self.try_promote_type_test_subject(infcx, generic_ty) else {
return false;
};

debug!("subject = {:?}", subject);

let r_scc = self.constraint_sccs.scc(*lower_bound);

let r_scc = self.constraint_sccs.scc(lower_bound);
debug!(
"lower_bound = {:?} r_scc={:?} universe={:?}",
lower_bound,
r_scc,
self.constraint_sccs.annotation(r_scc).min_universe()
);

// If the type test requires that `T: 'a` where `'a` is a
// placeholder from another universe, that effectively requires
// `T: 'static`, so we have to propagate that requirement.
Expand All @@ -1004,7 +999,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
propagated_outlives_requirements.push(ClosureOutlivesRequirement {
subject,
outlived_free_region: static_r,
blame_span: type_test.span,
blame_span,
category: ConstraintCategory::Boring,
});

Expand All @@ -1031,12 +1026,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// where `ur` is a local bound -- we are sometimes in a
// position to prove things that our caller cannot. See
// #53570 for an example.
if self.eval_verify_bound(infcx, generic_ty, ur, &type_test.verify_bound) {
if self.eval_verify_bound(infcx, generic_ty, ur, &verify_bound) {
continue;
}

let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur);
debug!("try_promote_type_test: non_local_ub={:?}", non_local_ub);
debug!(?non_local_ub);

// This is slightly too conservative. To show T: '1, given `'2: '1`
// and `'3: '1` we only need to prove that T: '2 *or* T: '3, but to
Expand All @@ -1049,10 +1044,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let requirement = ClosureOutlivesRequirement {
subject,
outlived_free_region: upper_bound,
blame_span: type_test.span,
blame_span,
category: ConstraintCategory::Boring,
};
debug!("try_promote_type_test: pushing {:#?}", requirement);
debug!(?requirement, "adding closure requirement");
propagated_outlives_requirements.push(requirement);
}
}
Expand All @@ -1063,44 +1058,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// variables in the type `T` with an equal universal region from the
/// closure signature.
/// This is not always possible, so this is a fallible process.
#[instrument(level = "debug", skip(self, infcx))]
#[instrument(level = "debug", skip(self, infcx), ret)]
fn try_promote_type_test_subject(
&self,
infcx: &InferCtxt<'tcx>,
ty: Ty<'tcx>,
) -> Option<ClosureOutlivesSubject<'tcx>> {
let tcx = infcx.tcx;

// Opaque types' args may include useless lifetimes.
// We will replace them with ReStatic.
struct OpaqueFolder<'tcx> {
tcx: TyCtxt<'tcx>,
}
impl<'tcx> ty::TypeFolder<TyCtxt<'tcx>> for OpaqueFolder<'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
use ty::TypeSuperFoldable as _;
let tcx = self.tcx;
let &ty::Alias(ty::Opaque, ty::AliasTy { args, def_id, .. }) = t.kind() else {
return t.super_fold_with(self);
};
let args = std::iter::zip(args, tcx.variances_of(def_id)).map(|(arg, v)| {
match (arg.unpack(), v) {
(ty::GenericArgKind::Lifetime(_), ty::Bivariant) => {
tcx.lifetimes.re_static.into()
}
_ => arg.fold_with(self),
}
});
Ty::new_opaque(tcx, def_id, tcx.mk_args_from_iter(args))
}
}

let ty = ty.fold_with(&mut OpaqueFolder { tcx });
let mut failed = false;

let ty = fold_regions(tcx, ty, |r, _depth| {
let r_vid = self.to_region_vid(r);
let r_scc = self.constraint_sccs.scc(r_vid);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// This example broke while refactoring the way closure
// requirements are handled. The setup here matches
// `thread::scope`.

//@ check-pass

struct Outlives<'hr, 'scope: 'hr>(*mut (&'scope (), &'hr ()));
impl<'hr, 'scope> Outlives<'hr, 'scope> {
fn outlives_hr<T: 'hr>(self) {}
}

fn takes_closure_implied_bound<'scope>(f: impl for<'hr> FnOnce(Outlives<'hr, 'scope>)) {}

fn requires_external_outlives_hr<T>() {
// implied bounds:
// - `T: 'scope` as `'scope` is local to this function
// - `'scope: 'hr` as it's an implied bound of `Outlives`
//
// need to prove `T: 'hr` :>
takes_closure_implied_bound(|proof| proof.outlives_hr::<T>());
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// This example incorrectly compiled while refactoring the way
// closure requirements are handled.

struct Outlives<'hr: 'scope, 'scope>(*mut (&'scope (), &'hr ()));
impl<'hr, 'scope> Outlives<'hr, 'scope> {
fn outlives_hr<T: 'hr>(self) {}
}

fn takes_closure_implied_bound<'scope>(f: impl for<'hr> FnOnce(Outlives<'hr, 'scope>)) {}

fn requires_external_outlives_hr<T>() {
// implied bounds:
// - `T: 'scope` as `'scope` is local to this function
// - `'hr: 'scope` as it's an implied bound of `Outlives`
//
// need to prove `T: 'hr` :<
takes_closure_implied_bound(|proof| proof.outlives_hr::<T>());
//~^ ERROR the parameter type `T` may not live long enough
}

fn main() {}
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/thread_scope_incorrect_implied_bound.rs:17:47
|
LL | takes_closure_implied_bound(|proof| proof.outlives_hr::<T>());
| ^^^^^^^^^^^
| |
| 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 requires_external_outlives_hr<T: 'static>() {
| +++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0310`.
2 changes: 0 additions & 2 deletions tests/ui/nll/ty-outlives/projection-implied-bounds.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Test that we can deduce when projections like `T::Item` outlive the
// function body. Test that this does not imply that `T: 'a` holds.

//@ compile-flags:-Zverbose-internals

use std::cell::Cell;

fn twice<F, T>(mut value: T, mut f: F)
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/nll/ty-outlives/projection-implied-bounds.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/projection-implied-bounds.rs:30:36
--> $DIR/projection-implied-bounds.rs:28:36
|
LL | twice(value, |value_ref, item| invoke2(value_ref, item));
| ^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
Loading