Skip to content

Commit

Permalink
Track causes for universes created during borrowck
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewjasper authored and lqd committed Aug 15, 2021
1 parent e271383 commit 5e6027c
Show file tree
Hide file tree
Showing 9 changed files with 329 additions and 129 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/mod.rs
Expand Up @@ -1506,7 +1506,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
self.inner.borrow_mut().projection_cache().clear();
}

fn universe(&self) -> ty::UniverseIndex {
pub fn universe(&self) -> ty::UniverseIndex {
self.universe.get()
}

Expand Down
110 changes: 110 additions & 0 deletions compiler/rustc_mir/src/borrow_check/diagnostics/bound_region_errors.rs
@@ -0,0 +1,110 @@
use rustc_infer::infer::canonical::Canonical;
use rustc_middle::ty::{self, Ty, TypeFoldable};
use rustc_span::Span;
use rustc_trait_selection::traits::query::type_op;

use std::fmt;
use std::rc::Rc;

use crate::borrow_check::region_infer::values::RegionElement;
use crate::borrow_check::MirBorrowckCtxt;

#[derive(Clone)]
crate struct UniverseInfo<'tcx>(UniverseInfoInner<'tcx>);

/// What operation a universe was created for.
#[derive(Clone)]
enum UniverseInfoInner<'tcx> {
/// Relating two types which have binders.
RelateTys { expected: Ty<'tcx>, found: Ty<'tcx> },
/// Created from performing a `TypeOp`.
TypeOp(Rc<dyn TypeOpInfo<'tcx> + 'tcx>),
/// Any other reason.
Other,
}

impl UniverseInfo<'tcx> {
crate fn other() -> UniverseInfo<'tcx> {
UniverseInfo(UniverseInfoInner::Other)
}

crate fn relate(expected: Ty<'tcx>, found: Ty<'tcx>) -> UniverseInfo<'tcx> {
UniverseInfo(UniverseInfoInner::RelateTys { expected, found })
}

crate fn _report_error(
&self,
_mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
_placeholder: ty::PlaceholderRegion,
_error_element: RegionElement,
_span: Span,
) {
todo!();
}
}

crate trait ToUniverseInfo<'tcx> {
fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx>;
}

impl<'tcx> ToUniverseInfo<'tcx>
for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>>
{
fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(PredicateQuery {
_canonical_query: self,
_base_universe: base_universe,
})))
}
}

impl<'tcx, T: Copy + fmt::Display + TypeFoldable<'tcx> + 'tcx> ToUniverseInfo<'tcx>
for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>
{
fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(NormalizeQuery {
_canonical_query: self,
_base_universe: base_universe,
})))
}
}

impl<'tcx> ToUniverseInfo<'tcx>
for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>
{
fn to_universe_info(self, _base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
// Ascribe user type isn't usually called on types that have different
// bound regions.
UniverseInfo::other()
}
}

impl<'tcx, F, G> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::CustomTypeOp<F, G>> {
fn to_universe_info(self, _base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
// We can't rerun custom type ops.
UniverseInfo::other()
}
}

#[allow(unused_lifetimes)]
trait TypeOpInfo<'tcx> {
// TODO: Methods for rerunning type op and reporting an error
}

struct PredicateQuery<'tcx> {
_canonical_query:
Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>>,
_base_universe: ty::UniverseIndex,
}

impl TypeOpInfo<'tcx> for PredicateQuery<'tcx> {}

struct NormalizeQuery<'tcx, T> {
_canonical_query: Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>,
_base_universe: ty::UniverseIndex,
}

impl<T> TypeOpInfo<'tcx> for NormalizeQuery<'tcx, T> where
T: Copy + fmt::Display + TypeFoldable<'tcx> + 'tcx
{
}
2 changes: 2 additions & 0 deletions compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
Expand Up @@ -28,12 +28,14 @@ mod outlives_suggestion;
mod region_name;
mod var_name;

mod bound_region_errors;
mod conflict_errors;
mod explain_borrow;
mod move_errors;
mod mutability_errors;
mod region_errors;

crate use bound_region_errors::{ToUniverseInfo, UniverseInfo};
crate use mutability_errors::AccessKind;
crate use outlives_suggestion::OutlivesSuggestionBuilder;
crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_mir/src/borrow_check/nll.rs
Expand Up @@ -239,6 +239,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
outlives_constraints,
member_constraints,
closure_bounds_mapping,
universe_causes,
type_tests,
} = constraints;
let placeholder_indices = Rc::new(placeholder_indices);
Expand All @@ -260,6 +261,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
outlives_constraints,
member_constraints,
closure_bounds_mapping,
universe_causes,
type_tests,
liveness_constraints,
elements,
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_mir/src/borrow_check/region_infer/mod.rs
Expand Up @@ -21,7 +21,7 @@ use crate::borrow_check::{
constraints::{
graph::NormalConstraintGraph, ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet,
},
diagnostics::{RegionErrorKind, RegionErrors},
diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo},
member_constraints::{MemberConstraintSet, NllMemberConstraintIndex},
nll::{PoloniusOutput, ToRegionVid},
region_infer::reverse_sccs::ReverseSccGraph,
Expand Down Expand Up @@ -84,6 +84,9 @@ pub struct RegionInferenceContext<'tcx> {
closure_bounds_mapping:
FxHashMap<Location, FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>>,

/// Map universe indexes to information on why we created it.
_universe_causes: IndexVec<ty::UniverseIndex, UniverseInfo<'tcx>>,

