Skip to content
Open
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
7 changes: 7 additions & 0 deletions compiler/rustc_borrowck/src/diagnostics/region_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ impl std::fmt::Debug for RegionErrors<'_> {
pub(crate) enum RegionErrorKind<'tcx> {
/// A generic bound failure for a type test (`T: 'a`).
TypeTestError { type_test: TypeTest<'tcx> },
/// A solver outlives constraint could not be satisfied.
BoundVerificationError { span: Span },

/// 'p outlives 'r, which does not hold. 'p is always a placeholder
/// and 'r is some other region.
Expand Down Expand Up @@ -308,6 +310,11 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> {
let mut outlives_suggestion = OutlivesSuggestionBuilder::default();
for (nll_error, _) in nll_errors.into_iter() {
match nll_error {
RegionErrorKind::BoundVerificationError { span } => {
self.buffer_error(
self.dcx().struct_span_err(span, "unable to satisfy outlives constraints"),
);
}
RegionErrorKind::TypeTestError { type_test } => {
// Try to convert the lower-bound region into something named we can print for
// the user.
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_borrowck/src/handle_placeholders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub(crate) struct LoweredConstraints<'tcx> {
pub(crate) scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
pub(crate) outlives_constraints: Frozen<OutlivesConstraintSet<'tcx>>,
pub(crate) type_tests: Vec<TypeTest<'tcx>>,
pub(crate) verify_bounds: Vec<rustc_infer::infer::region_constraints::VerifyBoundCheck<'tcx>>,
pub(crate) liveness_constraints: LivenessValues,
pub(crate) universe_causes: FxIndexMap<UniverseIndex, UniverseInfo<'tcx>>,
pub(crate) placeholder_indices: PlaceholderIndices<'tcx>,
Expand Down Expand Up @@ -246,7 +247,10 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
mut outlives_constraints,
universe_causes,
type_tests,
verify_bounds,
solver_region_constraints,
} = constraints;
assert!(solver_region_constraints.is_empty(), "unconverted solver region constraints");

let fr_static = universal_regions.fr_static;
let compute_sccs =
Expand All @@ -269,6 +273,7 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(

return LoweredConstraints {
type_tests,
verify_bounds,
constraint_sccs,
scc_annotations: scc_annotations.scc_to_annotation,
definitions,
Expand Down Expand Up @@ -307,6 +312,7 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
scc_annotations,
outlives_constraints: Frozen::freeze(outlives_constraints),
type_tests,
verify_bounds,
liveness_constraints,
universe_causes,
placeholder_indices,
Expand Down
15 changes: 12 additions & 3 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ pub struct ClosureRegionRequirements<'tcx> {
/// Requirements between the various free regions defined in
/// indices.
pub outlives_requirements: Vec<ClosureOutlivesRequirement<'tcx>>,

/// Each group requires one of its alternative conjunctions to hold.
pub(crate) outlives_alternatives: Vec<Vec<Vec<ClosureOutlivesRequirement<'tcx>>>>,
}

/// Indicates an outlives-constraint between a type or between two
Expand Down Expand Up @@ -407,15 +410,21 @@ fn borrowck_check_region_constraints<'diag, 'tcx>(
location_table,
location_map,
universal_region_relations,
region_bound_pairs: _,
known_type_outlives_obligations: _,
constraints,
region_bound_pairs,
known_type_outlives_obligations,
mut constraints,
deferred_closure_requirements,
deferred_opaque_type_errors,
polonius_facts,
polonius_context,
}: CollectRegionConstraintsResult<'tcx>,
) -> PropagatedBorrowCheckResults<'tcx> {
constraints.flush_solver_region_constraints(
&infcx,
&universal_region_relations,
&region_bound_pairs,
&known_type_outlives_obligations,
);
assert!(!infcx.has_opaque_types_in_storage());
assert!(deferred_closure_requirements.is_empty());
let tcx = root_cx.tcx;
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_borrowck/src/nll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,13 @@ pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>(
) -> Option<ClosureRegionRequirements<'tcx>> {
// FIXME(#146079): we shouldn't have to clone all this stuff here.
// Computing the region graph should take at least some of it by reference/`Rc`.
let mut constraints = constraints.clone();
// Standalone alternatives do not produce closure requirements. Check them
// only after opaque equalities have been applied; the original constraints
// retain them for the final solve.
constraints.verify_bounds.clear();
let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints(
constraints.clone(),
constraints,
&universal_region_relations,
infcx,
);
Expand Down
150 changes: 149 additions & 1 deletion compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ pub struct RegionInferenceContext<'tcx> {

/// Type constraints that we check after solving.
type_tests: Vec<TypeTest<'tcx>>,
verify_bounds: Vec<rustc_infer::infer::region_constraints::VerifyBoundCheck<'tcx>>,

/// Information about how the universally quantified regions in
/// scope on this function relate to one another.
Expand Down Expand Up @@ -230,6 +231,12 @@ impl fmt::Debug for TypeTest<'_> {
write!(f, "]")
}
VerifyBound::IsEmpty => write!(f, "Empty({lower:?})"),
VerifyBound::RegionOutlives(ty::OutlivesClause(sup, sub)) => {
write!(f, "{sup:?}: {sub:?}")
}
VerifyBound::TypeOutlives { subject, region, bound } => {
write!(f, "TypeOutlives({subject:?}, {region:?}, {bound:?})")
}
}
}
write!(f, "TypeTest from {:?}[", self.span)?;
Expand Down Expand Up @@ -343,6 +350,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
outlives_constraints,
scc_annotations,
type_tests,
verify_bounds,
liveness_constraints,
universe_causes,
placeholder_indices,
Expand Down Expand Up @@ -405,6 +413,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
universe_causes,
scc_values,
type_tests,
verify_bounds,
universal_region_relations,
}
}
Expand Down Expand Up @@ -490,8 +499,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// eagerly erroing.
let mut propagated_outlives_requirements =
infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new);
let mut outlives_alternatives = Vec::new();

