Skip to content

Commit d08cb3d

Browse files
committed
Pick the first small-universed region, this fixes two bugs
1 parent e45630d commit d08cb3d

File tree

4 files changed

+116
-52
lines changed

4 files changed

+116
-52
lines changed

compiler/rustc_borrowck/src/eliminate_placeholders.rs

Lines changed: 101 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
//! This logic is provisional and should be removed once the trait
66
//! solver can handle this kind of constraint.
77
8+
use std::collections::VecDeque;
9+
810
use rustc_data_structures::fx::FxHashSet;
911
use rustc_data_structures::graph::scc::{self, Sccs};
1012
use rustc_index::IndexVec;
@@ -13,6 +15,7 @@ use rustc_infer::infer::region_constraints::{VarInfos, VerifyBound};
1315
use rustc_middle::ty::{Region, RegionVid, TyCtxt, UniverseIndex};
1416
use tracing::{debug, instrument};
1517

18+
use crate::constraints::graph::{ConstraintGraph, Normal};
1619
use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet};
1720
use crate::diagnostics::RegionErrorKind;
1821
use crate::member_constraints::MemberConstraintSet;
@@ -105,9 +108,15 @@ struct RegionTracker {
105108

106109
impl scc::Annotation for RegionTracker {
107110
fn merge_scc(self, other: Self) -> Self {
111+
debug!("{:?} << {:?}", self.representative, other.representative);
112+
let min_universe = if other.min_universe.0 < self.min_universe.0 {
113+
other.min_universe
114+
} else {
115+
self.min_universe
116+
};
108117
Self {
109118
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
110-
min_universe: self.min_universe.min(other.min_universe),
119+
min_universe,
111120
representative: self.representative.merge_scc(other.representative),
112121
}
113122
}
@@ -150,39 +159,6 @@ impl RegionTracker {
150159
self.min_universe.0
151160
}
152161

153-
/// Figure out if there is a universe violation going on.
154-
/// This can happen in two cases: either one of our placeholders
155-
/// had its universe lowered from reaching a region with a lower universe,
156-
/// (in which case we blame the lower universe's region), or because we reached
157-
/// a larger universe (in which case we blame the larger universe's region).
158-
fn universe_violation(&self) -> Option<RegionVid> {
159-
let PlaceholderReachability::Placeholders { max_universe: (max_u, max_u_rvid), .. } =
160-
self.reachable_placeholders
161-
else {
162-
return None;
163-
};
164-
165-
let (min_u, min_u_rvid) = self.min_universe;
166-
167-
if min_u.can_name(max_u) {
168-
return None;
169-
}
170-
171-
debug!("Universe {max_u:?} is too large for its SCC!");
172-
let to_blame = if self.representative.rvid() == max_u_rvid {
173-
// We originally had a large enough universe to fit all our reachable
174-
// placeholders, but had it lowered because we also reached something
175-
// small-universed. In this case, that's to blame!
176-
debug!("{min_u_rvid:?} lowered our universe to {min_u:?}");
177-
min_u_rvid
178-
} else {
179-
// The problem is that we, who have a small universe, reach a large one.
180-
max_u_rvid
181-
};
182-
183-
Some(to_blame)
184-
}
185-
186162
/// Determine if this SCC reaches a placeholder that isn't `placeholder_rvid`,
187163
/// returning it if that is the case. This prefers the placeholder with the
188164
/// smallest region variable ID.
@@ -250,6 +226,7 @@ impl scc::Annotations<RegionVid, ConstraintSccIndex, Representative>
250226
}
251227
}
252228