/// Contains the minimum universe of any variable within the same
/// SCC. We will ensure that no SCC contains values that are not
/// visible from this index.
Expand Down Expand Up @@ -253,6 +256,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
Location,
FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>,
>,
universe_causes: IndexVec<ty::UniverseIndex, UniverseInfo<'tcx>>,
type_tests: Vec<TypeTest<'tcx>>,
liveness_constraints: LivenessValues<RegionVid>,
elements: &Rc<RegionValueElements>,
Expand Down Expand Up @@ -293,6 +297,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
member_constraints,
member_constraints_applied: Vec::new(),
closure_bounds_mapping,
_universe_causes: universe_causes,
scc_universes,
scc_representatives,
scc_values,
Expand Down
158 changes: 158 additions & 0 deletions compiler/rustc_mir/src/borrow_check/type_check/canonical.rs
@@ -0,0 +1,158 @@
use std::fmt;

use rustc_hir as hir;
use rustc_infer::infer::canonical::Canonical;
use rustc_infer::traits::query::NoSolution;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::{self, ToPredicate, TypeFoldable};
use rustc_span::Span;
use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput};
use rustc_trait_selection::traits::query::Fallible;

use crate::borrow_check::diagnostics::{ToUniverseInfo, UniverseInfo};

use super::{Locations, NormalizeLocation, TypeChecker};

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
/// Given some operation `op` that manipulates types, proves
/// predicates, or otherwise uses the inference context, executes
/// `op` and then executes all the further obligations that `op`
/// returns. This will yield a set of outlives constraints amongst
/// regions which are extracted and stored as having occurred at
/// `locations`.
///
/// **Any `rustc_infer::infer` operations that might generate region
/// constraints should occur within this method so that those
/// constraints can be properly localized!**
pub(super) fn fully_perform_op<R, Op>(
&mut self,
locations: Locations,
category: ConstraintCategory,
op: Op,
) -> Fallible<R>
where
Op: type_op::TypeOp<'tcx, Output = R>,
Canonical<'tcx, Op>: ToUniverseInfo<'tcx>,
{
let old_universe = self.infcx.universe();

let TypeOpOutput { output, constraints, canonicalized_query } =
op.fully_perform(self.infcx)?;

if let Some(data) = &constraints {
self.push_region_constraints(locations, category, data);
}

let universe = self.infcx.universe();

if old_universe != universe {
let universe_info = match canonicalized_query {
Some(canonicalized_query) => canonicalized_query.to_universe_info(old_universe),
None => UniverseInfo::other(),
};
for u in old_universe..universe {
let info_universe =
self.borrowck_context.constraints.universe_causes.push(universe_info.clone());
assert_eq!(u.as_u32() + 1, info_universe.as_u32());
}
}

Ok(output)
}

pub(super) fn instantiate_canonical_with_fresh_inference_vars<T>(
&mut self,
span: Span,
canonical: &Canonical<'tcx, T>,
) -> T
where
T: TypeFoldable<'tcx>,
{
let (instantiated, _) =
self.infcx.instantiate_canonical_with_fresh_inference_vars(span, canonical);

for _ in 0..canonical.max_universe.as_u32() {
let info = UniverseInfo::other();
self.borrowck_context.constraints.universe_causes.push(info);
}

instantiated
}

pub(super) fn prove_trait_ref(
&mut self,
trait_ref: ty::TraitRef<'tcx>,
locations: Locations,
category: ConstraintCategory,
) {
self.prove_predicates(
Some(ty::PredicateKind::Trait(ty::TraitPredicate {
trait_ref,
constness: hir::Constness::NotConst,
})),
locations,
category,
);
}

pub(super) fn normalize_and_prove_instantiated_predicates(
&mut self,
instantiated_predicates: ty::InstantiatedPredicates<'tcx>,
locations: Locations,
) {
for predicate in instantiated_predicates.predicates {
let predicate = self.normalize(predicate, locations);
self.prove_predicate(predicate, locations, ConstraintCategory::Boring);
}
}

pub(super) fn prove_predicates(
&mut self,
predicates: impl IntoIterator<Item = impl ToPredicate<'tcx>>,
locations: Locations,
category: ConstraintCategory,
) {
for predicate in predicates {
let predicate = predicate.to_predicate(self.tcx());
debug!("prove_predicates(predicate={:?}, locations={:?})", predicate, locations,);

self.prove_predicate(predicate, locations, category);
}
}

pub(super) fn prove_predicate(
&mut self,
predicate: ty::Predicate<'tcx>,
locations: Locations,
category: ConstraintCategory,
) {
debug!("prove_predicate(predicate={:?}, location={:?})", predicate, locations,);

let param_env = self.param_env;
self.fully_perform_op(
locations,
category,
param_env.and(type_op::prove_predicate::ProvePredicate::new(predicate)),
)
.unwrap_or_else(|NoSolution| {
span_mirbug!(self, NoSolution, "could not prove {:?}", predicate);
})
}

pub(super) fn normalize<T>(&mut self, value: T, location: impl NormalizeLocation) -> T
where
T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx,
{
debug!("normalize(value={:?}, location={:?})", value, location);
let param_env = self.param_env;
self.fully_perform_op(
location.to_locations(),
ConstraintCategory::Boring,
param_env.and(type_op::normalize::Normalize::new(value)),
)
.unwrap_or_else(|NoSolution| {
span_mirbug!(self, NoSolution, "failed to normalize `{:?}`", value);
value
})
}
}
Expand Up @@ -44,7 +44,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// Instantiate the canonicalized variables from
// user-provided signature (e.g., the `_` in the code
// above) with fresh variables.
let (poly_sig, _) = self.infcx.instantiate_canonical_with_fresh_inference_vars(
let poly_sig = self.instantiate_canonical_with_fresh_inference_vars(
body.span,
&user_provided_poly_sig,
);
Expand Down

0 comments on commit 5e6027c

Please sign in to comment.