self.check_type_tests(infcx, propagated_outlives_requirements.as_mut(), &mut errors_buffer);
for check in &self.verify_bounds {
if !self.eval_verify_bound(
infcx,
infcx.tcx.types.unit,
self.universal_regions().fr_static,
&check.bound,
) {
if propagated_outlives_requirements.is_some()
&& let Some(alternatives) =
self.promote_verify_bound(infcx, &check.bound, check.span)
{
outlives_alternatives.push(alternatives);
} else {
errors_buffer
.push(RegionErrorKind::BoundVerificationError { span: check.span });
}
}
}

debug!(?errors_buffer);
debug!(?propagated_outlives_requirements);
Expand All @@ -518,14 +546,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {

let propagated_outlives_requirements = propagated_outlives_requirements.unwrap_or_default();

if propagated_outlives_requirements.is_empty() {
if propagated_outlives_requirements.is_empty() && outlives_alternatives.is_empty() {
(None, errors_buffer)
} else {
let num_external_vids = self.universal_regions().num_global_and_external_regions();
(
Some(ClosureRegionRequirements {
num_external_vids,
outlives_requirements: propagated_outlives_requirements,
outlives_alternatives,
}),
errors_buffer,
)
Expand Down Expand Up @@ -809,6 +838,119 @@ impl<'tcx> RegionInferenceContext<'tcx> {
Some(ClosureOutlivesSubject::Ty(ClosureOutlivesSubjectTy::bind(tcx, ty)))
}

fn promote_verify_bound(
&self,
infcx: &InferCtxt<'tcx>,
bound: &VerifyBound<'tcx>,
span: Span,
) -> Option<Vec<Vec<ClosureOutlivesRequirement<'tcx>>>> {
if self.eval_verify_bound(
infcx,
infcx.tcx.types.unit,
self.universal_regions().fr_static,
bound,
) {
return Some(vec![vec![]]);
}
match bound {
VerifyBound::AnyBound(bounds) => {
let alternatives: Vec<_> = bounds
.iter()
.filter_map(|bound| self.promote_verify_bound(infcx, bound, span))
.flatten()
.collect();
(!alternatives.is_empty()).then_some(alternatives)
}
VerifyBound::AllBounds(bounds) => {
let mut alternatives = vec![vec![]];
for bound in bounds {
let next = self.promote_verify_bound(infcx, bound, span)?;
alternatives = alternatives
.into_iter()
.flat_map(|prefix| {
next.iter()
.map(move |suffix| prefix.iter().chain(suffix).copied().collect())
})
.collect();
}
Some(alternatives)
}
VerifyBound::TypeOutlives { subject, region, bound } => {
let generic_kind = match *subject.kind() {
ty::Param(param) => GenericKind::Param(param),
ty::Placeholder(placeholder) => GenericKind::Placeholder(placeholder),
ty::Alias(_, alias) => GenericKind::Alias(alias),
_ => return None,
};
let mut requirements = Vec::new();
self.try_promote_type_test(
infcx,
&TypeTest {
generic_kind,
lower_bound: self.to_region_vid(*region),
span,
verify_bound: (**bound).clone(),
},
&mut requirements,
)
.then_some(vec![requirements])
}
VerifyBound::RegionOutlives(ty::OutlivesClause(sup, sub)) => {
let sup = self.to_region_vid(*sup);
let sub = self.to_region_vid(*sub);
let sup_scc = self.constraint_sccs.scc(sup);
let sub_scc = self.constraint_sccs.scc(sub);
let lower: FxIndexSet<_> = self
.scc_values
.universal_regions_outlived_by(sup_scc)
.flat_map(|region| {
self.universal_region_relations.non_local_lower_bounds(region)
})
.collect();
if lower.is_empty() {
return None;
}
let upper: Vec<_> =
if self.scc_values.placeholders_contained_in(sub_scc).next().is_some() {
vec![vec![self.universal_regions().fr_static]]
} else {
self.scc_values
.universal_regions_outlived_by(sub_scc)
.map(|region| {
self.universal_region_relations.non_local_upper_bounds(region)
})
.collect()
};
let mut alternatives = vec![vec![]];
for upper in upper {
let next: Vec<_> = lower
.iter()
.flat_map(|&sup| {
upper.iter().map(move |&sub| ClosureOutlivesRequirement {
subject: ClosureOutlivesSubject::Region(sup),
outlived_free_region: sub,
blame_span: span,
category: ConstraintCategory::Boring,
})
})
.collect();
alternatives = alternatives
.into_iter()
.flat_map(|prefix| {
next.iter().map(move |&requirement| {
let mut requirements = prefix.clone();
requirements.push(requirement);
requirements
})
})
.collect();
}
Some(alternatives)
}
VerifyBound::IfEq(_) | VerifyBound::IsEmpty | VerifyBound::OutlivedBy(_) => None,
}
}

/// Like `universal_upper_bound`, but returns an approximation more suitable
/// for diagnostics. If `r` contains multiple disjoint universal regions
/// (e.g. 'a and 'b in `fn foo<'a, 'b> { ... }`, we pick the lower-numbered region.
Expand Down Expand Up @@ -893,6 +1035,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| {
self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound)
}),
VerifyBound::RegionOutlives(ty::OutlivesClause(sup, sub)) => {
self.eval_outlives(self.to_region_vid(*sup), self.to_region_vid(*sub))
}
VerifyBound::TypeOutlives { subject, region, bound } => {
self.eval_verify_bound(infcx, *subject, self.to_region_vid(*region), bound)
}
}
}

