use crate::infer::region_constraints::Constraint;
use crate::infer::region_constraints::GenericKind;
use crate::infer::region_constraints::MemberConstraint;
use crate::infer::region_constraints::RegionConstraintData;
use crate::infer::region_constraints::VarInfos;
use crate::infer::region_constraints::VerifyBound;
use crate::infer::RegionRelations;
use crate::infer::RegionVariableOrigin;
use crate::infer::RegionckMode;
use crate::infer::SubregionOrigin;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::implementation::{
Direction, Graph, NodeIndex, INCOMING, OUTGOING,
};
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic};
use rustc_middle::ty::{ReLateBound, RePlaceholder, ReVar};
use rustc_middle::ty::{Region, RegionVid};
use rustc_span::Span;
use std::fmt;
pub fn resolve<'tcx>(
region_rels: &RegionRelations<'_, 'tcx>,
var_infos: VarInfos,
data: RegionConstraintData<'tcx>,
mode: RegionckMode,
) -> (LexicalRegionResolutions<'tcx>, Vec<RegionResolutionError<'tcx>>) {
debug!("RegionConstraintData: resolve_regions()");
let mut errors = vec![];
let mut resolver = LexicalResolver { region_rels, var_infos, data };
match mode {
RegionckMode::Solve => {
let values = resolver.infer_variable_values(&mut errors);
(values, errors)
}
RegionckMode::Erase { suppress_errors: false } => {
let mut values = resolver.infer_variable_values(&mut errors);
let re_erased = region_rels.tcx.lifetimes.re_erased;
values.values.iter_mut().for_each(|v| match *v {
VarValue::Value(ref mut r) => *r = re_erased,
VarValue::ErrorValue => {}
});
(values, errors)
}
RegionckMode::Erase { suppress_errors: true } => {
(resolver.erased_data(region_rels.tcx), Vec::new())
}
}
}
pub struct LexicalRegionResolutions<'tcx> {
values: IndexVec<RegionVid, VarValue<'tcx>>,
error_region: ty::Region<'tcx>,
}
#[derive(Copy, Clone, Debug)]
enum VarValue<'tcx> {
Value(Region<'tcx>),
ErrorValue,
}
#[derive(Clone, Debug)]
pub enum RegionResolutionError<'tcx> {
ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>),
GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region<'tcx>),
SubSupConflict(
RegionVid,
RegionVariableOrigin,
SubregionOrigin<'tcx>,
Region<'tcx>,
SubregionOrigin<'tcx>,
Region<'tcx>,
),
UpperBoundUniverseConflict(
RegionVid,
RegionVariableOrigin,
ty::UniverseIndex,
SubregionOrigin<'tcx>,
Region<'tcx>,
),
MemberConstraintFailure { span: Span, hidden_ty: Ty<'tcx>, member_region: Region<'tcx> },
}
struct RegionAndOrigin<'tcx> {
region: Region<'tcx>,
origin: SubregionOrigin<'tcx>,
}
type RegionGraph<'tcx> = Graph<(), Constraint<'tcx>>;
struct LexicalResolver<'cx, 'tcx> {
region_rels: &'cx RegionRelations<'cx, 'tcx>,
var_infos: VarInfos,
data: RegionConstraintData<'tcx>,
}
impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.region_rels.tcx
}
fn infer_variable_values(
&mut self,
errors: &mut Vec<RegionResolutionError<'tcx>>,
) -> LexicalRegionResolutions<'tcx> {
let mut var_data = self.construct_var_data(self.tcx());
debug!(
"----() End constraint listing (context={:?}) {:?}---",
self.region_rels.context,
self.dump_constraints(self.region_rels)
);
let graph = self.construct_graph();
self.expand_givens(&graph);
loop {
self.expansion(&mut var_data);
if !self.enforce_member_constraints(&graph, &mut var_data) {
break;
}
}
self.collect_errors(&mut var_data, errors);
self.collect_var_errors(&var_data, &graph, errors);
var_data
}
fn num_vars(&self) -> usize {
self.var_infos.len()
}
fn construct_var_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> {
LexicalRegionResolutions {
error_region: tcx.lifetimes.re_static,
values: IndexVec::from_fn_n(
|vid| {
let vid_universe = self.var_infos[vid].universe;
let re_empty = tcx.mk_region(ty::ReEmpty(vid_universe));
VarValue::Value(re_empty)
},
self.num_vars(),
),
}
}
fn erased_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> {
LexicalRegionResolutions {
error_region: tcx.lifetimes.re_static,
values: IndexVec::from_elem_n(
VarValue::Value(tcx.lifetimes.re_erased),
self.num_vars(),
),
}
}
fn dump_constraints(&self, free_regions: &RegionRelations<'_, 'tcx>) {
debug!("----() Start constraint listing (context={:?}) ()----", free_regions.context);
for (idx, (constraint, _)) in self.data.constraints.iter().enumerate() {
debug!("Constraint {} => {:?}", idx, constraint);
}
}
fn expand_givens(&mut self, graph: &RegionGraph<'_>) {
let seeds: Vec<_> = self.data.givens.iter().cloned().collect();
for (r, vid) in seeds {
let seed_index = NodeIndex(vid.index() as usize);
for succ_index in graph.depth_traverse(seed_index, OUTGOING) {
let succ_index = succ_index.0;
if succ_index < self.num_vars() {
let succ_vid = RegionVid::new(succ_index);
self.data.givens.insert((r, succ_vid));
}
}
}
}
fn enforce_member_constraints(
&self,
graph: &RegionGraph<'tcx>,
var_values: &mut LexicalRegionResolutions<'tcx>,
) -> bool {
let mut any_changed = false;
for member_constraint in &self.data.member_constraints {
any_changed |= self.enforce_member_constraint(graph, member_constraint, var_values);
}
any_changed
}
fn enforce_member_constraint(
&self,
graph: &RegionGraph<'tcx>,
member_constraint: &MemberConstraint<'tcx>,
var_values: &mut LexicalRegionResolutions<'tcx>,
) -> bool {
debug!("enforce_member_constraint(member_constraint={:#?})", member_constraint);
let member_vid = match member_constraint.member_region {
ty::ReVar(vid) => *vid,
_ => return false,
};
let member_lower_bound: ty::Region<'tcx> = match var_values.value(member_vid) {
VarValue::ErrorValue => return false,
VarValue::Value(r) => r,
};
let (member_upper_bounds, ..) =
self.collect_bounding_regions(graph, member_vid, OUTGOING, None);
debug!("enforce_member_constraint: upper_bounds={:#?}", member_upper_bounds);
let mut options = member_constraint.choice_regions.iter().filter(|option| {
self.sub_concrete_regions(member_lower_bound, option)
&& member_upper_bounds
.iter()
.all(|upper_bound| self.sub_concrete_regions(option, upper_bound.region))
});
let mut least_choice: ty::Region<'tcx> = match options.next() {
Some(&r) => r,
None => return false,
};
debug!("enforce_member_constraint: least_choice={:?}", least_choice);
for &option in options {
debug!("enforce_member_constraint: option={:?}", option);
if !self.sub_concrete_regions(least_choice, option) {
if self.sub_concrete_regions(option, least_choice) {
debug!("enforce_member_constraint: new least choice");
least_choice = option;
} else {
debug!("enforce_member_constraint: no least choice");
return false;
}
}
}
let lub = self.lub_concrete_regions(least_choice, member_lower_bound);
debug!(
"enforce_member_constraint: final least choice = {:?}\nlub = {:?}",
least_choice, lub
);
if lub != member_lower_bound {
*var_values.value_mut(member_vid) = VarValue::Value(least_choice);
true
} else {
false
}
}
fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) {
let mut constraints = IndexVec::from_elem_n(Vec::new(), var_values.values.len());
let mut changes = Vec::new();
for constraint in self.data.constraints.keys() {
let (a_vid, a_region, b_vid, b_data) = match *constraint {
Constraint::RegSubVar(a_region, b_vid) => {
let b_data = var_values.value_mut(b_vid);
(None, a_region, b_vid, b_data)
}
Constraint::VarSubVar(a_vid, b_vid) => match *var_values.value(a_vid) {
VarValue::ErrorValue => continue,
VarValue::Value(a_region) => {
let b_data = var_values.value_mut(b_vid);
(Some(a_vid), a_region, b_vid, b_data)
}
},
Constraint::RegSubReg(..) | Constraint::VarSubReg(..) => {
continue;
}
};
if self.expand_node(a_region, b_vid, b_data) {
changes.push(b_vid);
}
if let Some(a_vid) = a_vid {
match *b_data {
VarValue::Value(ReStatic) | VarValue::ErrorValue => (),
_ => {
constraints[a_vid].push((a_vid, b_vid));
constraints[b_vid].push((a_vid, b_vid));
}
}
}
}
while let Some(vid) = changes.pop() {
constraints[vid].retain(|&(a_vid, b_vid)| {
let a_region = match *var_values.value(a_vid) {
VarValue::ErrorValue => return false,
VarValue::Value(a_region) => a_region,
};
let b_data = var_values.value_mut(b_vid);
if self.expand_node(a_region, b_vid, b_data) {
changes.push(b_vid);
}
match *b_data {
VarValue::Value(ReStatic) | VarValue::ErrorValue => false,
_ => true,
}
});
}
}
fn expand_node(
&self,
a_region: Region<'tcx>,
b_vid: RegionVid,
b_data: &mut VarValue<'tcx>,
) -> bool {
debug!("expand_node({:?}, {:?} == {:?})", a_region, b_vid, b_data);
match *a_region {
ty::ReEarlyBound(_) | ty::ReFree(_) => {
if self.data.givens.contains(&(a_region, b_vid)) {
debug!("given");
return false;
}
}
_ => {}
}
match *b_data {
VarValue::Value(cur_region) => {
let b_universe = self.var_infos[b_vid].universe;
if let ReEmpty(a_universe) = a_region {
if *a_universe == b_universe {
return false;
}
}
let mut lub = self.lub_concrete_regions(a_region, cur_region);
if lub == cur_region {
return false;
}
if let ty::RePlaceholder(p) = lub {
if b_universe.cannot_name(p.universe) {
lub = self.tcx().lifetimes.re_static;
}
}
debug!("Expanding value of {:?} from {:?} to {:?}", b_vid, cur_region, lub);
*b_data = VarValue::Value(lub);
true
}
VarValue::ErrorValue => false,
}
}
fn sub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> bool {
let tcx = self.tcx();
let sub_free_regions = |r1, r2| self.region_rels.free_regions.sub_free_regions(tcx, r1, r2);
let b_free_or_static = self.region_rels.free_regions.is_free_or_static(b);
if b_free_or_static && sub_free_regions(tcx.lifetimes.re_static, b) {
return true;
}
let a_free_or_static = self.region_rels.free_regions.is_free_or_static(a);
if a_free_or_static && b_free_or_static {
return sub_free_regions(a, b);
}
self.lub_concrete_regions(a, b) == b
}
fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
let r = match (a, b) {
(&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => {
bug!("cannot relate region: LUB({:?}, {:?})", a, b);
}
(&ReVar(v_id), _) | (_, &ReVar(v_id)) => {
span_bug!(
self.var_infos[v_id].origin.span(),
"lub_concrete_regions invoked with non-concrete \
regions: {:?}, {:?}",
a,
b
);
}
(&ReStatic, _) | (_, &ReStatic) => {
self.tcx().lifetimes.re_static
}
(&ReEmpty(_), r @ (ReEarlyBound(_) | ReFree(_)))
| (r @ (ReEarlyBound(_) | ReFree(_)), &ReEmpty(_)) => {
r
}
(&ReEmpty(a_ui), &ReEmpty(b_ui)) => {
let ui = a_ui.min(b_ui);
self.tcx().mk_region(ReEmpty(ui))
}
(&ReEmpty(empty_ui), &RePlaceholder(placeholder))
| (&RePlaceholder(placeholder), &ReEmpty(empty_ui)) => {
if empty_ui.can_name(placeholder.universe) {
self.tcx().mk_region(RePlaceholder(placeholder))
} else {
self.tcx().lifetimes.re_static
}
}
(&ReEarlyBound(_) | &ReFree(_), &ReEarlyBound(_) | &ReFree(_)) => {
self.region_rels.lub_free_regions(a, b)
}
(&RePlaceholder(..), _) | (_, &RePlaceholder(..)) => {
if a == b {
a
} else {
self.tcx().lifetimes.re_static
}
}
};
debug!("lub_concrete_regions({:?}, {:?}) = {:?}", a, b, r);
r
}
fn collect_errors(
&self,
var_data: &mut LexicalRegionResolutions<'tcx>,
errors: &mut Vec<RegionResolutionError<'tcx>>,
) {
for (constraint, origin) in &self.data.constraints {
debug!("collect_errors: constraint={:?} origin={:?}", constraint, origin);
match *constraint {
Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => {
}
Constraint::RegSubReg(sub, sup) => {
if self.sub_concrete_regions(sub, sup) {
continue;
}
debug!(
"collect_errors: region error at {:?}: \
cannot verify that {:?} <= {:?}",
origin, sub, sup
);
errors.push(RegionResolutionError::ConcreteFailure(
(*origin).clone(),
sub,
sup,
));
}
Constraint::VarSubReg(a_vid, b_region) => {
let a_data = var_data.value_mut(a_vid);
debug!("contraction: {:?} == {:?}, {:?}", a_vid, a_data, b_region);
let a_region = match *a_data {
VarValue::ErrorValue => continue,
VarValue::Value(a_region) => a_region,
};
if !self.sub_concrete_regions(a_region, b_region) {
debug!(
"collect_errors: region error at {:?}: \
cannot verify that {:?}={:?} <= {:?}",
origin, a_vid, a_region, b_region
);
*a_data = VarValue::ErrorValue;
}
}
}
}
for member_constraint in &self.data.member_constraints {
let member_region = var_data.normalize(self.tcx(), member_constraint.member_region);
let choice_regions = member_constraint
.choice_regions
.iter()
.map(|&choice_region| var_data.normalize(self.tcx(), choice_region));
if !choice_regions.clone().any(|choice_region| member_region == choice_region) {
let span = self.tcx().def_span(member_constraint.opaque_type_def_id);
errors.push(RegionResolutionError::MemberConstraintFailure {
span,
hidden_ty: member_constraint.hidden_ty,
member_region,
});
}
}
for verify in &self.data.verifys {
debug!("collect_errors: verify={:?}", verify);
let sub = var_data.normalize(self.tcx(), verify.region);
let verify_kind_ty = verify.kind.to_ty(self.tcx());
if self.bound_is_met(&verify.bound, var_data, verify_kind_ty, sub) {
continue;
}
debug!(
"collect_errors: region error at {:?}: \
cannot verify that {:?} <= {:?}",
verify.origin, verify.region, verify.bound
);
errors.push(RegionResolutionError::GenericBoundFailure(
verify.origin.clone(),
verify.kind,
sub,
));
}
}
fn collect_var_errors(
&self,
var_data: &LexicalRegionResolutions<'tcx>,
graph: &RegionGraph<'tcx>,
errors: &mut Vec<RegionResolutionError<'tcx>>,
) {
debug!("collect_var_errors, var_data = {:#?}", var_data.values);
let mut dup_vec = IndexVec::from_elem_n(None, self.num_vars());
for (node_vid, value) in var_data.values.iter_enumerated() {
match *value {
VarValue::Value(_) => { }
VarValue::ErrorValue => {
self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors);
}
}
}
}
fn construct_graph(&self) -> RegionGraph<'tcx> {
let num_vars = self.num_vars();
let mut graph = Graph::new();
for _ in 0..num_vars {
graph.add_node(());
}
let dummy_source = graph.add_node(());
let dummy_sink = graph.add_node(());
for constraint in self.data.constraints.keys() {
match *constraint {
Constraint::VarSubVar(a_id, b_id) => {
graph.add_edge(
NodeIndex(a_id.index() as usize),
NodeIndex(b_id.index() as usize),
*constraint,
);
}
Constraint::RegSubVar(_, b_id) => {
graph.add_edge(dummy_source, NodeIndex(b_id.index() as usize), *constraint);
}
Constraint::VarSubReg(a_id, _) => {
graph.add_edge(NodeIndex(a_id.index() as usize), dummy_sink, *constraint);
}
Constraint::RegSubReg(..) => {
}
}
}
graph
}
fn collect_error_for_expanding_node(
&self,
graph: &RegionGraph<'tcx>,
dup_vec: &mut IndexVec<RegionVid, Option<RegionVid>>,
node_idx: RegionVid,
errors: &mut Vec<RegionResolutionError<'tcx>>,
) {
let (mut lower_bounds, lower_vid_bounds, lower_dup) =
self.collect_bounding_regions(graph, node_idx, INCOMING, Some(dup_vec));
let (mut upper_bounds, _, upper_dup) =
self.collect_bounding_regions(graph, node_idx, OUTGOING, Some(dup_vec));
if lower_dup || upper_dup {
return;
}
fn region_order_key(x: &RegionAndOrigin<'_>) -> u8 {
match *x.region {
ReEarlyBound(_) => 0,
ReFree(_) => 1,
_ => 2,
}
}
lower_bounds.sort_by_key(region_order_key);
upper_bounds.sort_by_key(region_order_key);
let node_universe = self.var_infos[node_idx].universe;
for lower_bound in &lower_bounds {
let effective_lower_bound = if let ty::RePlaceholder(p) = lower_bound.region {
if node_universe.cannot_name(p.universe) {
self.tcx().lifetimes.re_static
} else {
lower_bound.region
}
} else {
lower_bound.region
};
for upper_bound in &upper_bounds {
if !self.sub_concrete_regions(effective_lower_bound, upper_bound.region) {
let origin = self.var_infos[node_idx].origin;
debug!(
"region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \
sup: {:?}",
origin, node_idx, lower_bound.region, upper_bound.region
);
errors.push(RegionResolutionError::SubSupConflict(
node_idx,
origin,
lower_bound.origin.clone(),
lower_bound.region,
upper_bound.origin.clone(),
upper_bound.region,
));
return;
}
}
}
let min_universe = lower_vid_bounds
.into_iter()
.map(|vid| self.var_infos[vid].universe)
.min()
.expect("lower_vid_bounds should at least include `node_idx`");
for upper_bound in &upper_bounds {
if let ty::RePlaceholder(p) = upper_bound.region {
if min_universe.cannot_name(p.universe) {
let origin = self.var_infos[node_idx].origin;
errors.push(RegionResolutionError::UpperBoundUniverseConflict(
node_idx,
origin,
min_universe,
upper_bound.origin.clone(),
upper_bound.region,
));
return;
}
}
}
self.tcx().sess.delay_span_bug(
self.var_infos[node_idx].origin.span(),
&format!(
"collect_error_for_expanding_node() could not find \
error for var {:?} in universe {:?}, lower_bounds={:#?}, \
upper_bounds={:#?}",
node_idx, node_universe, lower_bounds, upper_bounds
),
);
}
fn collect_bounding_regions(
&self,
graph: &RegionGraph<'tcx>,
orig_node_idx: RegionVid,
dir: Direction,
mut dup_vec: Option<&mut IndexVec<RegionVid, Option<RegionVid>>>,
) -> (Vec<RegionAndOrigin<'tcx>>, FxHashSet<RegionVid>, bool) {
struct WalkState<'tcx> {
set: FxHashSet<RegionVid>,
stack: Vec<RegionVid>,
result: Vec<RegionAndOrigin<'tcx>>,
dup_found: bool,
}
let mut state = WalkState {
set: Default::default(),
stack: vec![orig_node_idx],
result: Vec::new(),
dup_found: false,
};
state.set.insert(orig_node_idx);
process_edges(&self.data, &mut state, graph, orig_node_idx, dir);
while let Some(node_idx) = state.stack.pop() {
if let Some(dup_vec) = &mut dup_vec {
if dup_vec[node_idx].is_none() {
dup_vec[node_idx] = Some(orig_node_idx);
} else if dup_vec[node_idx] != Some(orig_node_idx) {
state.dup_found = true;
}
debug!(
"collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})",
orig_node_idx, node_idx
);
}
process_edges(&self.data, &mut state, graph, node_idx, dir);
}
let WalkState { result, dup_found, set, .. } = state;
return (result, set, dup_found);
fn process_edges<'tcx>(
this: &RegionConstraintData<'tcx>,
state: &mut WalkState<'tcx>,
graph: &RegionGraph<'tcx>,
source_vid: RegionVid,
dir: Direction,
) {
debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir);
let source_node_index = NodeIndex(source_vid.index() as usize);
for (_, edge) in graph.adjacent_edges(source_node_index, dir) {
match edge.data {
Constraint::VarSubVar(from_vid, to_vid) => {
let opp_vid = if from_vid == source_vid { to_vid } else { from_vid };
if state.set.insert(opp_vid) {
state.stack.push(opp_vid);
}
}
Constraint::RegSubVar(region, _) | Constraint::VarSubReg(_, region) => {
state.result.push(RegionAndOrigin {
region,
origin: this.constraints.get(&edge.data).unwrap().clone(),
});
}
Constraint::RegSubReg(..) => panic!(
"cannot reach reg-sub-reg edge in region inference \
post-processing"
),
}
}
}
}
fn bound_is_met(
&self,
bound: &VerifyBound<'tcx>,
var_values: &LexicalRegionResolutions<'tcx>,
generic_ty: Ty<'tcx>,
min: ty::Region<'tcx>,
) -> bool {
match bound {
VerifyBound::IfEq(k, b) => {
(var_values.normalize(self.region_rels.tcx, *k) == generic_ty)
&& self.bound_is_met(b, var_values, generic_ty, min)
}
VerifyBound::OutlivedBy(r) => {
self.sub_concrete_regions(min, var_values.normalize(self.tcx(), r))
}
VerifyBound::IsEmpty => {
if let ty::ReEmpty(_) = min {
true
} else {
false
}
}
VerifyBound::AnyBound(bs) => {
bs.iter().any(|b| self.bound_is_met(b, var_values, generic_ty, min))
}
VerifyBound::AllBounds(bs) => {
bs.iter().all(|b| self.bound_is_met(b, var_values, generic_ty, min))
}
}
}
}
impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin)
}
}
impl<'tcx> LexicalRegionResolutions<'tcx> {
fn normalize<T>(&self, tcx: TyCtxt<'tcx>, value: T) -> T
where
T: TypeFoldable<'tcx>,
{
tcx.fold_regions(value, &mut false, |r, _db| match r {
ty::ReVar(rid) => self.resolve_var(*rid),
_ => r,
})
}
fn value(&self, rid: RegionVid) -> &VarValue<'tcx> {
&self.values[rid]
}
fn value_mut(&mut self, rid: RegionVid) -> &mut VarValue<'tcx> {
&mut self.values[rid]
}
pub fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> {
let result = match self.values[rid] {
VarValue::Value(r) => r,
VarValue::ErrorValue => self.error_region,
};
debug!("resolve_var({:?}) = {:?}", rid, result);
result
}
}