229+
#[instrument(skip(definitions, sccs, annotations), ret, level = "debug")]
253230
fn find_placeholder_mismatch_errors<'tcx>(
254231
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
255232
sccs: &Sccs<RegionVid, ConstraintSccIndex>,
@@ -397,7 +374,7 @@ pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>(
397374
let sccs = outlives_constraints.compute_sccs(fr_static, definitions.len(), &mut annotations);
398375

399376
let outlives_static =
400-
rewrite_outlives(&sccs, &annotations, fr_static, &mut outlives_constraints);
377+
rewrite_outlives(&sccs, &annotations, fr_static, &mut outlives_constraints, &definitions);
401378

402379
let placeholder_errors = find_placeholder_mismatch_errors(&definitions, &sccs, &annotations);
403380

@@ -467,35 +444,112 @@ fn rewrite_outlives<'tcx>(
467444
annotations: &SccAnnotations<'_, '_, RegionTracker>,
468445
fr_static: RegionVid,
469446
outlives_constraints: &mut OutlivesConstraintSet<'tcx>,
447+
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
470448
) -> FxHashSet<ConstraintSccIndex> {
471449
// Is this SCC already outliving static directly or transitively?
472450
let mut outlives_static = FxHashSet::default();
473451

452+
let mut memoised_constraint_graph: Option<ConstraintGraph<Normal>> = None;
453+
474454
for scc in sccs.all_sccs() {
475455
let annotation: RegionTracker = annotations.scc_to_annotation[scc];
476456
if scc == sccs.scc(fr_static) {
477457
// No use adding 'static: 'static.
478458
continue;
479459
}
480460

481-
// If this SCC participates in a universe violation
482-
// e.g. if it reaches a region with a universe smaller than
483-
// the largest region reached, add a requirement that it must
484-
// outlive `'static`. Here we get to know which reachable region
485-
// caused the violation.
486-
if let Some(to) = annotation.universe_violation() {
487-
outlives_static.insert(scc);
488-
outlives_constraints.add_placeholder_violation_constraint(
489-
annotation.representative_rvid(),
490-
annotation.representative_rvid(),
491-
to,
461+
// Figure out if there is a universe violation in this SCC.
462+
// This can happen in two cases: either one of our placeholders
463+
// had its universe lowered from reaching a region with a lower universe,
464+
// (in which case we blame the lower universe's region), or because we reached
465+
// a larger universe (in which case we blame the larger universe's region).
466+
let PlaceholderReachability::Placeholders { max_universe: (max_u, max_u_rvid), .. } =
467+
annotation.reachable_placeholders
468+
else {
469+
continue;
470+
};
471+
472+
let (min_u, _) = annotation.min_universe;
473+
474+
if min_u.can_name(max_u) {
475+
continue;
476+
}
477+
478+
debug!("Universe {max_u:?} is too large for its SCC!");
479+
let blame_to = if annotation.representative.rvid() == max_u_rvid {
480+
// We originally had a large enough universe to fit all our reachable
481+
// placeholders, but had it lowered because we also absorbed something
482+
// small-universed. In this case, that's to blame!
483+
let small_universed_rvid = find_region(
484+
outlives_constraints,
485+
memoised_constraint_graph
486+
.get_or_insert_with(|| outlives_constraints.graph(definitions.len())),
487+
definitions,
488+
max_u_rvid,
489+
|r: RegionVid| definitions[r].universe == min_u,
492490
fr_static,
493491
);
494-
}
492+
debug!("{small_universed_rvid:?} lowered our universe to {min_u:?}");
493+
small_universed_rvid
494+
} else {
495+
// The problem is that we, who have a small universe, reach a large one.
496+
max_u_rvid
497+
};
498+
499+
outlives_static.insert(scc);
500+
outlives_constraints.add_placeholder_violation_constraint(
501+
annotation.representative_rvid(),
502+
annotation.representative_rvid(),
503+
blame_to,
504+
fr_static,
505+
);
495506
}
496507
outlives_static
497508
}
498509

510+
/// Find a region matching a predicate in a set of constraints, using BFS.
511+
// FIXME(amandasystems) this is at least partially duplicated code to the constraint search in `region_infer`.
512+
// It's probably also very expensive.
513+
fn find_region<'tcx>(
514+
constraints: &OutlivesConstraintSet<'tcx>,
515+
graph: &ConstraintGraph<Normal>,
516+
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
517+
start_region: RegionVid,
518+
target_test: impl Fn(RegionVid) -> bool,
519+
fr_static: RegionVid,
520+
) -> RegionVid {
521+
#[derive(Clone, PartialEq, Eq, Debug)]
522+
enum Trace {
523+
StartRegion,
524+
NotVisited,
525+
Visited,
526+
}
527+
528+
let mut context = IndexVec::from_elem(Trace::NotVisited, definitions);
529+
context[start_region] = Trace::StartRegion;
530+
531+
let mut deque = VecDeque::new();
532+
deque.push_back(start_region);
533+
534+
while let Some(r) = deque.pop_front() {
535+
if target_test(r) {
536+
return r;
537+
}
538+
539+
let outgoing_edges_from_graph = graph.outgoing_edges(r, constraints, Some(fr_static));
540+
541+
for constraint in outgoing_edges_from_graph {
542+
debug_assert_eq!(constraint.sup, r);
543+
let sub_region = constraint.sub;
544+
if let Trace::NotVisited = context[sub_region] {
545+
context[sub_region] = Trace::Visited;
546+
deque.push_back(sub_region);
547+
}
548+
}
549+
}
550+
unreachable!("Should have found something!");
551+
}
552+
499553
#[instrument(skip(sccs, scc_annotations, universal_regions), ret)]
500554
fn bound_has_universe_violation<'t>(
501555
bound: &VerifyBound<'t>,