Expand Down
15 changes: 13 additions & 2 deletions compiler/rustc_borrowck/src/root_cx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ impl<'diag, 'tcx> BorrowCheckRootCtxt<'diag, 'tcx> {
fn handle_opaque_type_uses(&mut self) {
let mut per_body_info = Vec::new();
for (def_id, input) in &mut self.collect_region_constraints_results {
Self::flush_solver_region_constraints(input);
let (num_entries, opaque_types) = clone_and_resolve_opaque_types(
&input.infcx,
&input.universal_region_relations,
Expand Down Expand Up @@ -220,7 +221,7 @@ impl<'diag, 'tcx> BorrowCheckRootCtxt<'diag, 'tcx> {
// and write its result into `propagated_borrowck_results`.
if depends_on_opaques {
if def_id != self.root_def_id {
let req = Self::compute_closure_requirements_modulo_opaques(&input);
let req = Self::compute_closure_requirements_modulo_opaques(&mut input);
closure_requirements_modulo_opaques.insert(def_id, req);
}
self.collect_region_constraints_results.insert(def_id, input);
Expand All @@ -233,8 +234,9 @@ impl<'diag, 'tcx> BorrowCheckRootCtxt<'diag, 'tcx> {
}

fn compute_closure_requirements_modulo_opaques(
input: &CollectRegionConstraintsResult<'tcx>,
input: &mut CollectRegionConstraintsResult<'tcx>,
) -> Option<ClosureRegionRequirements<'tcx>> {
Self::flush_solver_region_constraints(input);
compute_closure_requirements_modulo_opaques(
&input.infcx,
&input.body_owned,
Expand All @@ -244,6 +246,15 @@ impl<'diag, 'tcx> BorrowCheckRootCtxt<'diag, 'tcx> {
)
}

fn flush_solver_region_constraints(input: &mut CollectRegionConstraintsResult<'tcx>) {
input.constraints.flush_solver_region_constraints(
&input.infcx,
&input.universal_region_relations,
&input.region_bound_pairs,
&input.known_type_outlives_obligations,
);
}

fn apply_closure_requirements(
input: &mut CollectRegionConstraintsResult<'tcx>,
closure_requirements: &Option<ClosureRegionRequirements<'tcx>>,
Expand Down
Loading
Loading