mirror of
https://github.com/rust-lang/rust.git
synced 2025-10-02 10:18:25 +00:00
Region inference: Use outlives-static constraints in constraint search
Revise the extra `r: 'static` constraints added upon universe issues to add an explanation, and use that explanation during constraint blame search. This greatly simplifies the region inference logic, which now does not need to reverse-engineer the event that caused a region to outlive 'static.
This commit is contained in:
parent
831e291d3b
commit
7dd56b10ad
@ -565,7 +565,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
||||
let (blame_constraint, path) = self.regioncx.best_blame_constraint(
|
||||
borrow_region,
|
||||
NllRegionVariableOrigin::FreeRegion,
|
||||
|r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
|
||||
outlived_region,
|
||||
);
|
||||
let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;
|
||||
|
||||
|
@ -243,10 +243,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
|
||||
let opaque_region_vid = self.regioncx.to_region_vid(opaque_region);
|
||||
|
||||
// Find a path between the borrow region and our opaque capture.
|
||||
if let Some((path, _)) =
|
||||
self.regioncx.find_constraint_path_between_regions(self.borrow_region, |r| {
|
||||
r == opaque_region_vid
|
||||
})
|
||||
if let Some(path) = self
|
||||
.regioncx
|
||||
.constraint_path_between_regions(self.borrow_region, opaque_region_vid)
|
||||
{
|
||||
for constraint in path {
|
||||
// If we find a call in this path, then check if it defines the opaque.
|
||||
|
@ -61,7 +61,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
|
||||
| ConstraintCategory::Boring
|
||||
| ConstraintCategory::BoringNoLocation
|
||||
| ConstraintCategory::Internal
|
||||
| ConstraintCategory::IllegalUniverse => "",
|
||||
| ConstraintCategory::OutlivesUnnameablePlaceholder(..) => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -369,11 +369,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
|
||||
|
||||
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
|
||||
let (_, cause) = self.regioncx.find_outlives_blame_span(
|
||||
longer_fr,
|
||||
NllRegionVariableOrigin::Placeholder(placeholder),
|
||||
error_vid,
|
||||
);
|
||||
let cause = self
|
||||
.regioncx
|
||||
.best_blame_constraint(
|
||||
longer_fr,
|
||||
NllRegionVariableOrigin::Placeholder(placeholder),
|
||||
error_vid,
|
||||
)
|
||||
.0
|
||||
.cause;
|
||||
|
||||
let universe = placeholder.universe;
|
||||
let universe_info = self.regioncx.universe_info(universe);
|
||||
@ -429,9 +433,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
) {
|
||||
debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
|
||||
|
||||
let (blame_constraint, path) = self.regioncx.best_blame_constraint(fr, fr_origin, |r| {
|
||||
self.regioncx.provides_universal_region(r, fr, outlived_fr)
|
||||
});
|
||||
let (blame_constraint, path) =
|
||||
self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr);
|
||||
let BlameConstraint { category, cause, variance_info, .. } = blame_constraint;
|
||||
|
||||
debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info);
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Logic for lowering higher-kinded outlives constraints
|
||||
//! (with placeholders and universes) and turn them into regular
|
||||
//! outlives constraints.
|
||||
use core::cmp;
|
||||
|
||||
use rustc_data_structures::frozen::Frozen;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
@ -10,7 +11,7 @@ use rustc_index::IndexVec;
|
||||
use rustc_infer::infer::RegionVariableOrigin;
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::ty::{RegionVid, UniverseIndex};
|
||||
use tracing::debug;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet};
|
||||
use crate::consumers::OutlivesConstraint;
|
||||
@ -62,18 +63,71 @@ impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
|
||||
type SccIdx = ConstraintSccIndex;
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
|
||||
enum PlaceholderReachability {
|
||||
/// This SCC reaches no placeholders.
|
||||
NoPlaceholders,
|
||||
/// This SCC reaches at least one placeholder.
|
||||
Placeholders {
|
||||
/// The largest-universed placeholder we can reach
|
||||
max_universe: (UniverseIndex, RegionVid),
|
||||
|
||||
/// The placeholder with the smallest ID
|
||||
min_placeholder: RegionVid,
|
||||
|
||||
/// The placeholder with the largest ID
|
||||
max_placeholder: RegionVid,
|
||||
},
|
||||
}
|
||||
|
||||
impl PlaceholderReachability {
|
||||
/// Merge the reachable placeholders of two graph components.
|
||||
fn merge(self, other: PlaceholderReachability) -> PlaceholderReachability {
|
||||
use PlaceholderReachability::*;
|
||||
match (self, other) {
|
||||
(NoPlaceholders, NoPlaceholders) => NoPlaceholders,
|
||||
(NoPlaceholders, p @ Placeholders { .. })
|
||||
| (p @ Placeholders { .. }, NoPlaceholders) => p,
|
||||
(
|
||||
Placeholders {
|
||||
min_placeholder: min_pl,
|
||||
max_placeholder: max_pl,
|
||||
max_universe: max_u,
|
||||
},
|
||||
Placeholders { min_placeholder, max_placeholder, max_universe },
|
||||
) => Placeholders {
|
||||
min_placeholder: cmp::min(min_pl, min_placeholder),
|
||||
max_placeholder: cmp::max(max_pl, max_placeholder),
|
||||
max_universe: cmp::max(max_u, max_universe),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn max_universe(&self) -> Option<(UniverseIndex, RegionVid)> {
|
||||
match self {
|
||||
Self::NoPlaceholders => None,
|
||||
Self::Placeholders { max_universe, .. } => Some(*max_universe),
|
||||
}
|
||||
}
|
||||
|
||||
/// If we have reached placeholders, determine if they can
|
||||
/// be named from this universe.
|
||||
fn can_be_named_by(&self, from: UniverseIndex) -> bool {
|
||||
self.max_universe()
|
||||
.is_none_or(|(max_placeholder_universe, _)| from.can_name(max_placeholder_universe))
|
||||
}
|
||||
}
|
||||
|
||||
/// An annotation for region graph SCCs that tracks
|
||||
/// the values of its elements. This annotates a single SCC.
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
pub(crate) struct RegionTracker {
|
||||
/// The largest universe of a placeholder reached from this SCC.
|
||||
/// This includes placeholders within this SCC.
|
||||
max_placeholder_universe_reached: UniverseIndex,
|
||||
reachable_placeholders: PlaceholderReachability,
|
||||
|
||||
/// The largest universe nameable from this SCC.
|
||||
/// It is the smallest nameable universes of all
|
||||
/// existential regions reachable from it.
|
||||
max_nameable_universe: UniverseIndex,
|
||||
/// existential regions reachable from it. Small Rvids are preferred.
|
||||
max_nameable_universe: (UniverseIndex, RegionVid),
|
||||
|
||||
/// The representative Region Variable Id for this SCC.
|
||||
pub(crate) representative: Representative,
|
||||
@ -81,65 +135,75 @@ pub(crate) struct RegionTracker {
|
||||
|
||||
impl RegionTracker {
|
||||
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
|
||||
let placeholder_universe =
|
||||
let reachable_placeholders =
|
||||
if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) {
|
||||
definition.universe
|
||||
PlaceholderReachability::Placeholders {
|
||||
max_universe: (definition.universe, rvid),
|
||||
min_placeholder: rvid,
|
||||
max_placeholder: rvid,
|
||||
}
|
||||
} else {
|
||||
UniverseIndex::ROOT
|
||||
PlaceholderReachability::NoPlaceholders
|
||||
};
|
||||
|
||||
Self {
|
||||
max_placeholder_universe_reached: placeholder_universe,
|
||||
max_nameable_universe: definition.universe,
|
||||
reachable_placeholders,
|
||||
max_nameable_universe: (definition.universe, rvid),
|
||||
representative: Representative::new(rvid, definition),
|
||||
}
|
||||
}
|
||||
|
||||
/// The largest universe this SCC can name. It's the smallest
|
||||
/// largest nameable uninverse of any reachable region.
|
||||
/// largest nameable universe of any reachable region, or
|
||||
/// `max_nameable(r) = min (max_nameable(r') for r' reachable from r)`
|
||||
pub(crate) fn max_nameable_universe(self) -> UniverseIndex {
|
||||
self.max_nameable_universe
|
||||
self.max_nameable_universe.0
|
||||
}
|
||||
|
||||
pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex {
|
||||
self.max_placeholder_universe_reached
|
||||
}
|
||||
|
||||
fn merge_min_max_seen(&mut self, other: &Self) {
|
||||
self.max_placeholder_universe_reached = std::cmp::max(
|
||||
self.max_placeholder_universe_reached,
|
||||
other.max_placeholder_universe_reached,
|
||||
);
|
||||
|
||||
self.max_nameable_universe =
|
||||
std::cmp::min(self.max_nameable_universe, other.max_nameable_universe);
|
||||
}
|
||||
|
||||
/// Returns `true` if during the annotated SCC reaches a placeholder
|
||||
/// with a universe larger than the smallest nameable universe of any
|
||||
/// reachable existential region.
|
||||
pub(crate) fn has_incompatible_universes(&self) -> bool {
|
||||
self.max_nameable_universe().cannot_name(self.max_placeholder_universe_reached)
|
||||
if let Some((universe, _)) = self.reachable_placeholders.max_universe() {
|
||||
universe
|
||||
} else {
|
||||
UniverseIndex::ROOT
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine if the tracked universes of the two SCCs are compatible.
|
||||
pub(crate) fn universe_compatible_with(&self, other: Self) -> bool {
|
||||
// HACK: We first check whether we can name the highest existential universe
|
||||
// of `other`. This only exists to avoid errors in case that scc already
|
||||
// depends on a placeholder it cannot name itself.
|
||||
self.max_nameable_universe().can_name(other.max_nameable_universe())
|
||||
|| self.max_nameable_universe().can_name(other.max_placeholder_universe_reached)
|
||||
|| other.reachable_placeholders.can_be_named_by(self.max_nameable_universe())
|
||||
}
|
||||
|
||||
/// If this SCC reaches a placeholder it can't name, return it.
|
||||
fn unnameable_placeholder(&self) -> Option<(UniverseIndex, RegionVid)> {
|
||||
self.reachable_placeholders.max_universe().filter(|&(placeholder_universe, _)| {
|
||||
!self.max_nameable_universe().can_name(placeholder_universe)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl scc::Annotation for RegionTracker {
|
||||
fn merge_scc(mut self, other: Self) -> Self {
|
||||
self.representative = self.representative.merge_scc(other.representative);
|
||||
self.merge_min_max_seen(&other);
|
||||
self
|
||||
fn merge_scc(self, other: Self) -> Self {
|
||||
trace!("{:?} << {:?}", self.representative, other.representative);
|
||||
|
||||
Self {
|
||||
representative: self.representative.merge_scc(other.representative),
|
||||
..self.merge_reached(other)
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_reached(mut self, other: Self) -> Self {
|
||||
// No update to in-component values, only add seen values.
|
||||
self.merge_min_max_seen(&other);
|
||||
self
|
||||
fn merge_reached(self, other: Self) -> Self {
|
||||
Self {
|
||||
max_nameable_universe: cmp::min(
|
||||
self.max_nameable_universe,
|
||||
other.max_nameable_universe,
|
||||
),
|
||||
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
|
||||
representative: self.representative,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,28 +374,52 @@ fn rewrite_placeholder_outlives<'tcx>(
|
||||
|
||||
let annotation = annotations[scc];
|
||||
|
||||
// If this SCC participates in a universe violation,
|
||||
// e.g. if it reaches a region with a universe smaller than
|
||||
// the largest region reached, add a requirement that it must
|
||||
// outlive `'static`.
|
||||
if annotation.has_incompatible_universes() {
|
||||
// Optimisation opportunity: this will add more constraints than
|
||||
// needed for correctness, since an SCC upstream of another with
|
||||
// a universe violation will "infect" its downstream SCCs to also
|
||||
// outlive static.
|
||||
let scc_representative_outlives_static = OutlivesConstraint {
|
||||
sup: annotation.representative.rvid(),
|
||||
sub: fr_static,
|
||||
category: ConstraintCategory::IllegalUniverse,
|
||||
locations: Locations::All(rustc_span::DUMMY_SP),
|
||||
span: rustc_span::DUMMY_SP,
|
||||
variance_info: VarianceDiagInfo::None,
|
||||
from_closure: false,
|
||||
};
|
||||
outlives_constraints.push(scc_representative_outlives_static);
|
||||
added_constraints = true;
|
||||
debug!("Added {:?}: 'static!", annotation.representative.rvid());
|
||||
}
|
||||
let Some((max_u, max_u_rvid)) = annotation.unnameable_placeholder() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
debug!(
|
||||
"Placeholder universe {max_u:?} is too large for its SCC, represented by {:?}",
|
||||
annotation.representative
|
||||
);
|
||||
|
||||
// We only add one `r: 'static` constraint per SCC, where `r` is the SCC representative.
|
||||
// That constraint is annotated with some placeholder `unnameable` where
|
||||
// `unnameable` is unnameable from `r` and there is a path in the constraint graph
|
||||
// between them.
|
||||
//
|
||||
// There is one exception; if some other region in this SCC can't name `'r`, then
|
||||
// we pick the region with the smallest universe in the SCC, so that a path can
|
||||
// always start in `'r` to find a motivation that isn't cyclic.
|
||||
let blame_to = if annotation.representative.rvid() == max_u_rvid {
|
||||
// Assertion: the region that lowered our universe is an existential one and we are a placeholder!
|
||||
|
||||
// The SCC's representative is not nameable from some region
|
||||
// that ends up in the SCC.
|
||||
let small_universed_rvid = annotation.max_nameable_universe.1;
|
||||
debug!(
|
||||
"{small_universed_rvid:?} lowered our universe to {:?}",
|
||||
annotation.max_nameable_universe()
|
||||
);
|
||||
small_universed_rvid
|
||||
} else {
|
||||
// `max_u_rvid` is not nameable by the SCC's representative.
|
||||
max_u_rvid
|
||||
};
|
||||
|
||||
// FIXME: if we can extract a useful blame span here, future error
|
||||
// reporting and constraint search can be simplified.
|
||||
|
||||
added_constraints = true;
|
||||
outlives_constraints.push(OutlivesConstraint {
|
||||
sup: annotation.representative.rvid(),
|
||||
sub: fr_static,
|
||||
category: ConstraintCategory::OutlivesUnnameablePlaceholder(blame_to),
|
||||
locations: Locations::All(rustc_span::DUMMY_SP),
|
||||
span: rustc_span::DUMMY_SP,
|
||||
variance_info: VarianceDiagInfo::None,
|
||||
from_closure: false,
|
||||
});
|
||||
}
|
||||
added_constraints
|
||||
}
|
||||
|
@ -12,9 +12,13 @@ use rustc_middle::ty::UniverseIndex;
|
||||
use super::*;
|
||||
|
||||
fn render_outlives_constraint(constraint: &OutlivesConstraint<'_>) -> String {
|
||||
match constraint.locations {
|
||||
Locations::All(_) => "All(...)".to_string(),
|
||||
Locations::Single(loc) => format!("{loc:?}"),
|
||||
if let ConstraintCategory::OutlivesUnnameablePlaceholder(unnameable) = constraint.category {
|
||||
format!("{unnameable:?} unnameable")
|
||||
} else {
|
||||
match constraint.locations {
|
||||
Locations::All(_) => "All(...)".to_string(),
|
||||
Locations::Single(loc) => format!("{loc:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1285,11 +1285,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
{
|
||||
debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus);
|
||||
|
||||
let blame_span_category = self.find_outlives_blame_span(
|
||||
longer_fr,
|
||||
NllRegionVariableOrigin::FreeRegion,
|
||||
shorter_fr,
|
||||
);
|
||||
let blame_constraint = self
|
||||
.best_blame_constraint(longer_fr, NllRegionVariableOrigin::FreeRegion, shorter_fr)
|
||||
.0;
|
||||
|
||||
// Grow `shorter_fr` until we find some non-local regions. (We
|
||||
// always will.) We'll call them `shorter_fr+` -- they're ever
|
||||
@ -1302,8 +1300,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
propagated_outlives_requirements.push(ClosureOutlivesRequirement {
|
||||
subject: ClosureOutlivesSubject::Region(fr_minus),
|
||||
outlived_free_region: fr,
|
||||
blame_span: blame_span_category.1.span,
|
||||
category: blame_span_category.0,
|
||||
blame_span: blame_constraint.cause.span,
|
||||
category: blame_constraint.category,
|
||||
});
|
||||
}
|
||||
return RegionRelationCheckResult::Propagated;
|
||||
@ -1342,66 +1340,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// We have a constraint `fr1: fr2` that is not satisfied, where
|
||||
/// `fr2` represents some universal region. Here, `r` is some
|
||||
/// region where we know that `fr1: r` and this function has the
|
||||
/// job of determining whether `r` is "to blame" for the fact that
|
||||
/// `fr1: fr2` is required.
|
||||
///
|
||||
/// This is true under two conditions:
|
||||
///
|
||||
/// - `r == fr2`
|
||||
/// - `fr2` is `'static` and `r` is some placeholder in a universe
|
||||
/// that cannot be named by `fr1`; in that case, we will require
|
||||
/// that `fr1: 'static` because it is the only way to `fr1: r` to
|
||||
/// be satisfied. (See `add_incompatible_universe`.)
|
||||
pub(crate) fn provides_universal_region(
|
||||
pub(crate) fn constraint_path_between_regions(
|
||||
&self,
|
||||
r: RegionVid,
|
||||
fr1: RegionVid,
|
||||
fr2: RegionVid,
|
||||
) -> bool {
|
||||
debug!("provides_universal_region(r={:?}, fr1={:?}, fr2={:?})", r, fr1, fr2);
|
||||
let result = {
|
||||
r == fr2 || {
|
||||
fr2 == self.universal_regions().fr_static && self.cannot_name_placeholder(fr1, r)
|
||||
}
|
||||
};
|
||||
debug!("provides_universal_region: result = {:?}", result);
|
||||
result
|
||||
}
|
||||
|
||||
/// If `r2` represents a placeholder region, then this returns
|
||||
/// `true` if `r1` cannot name that placeholder in its
|
||||
/// value; otherwise, returns `false`.
|
||||
pub(crate) fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool {
|
||||
match self.definitions[r2].origin {
|
||||
NllRegionVariableOrigin::Placeholder(placeholder) => {
|
||||
let r1_universe = self.definitions[r1].universe;
|
||||
debug!(
|
||||
"cannot_name_value_of: universe1={r1_universe:?} placeholder={:?}",
|
||||
placeholder
|
||||
);
|
||||
r1_universe.cannot_name(placeholder.universe)
|
||||
}
|
||||
|
||||
NllRegionVariableOrigin::FreeRegion | NllRegionVariableOrigin::Existential { .. } => {
|
||||
false
|
||||
}
|
||||
from_region: RegionVid,
|
||||
to_region: RegionVid,
|
||||
) -> Option<Vec<OutlivesConstraint<'tcx>>> {
|
||||
if from_region == to_region {
|
||||
bug!("Tried to find a path between {from_region:?} and itself!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds a good `ObligationCause` to blame for the fact that `fr1` outlives `fr2`.
|
||||
pub(crate) fn find_outlives_blame_span(
|
||||
&self,
|
||||
fr1: RegionVid,
|
||||
fr1_origin: NllRegionVariableOrigin,
|
||||
fr2: RegionVid,
|
||||
) -> (ConstraintCategory<'tcx>, ObligationCause<'tcx>) {
|
||||
let BlameConstraint { category, cause, .. } = self
|
||||
.best_blame_constraint(fr1, fr1_origin, |r| self.provides_universal_region(r, fr1, fr2))
|
||||
.0;
|
||||
(category, cause)
|
||||
self.constraint_path_to(from_region, |to| to == to_region, true).map(|o| o.0)
|
||||
}
|
||||
|
||||
/// Walks the graph of constraints (where `'a: 'b` is considered
|
||||
@ -1410,15 +1357,30 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
///
|
||||
/// Returns: a series of constraints as well as the region `R`
|
||||
/// that passed the target test.
|
||||
/// If `include_static_outlives_all` is `true`, then the synthetic
|
||||
/// outlives constraints `'static -> a` for every region `a` are
|
||||
/// considered in the search, otherwise they are ignored.
|
||||
#[instrument(skip(self, target_test), ret)]
|
||||
pub(crate) fn find_constraint_path_between_regions(
|
||||
pub(crate) fn constraint_path_to(
|
||||
&self,
|
||||
from_region: RegionVid,
|
||||
target_test: impl Fn(RegionVid) -> bool,
|
||||
include_placeholder_static: bool,
|
||||
) -> Option<(Vec<OutlivesConstraint<'tcx>>, RegionVid)> {
|
||||
self.find_constraint_path_between_regions_inner(true, from_region, &target_test).or_else(
|
||||
|| self.find_constraint_path_between_regions_inner(false, from_region, &target_test),
|
||||
self.find_constraint_path_between_regions_inner(
|
||||
true,
|
||||
from_region,
|
||||
&target_test,
|
||||
include_placeholder_static,
|
||||
)
|
||||
.or_else(|| {
|
||||
self.find_constraint_path_between_regions_inner(
|
||||
false,
|
||||
from_region,
|
||||
&target_test,
|
||||
include_placeholder_static,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// The constraints we get from equating the hidden type of each use of an opaque
|
||||
@ -1438,6 +1400,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
ignore_opaque_type_constraints: bool,
|
||||
from_region: RegionVid,
|
||||
target_test: impl Fn(RegionVid) -> bool,
|
||||
include_placeholder_static: bool,
|
||||
) -> Option<(Vec<OutlivesConstraint<'tcx>>, RegionVid)> {
|
||||
let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
|
||||
context[from_region] = Trace::StartRegion;
|
||||
@ -1452,7 +1415,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|
||||
while let Some(r) = deque.pop_front() {
|
||||
debug!(
|
||||
"find_constraint_path_between_regions: from_region={:?} r={:?} value={}",
|
||||
"constraint_path_to: from_region={:?} r={:?} value={}",
|
||||
from_region,
|
||||
r,
|
||||
self.region_value_str(r),
|
||||
@ -1525,8 +1488,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// This loop can be hot.
|
||||
for constraint in edges {
|
||||
match constraint.category {
|
||||
ConstraintCategory::IllegalUniverse => {
|
||||
debug!("Ignoring illegal universe constraint: {constraint:?}");
|
||||
ConstraintCategory::OutlivesUnnameablePlaceholder(_)
|
||||
if !include_placeholder_static =>
|
||||
{
|
||||
debug!("Ignoring illegal placeholder constraint: {constraint:?}");
|
||||
continue;
|
||||
}
|
||||
ConstraintCategory::OpaqueType if ignore_opaque_type_constraints => {
|
||||
@ -1535,6 +1500,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
debug_assert_eq!(constraint.sup, r);
|
||||
handle_trace(constraint.sub, Trace::FromGraph(constraint));
|
||||
}
|
||||
@ -1549,33 +1515,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid {
|
||||
trace!(scc = ?self.constraint_sccs.scc(fr1));
|
||||
trace!(universe = ?self.max_nameable_universe(self.constraint_sccs.scc(fr1)));
|
||||
self.find_constraint_path_between_regions(fr1, |r| {
|
||||
// First look for some `r` such that `fr1: r` and `r` is live at `location`
|
||||
self.constraint_path_to(fr1, |r| {
|
||||
trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r));
|
||||
self.liveness_constraints.is_live_at(r, location)
|
||||
})
|
||||
.or_else(|| {
|
||||
// If we fail to find that, we may find some `r` such that
|
||||
// `fr1: r` and `r` is a placeholder from some universe
|
||||
// `fr1` cannot name. This would force `fr1` to be
|
||||
// `'static`.
|
||||
self.find_constraint_path_between_regions(fr1, |r| self.cannot_name_placeholder(fr1, r))
|
||||
})
|
||||
.or_else(|| {
|
||||
// If we fail to find THAT, it may be that `fr1` is a
|
||||
// placeholder that cannot "fit" into its SCC. In that
|
||||
// case, there should be some `r` where `fr1: r` and `fr1` is a
|
||||
// placeholder that `r` cannot name. We can blame that
|
||||
// edge.
|
||||
//
|
||||
// Remember that if `R1: R2`, then the universe of R1
|
||||
// must be able to name the universe of R2, because R2 will
|
||||
// be at least `'empty(Universe(R2))`, and `R1` must be at
|
||||
// larger than that.
|
||||
self.find_constraint_path_between_regions(fr1, |r| self.cannot_name_placeholder(r, fr1))
|
||||
})
|
||||
.map(|(_path, r)| r)
|
||||
.unwrap()
|
||||
}, true).unwrap().1
|
||||
}
|
||||
|
||||
/// Get the region outlived by `longer_fr` and live at `element`.
|
||||
@ -1619,22 +1562,38 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// creating a constraint path that forces `R` to outlive
|
||||
/// `from_region`, and then finding the best choices within that
|
||||
/// path to blame.
|
||||
#[instrument(level = "debug", skip(self, target_test))]
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn best_blame_constraint(
|
||||
&self,
|
||||
from_region: RegionVid,
|
||||
from_region_origin: NllRegionVariableOrigin,
|
||||
target_test: impl Fn(RegionVid) -> bool,
|
||||
to_region: RegionVid,
|
||||
) -> (BlameConstraint<'tcx>, Vec<OutlivesConstraint<'tcx>>) {
|
||||
// Find all paths
|
||||
let (path, target_region) = self
|
||||
.find_constraint_path_between_regions(from_region, target_test)
|
||||
.or_else(|| {
|
||||
self.find_constraint_path_between_regions(from_region, |r| {
|
||||
self.cannot_name_placeholder(from_region, r)
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
assert!(from_region != to_region, "Trying to blame a region for itself!");
|
||||
|
||||
let path = self.constraint_path_between_regions(from_region, to_region).unwrap();
|
||||
|
||||
// If we are passing through a constraint added because we reached an unnameable placeholder `'unnameable`,
|
||||
// redirect search towards `'unnameable`.
|
||||
let due_to_placeholder_outlives = path.iter().find_map(|c| {
|
||||
if let ConstraintCategory::OutlivesUnnameablePlaceholder(unnameable) = c.category {
|
||||
Some(unnameable)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
// Edge case: it's possible that `'from_region` is an unnameable placeholder.
|
||||
let path = if let Some(unnameable) = due_to_placeholder_outlives
|
||||
&& unnameable != from_region
|
||||
{
|
||||
// We ignore the extra edges due to unnameable placeholders to get
|
||||
// an explanation that was present in the original constraint graph.
|
||||
self.constraint_path_to(from_region, |r| r == unnameable, false).unwrap().0
|
||||
} else {
|
||||
path
|
||||
};
|
||||
|
||||
debug!(
|
||||
"path={:#?}",
|
||||
path.iter()
|
||||
@ -1742,7 +1701,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
ConstraintCategory::Cast {
|
||||
unsize_to: Some(unsize_ty),
|
||||
is_implicit_coercion: true,
|
||||
} if target_region == self.universal_regions().fr_static
|
||||
} if to_region == self.universal_regions().fr_static
|
||||
// Mirror the note's condition, to minimize how often this diverts blame.
|
||||
&& let ty::Adt(_, args) = unsize_ty.kind()
|
||||
&& args.iter().any(|arg| arg.as_type().is_some_and(|ty| ty.is_trait()))
|
||||
@ -1780,7 +1739,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// specific, and are not used for relations that would make sense to blame.
|
||||
ConstraintCategory::BoringNoLocation => 6,
|
||||
// Do not blame internal constraints.
|
||||
ConstraintCategory::IllegalUniverse => 7,
|
||||
ConstraintCategory::OutlivesUnnameablePlaceholder(_) => 7,
|
||||
ConstraintCategory::Internal => 8,
|
||||
};
|
||||
|
||||
@ -1817,6 +1776,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
path[best_choice]
|
||||
};
|
||||
|
||||
assert!(
|
||||
!matches!(
|
||||
best_constraint.category,
|
||||
ConstraintCategory::OutlivesUnnameablePlaceholder(_)
|
||||
),
|
||||
"Illegal placeholder constraint blamed; should have redirected to other region relation"
|
||||
);
|
||||
|
||||
let blame_constraint = BlameConstraint {
|
||||
category: best_constraint.category,
|
||||
from_closure: best_constraint.from_closure,
|
||||
|
@ -149,8 +149,15 @@ pub enum ConstraintCategory<'tcx> {
|
||||
/// A constraint that doesn't correspond to anything the user sees.
|
||||
Internal,
|
||||
|
||||
/// An internal constraint derived from an illegal universe relation.
|
||||
IllegalUniverse,
|
||||
/// An internal constraint added when a region outlives a placeholder
|
||||
/// it cannot name and therefore has to outlive `'static`. The argument
|
||||
/// is the unnameable placeholder and the constraint is always between
|
||||
/// an SCC representative and `'static`.
|
||||
OutlivesUnnameablePlaceholder(
|
||||
#[type_foldable(identity)]
|
||||
#[type_visitable(ignore)]
|
||||
ty::RegionVid,
|
||||
),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
|
@ -32,12 +32,10 @@ LL | fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::Assoc)>::Assoc)) {}
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/issue-111404-1.rs:10:1
|
||||
--> $DIR/issue-111404-1.rs:10:8
|
||||
|
|
||||
LL | fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::Assoc)>::Assoc)) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
| ^
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
|
@ -2,7 +2,7 @@ error: implementation of `FnMut` is not general enough
|
||||
--> $DIR/higher-ranked-lifetime-error.rs:12:5
|
||||
|
|
||||
LL | assert_all::<_, &String>(id);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnMut` is not general enough
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnMut` is not general enough
|
||||
|
|
||||
= note: `for<'a> fn(&'a String) -> &'a String {id}` must implement `FnMut<(&String,)>`
|
||||
= note: ...but it actually implements `FnMut<(&'0 String,)>`, for some specific lifetime `'0`
|
||||
|
@ -2,7 +2,7 @@ error: implementation of `Foo` is not general enough
|
||||
--> $DIR/due-to-where-clause.rs:2:5
|
||||
|
|
||||
LL | test::<FooS>(&mut 42);
|
||||
| ^^^^^^^^^^^^ implementation of `Foo` is not general enough
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
|
||||
|
|
||||
= note: `FooS<'_>` must implement `Foo<'0>`, for any lifetime `'0`...
|
||||
= note: ...but `FooS<'_>` actually implements `Foo<'1>`, for some specific lifetime `'1`
|
||||
|
@ -2,7 +2,7 @@ error[E0308]: mismatched types
|
||||
--> $DIR/hr-projection-mismatch.rs:20:5
|
||||
|
|
||||
LL | wrap::<_, Thing>();
|
||||
| ^^^^^^^^^^^^^^^^ one type is more general than the other
|
||||
| ^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
||||
|
|
||||
= note: expected reference `&'a _`
|
||||
found reference `&_`
|
||||
|
@ -17,15 +17,6 @@ LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
= note: ...but it actually implements `FnOnce<(&'1 mut V,)>`, for some specific lifetime `'1`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `Fn` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
@ -43,48 +34,58 @@ LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:9
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^ implementation of `FnOnce` is not general enough
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `Fn` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:9
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:9
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/ice-106874.rs:8:41
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^
|
||||
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/ice-106874.rs:8:41
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: implementation of `Fn` is not general enough
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/ice-106874.rs:8:7
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/ice-106874.rs:8:41
|
||||
|
|
||||
LL | A(B(C::new(D::new(move |st| f(st)))))
|
||||
| ^
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
|
@ -38,18 +38,16 @@ LL | accept(callback);
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/missing-universe-cause-issue-114907.rs:33:21
|
||||
--> $DIR/missing-universe-cause-issue-114907.rs:33:5
|
||||
|
|
||||
LL | accept(callback);
|
||||
| ^
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/missing-universe-cause-issue-114907.rs:33:21
|
||||
|
|
||||
LL | accept(callback);
|
||||
| ^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user