compiler/rustc_borrowck/src/nll.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,10 @@ pub(crate) fn compute_regions<'a, 'tcx>(
210210
for error in placeholder_errors.into_iter() {
211211
nll_errors.push(error);
212212
}
213+
} else {
214+
debug!(
215+
"Errors already reported, skipping these placeholder errors: {placeholder_errors:?}"
216+
);
213217
}
214218

215219
if let Some(guar) = nll_errors.has_errors() {

compiler/rustc_borrowck/src/region_infer/mod.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
742742

743743
// Type-test failed. Report the error.
744744
let erased_generic_kind = infcx.tcx.erase_regions(type_test.generic_kind);
745+
745746
let original_lower_bound = type_test
746747
.original
747748
.as_ref()
@@ -1757,6 +1758,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
17571758
| NllRegionVariableOrigin::Existential { from_forall: true } => false,
17581759
};
17591760

1761+
// FIXME(amandasystems) This closure is tooe big to inline, it should be a method if plausible.
17601762
// To pick a constraint to blame, we organize constraints by how interesting we expect them
17611763
// to be in diagnostics, then pick the most interesting one closest to either the source or
17621764
// the target on our constraint path.
@@ -1773,7 +1775,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
17731775
constraint.category
17741776
};
17751777

1776-
match category {
1778+
let interest = match category {
17771779
// Returns usually provide a type to blame and have specially written diagnostics,
17781780
// so prioritize them.
17791781
ConstraintCategory::Return(_) => 0,
@@ -1824,10 +1826,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
18241826
// `BoringNoLocation` constraints can point to user-written code, but are less
18251827
// specific, and are not used for relations that would make sense to blame.
18261828
ConstraintCategory::BoringNoLocation => 6,
1829+
ConstraintCategory::IllegalPlaceholder(_, _) => 7,
18271830
// Do not blame internal constraints.
1828-
ConstraintCategory::Internal => 7,
1829-
ConstraintCategory::IllegalPlaceholder(_, _) => 8,
1830-
}
1831+
ConstraintCategory::Internal => 8,
1832+
};
1833+
1834+
debug!("constraint {constraint:?} category: {category:?}, interest: {interest:?}");
1835+
1836+
interest
18311837
};
18321838

18331839
let best_choice = if blame_source {

tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: implementation of `FnMut` is not general enough
22
--> $DIR/higher-ranked-lifetime-error.rs:12:5
33
|
44
LL | assert_all::<_, &String>(id);
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnMut` is not general enough
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnMut` is not general enough
66
|
77
= note: `for<'a> fn(&'a String) -> &'a String {id}` must implement `FnMut<(&String,)>`
88
= note: ...but it actually implements `FnMut<(&'0 String,)>`, for some specific lifetime `'0`

0 commit comments

Comments
 (0)