Classify outlives constraints when type checking
The MIR/NLL type checker is in a much better position to classify constraints and already has to classify into boring and interesting. Adds spans to Locations::All for error reporting Adds more constraint categories
This commit is contained in:
parent
6e425219f1
commit
994dc4bd1e
12 changed files with 379 additions and 306 deletions
|
|
@ -284,7 +284,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn constrain_opaque_type<FRR: FreeRegionRelations<'tcx>>(
|
||||
pub fn constrain_opaque_type<FRR: FreeRegionRelations<'tcx>>(
|
||||
&self,
|
||||
def_id: DefId,
|
||||
opaque_defn: &OpaqueTypeDecl<'tcx>,
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@
|
|||
// except according to those terms.
|
||||
|
||||
use borrow_check::nll::type_check::Locations;
|
||||
use borrow_check::nll::constraints::{ConstraintIndex, ConstraintSet, OutlivesConstraint};
|
||||
use borrow_check::nll::constraints::{ConstraintCategory, ConstraintIndex};
|
||||
use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint};
|
||||
use rustc::ty::RegionVid;
|
||||
use rustc_data_structures::graph;
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use syntax_pos::DUMMY_SP;
|
||||
|
||||
/// The construct graph organizes the constraints by their end-points.
|
||||
/// It can be used to view a `R1: R2` constraint as either an edge `R1
|
||||
|
|
@ -174,7 +176,8 @@ impl<'s, D: ConstraintGraphDirecton> Iterator for Edges<'s, D> {
|
|||
Some(OutlivesConstraint {
|
||||
sup: self.static_region,
|
||||
sub: next_static_idx.into(),
|
||||
locations: Locations::All,
|
||||
locations: Locations::All(DUMMY_SP),
|
||||
category: ConstraintCategory::Internal,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -23,6 +23,42 @@ crate struct ConstraintSet {
|
|||
constraints: IndexVec<ConstraintIndex, OutlivesConstraint>,
|
||||
}
|
||||
|
||||
/// Constraints can be categorized to determine whether and why they are
|
||||
/// interesting. Order of variants indicates sort order of the category,
|
||||
/// thereby influencing diagnostic output.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||
pub enum ConstraintCategory {
|
||||
Return,
|
||||
TypeAnnotation,
|
||||
Cast,
|
||||
CallArgument,
|
||||
|
||||
/// A constraint that came from checking the body of a closure.
|
||||
///
|
||||
/// Ideally we would give an explanation that points to the relevant part
|
||||
/// of the closure's body.
|
||||
ClosureBounds,
|
||||
CopyBound,
|
||||
SizedBound,
|
||||
Assignment,
|
||||
OpaqueType,
|
||||
|
||||
/// A "boring" constraint (caused by the given location) is one that
|
||||
/// the user probably doesn't want to see described in diagnostics,
|
||||
/// because it is kind of an artifact of the type system setup.
|
||||
/// Example: `x = Foo { field: y }` technically creates
|
||||
/// intermediate regions representing the "type of `Foo { field: y
|
||||
/// }`", and data flows from `y` into those variables, but they
|
||||
/// are not very interesting. The assignment into `x` on the other
|
||||
/// hand might be.
|
||||
Boring,
|
||||
// Boring and applicable everywhere.
|
||||
BoringNoLocation,
|
||||
|
||||
/// A constraint that doesn't correspond to anything the user sees.
|
||||
Internal,
|
||||
}
|
||||
|
||||
impl ConstraintSet {
|
||||
crate fn push(&mut self, constraint: OutlivesConstraint) {
|
||||
debug!(
|
||||
|
|
@ -87,6 +123,9 @@ pub struct OutlivesConstraint {
|
|||
|
||||
/// Where did this constraint arise?
|
||||
pub locations: Locations,
|
||||
|
||||
/// What caused this constraint?
|
||||
pub category: ConstraintCategory,
|
||||
}
|
||||
|
||||
impl fmt::Debug for OutlivesConstraint {
|
||||
|
|
|
|||
|
|
@ -88,11 +88,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
sup,
|
||||
sub,
|
||||
locations,
|
||||
category,
|
||||
} = constraint;
|
||||
with_msg(&format!(
|
||||
"{:?}: {:?} due to {:?}",
|
||||
"{:?}: {:?} due to {:?} at {:?}",
|
||||
sup,
|
||||
sub,
|
||||
category,
|
||||
locations,
|
||||
))?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,13 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use borrow_check::nll::constraints::OutlivesConstraint;
|
||||
use borrow_check::nll::constraints::{OutlivesConstraint, ConstraintCategory};
|
||||
use borrow_check::nll::region_infer::RegionInferenceContext;
|
||||
use borrow_check::nll::type_check::Locations;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind};
|
||||
use rustc::ty::{self, TyCtxt, RegionVid};
|
||||
use rustc::mir::{Location, Mir};
|
||||
use rustc::ty::{self, RegionVid};
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use rustc_errors::{Diagnostic, DiagnosticBuilder};
|
||||
use std::collections::VecDeque;
|
||||
|
|
@ -28,19 +27,6 @@ mod var_name;
|
|||
|
||||
use self::region_name::RegionName;
|
||||
|
||||
/// Constraints that are considered interesting can be categorized to
|
||||
/// determine why they are interesting. Order of variants indicates
|
||||
/// sort order of the category, thereby influencing diagnostic output.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
enum ConstraintCategory {
|
||||
Cast,
|
||||
Assignment,
|
||||
Return,
|
||||
CallArgument,
|
||||
Other,
|
||||
Boring,
|
||||
}
|
||||
|
||||
impl fmt::Display for ConstraintCategory {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// Must end with a space. Allows for empty names to be provided.
|
||||
|
|
@ -49,7 +35,14 @@ impl fmt::Display for ConstraintCategory {
|
|||
ConstraintCategory::Return => write!(f, "returning this value "),
|
||||
ConstraintCategory::Cast => write!(f, "cast "),
|
||||
ConstraintCategory::CallArgument => write!(f, "argument "),
|
||||
_ => write!(f, ""),
|
||||
ConstraintCategory::TypeAnnotation => write!(f, "type annotation "),
|
||||
ConstraintCategory::ClosureBounds => write!(f, "closure body "),
|
||||
ConstraintCategory::SizedBound => write!(f, "proving this value is `Sized` "),
|
||||
ConstraintCategory::CopyBound => write!(f, "copying this value "),
|
||||
ConstraintCategory::OpaqueType => write!(f, "opaque type "),
|
||||
ConstraintCategory::Boring
|
||||
| ConstraintCategory::BoringNoLocation
|
||||
| ConstraintCategory::Internal => write!(f, ""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -71,7 +64,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
fn best_blame_constraint(
|
||||
&self,
|
||||
mir: &Mir<'tcx>,
|
||||
tcx: TyCtxt<'_, '_, 'tcx>,
|
||||
from_region: RegionVid,
|
||||
target_test: impl Fn(RegionVid) -> bool,
|
||||
) -> (ConstraintCategory, Span, RegionVid) {
|
||||
|
|
@ -96,7 +88,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
// Classify each of the constraints along the path.
|
||||
let mut categorized_path: Vec<(ConstraintCategory, Span)> = path
|
||||
.iter()
|
||||
.map(|&index| self.classify_constraint(index, mir, tcx))
|
||||
.map(|constraint| (constraint.category, constraint.locations.span(mir)))
|
||||
.collect();
|
||||
debug!(
|
||||
"best_blame_constraint: categorized_path={:#?}",
|
||||
|
|
@ -129,12 +121,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
|
||||
|
||||
match categorized_path[i].0 {
|
||||
ConstraintCategory::Boring => false,
|
||||
ConstraintCategory::Other => {
|
||||
// other isn't interesting when the two lifetimes
|
||||
// are unified.
|
||||
constraint_sup_scc != self.constraint_sccs.scc(constraint.sub)
|
||||
}
|
||||
ConstraintCategory::OpaqueType
|
||||
| ConstraintCategory::Boring
|
||||
| ConstraintCategory::BoringNoLocation
|
||||
| ConstraintCategory::Internal => false,
|
||||
_ => constraint_sup_scc != target_scc,
|
||||
}
|
||||
});
|
||||
|
|
@ -220,106 +210,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
None
|
||||
}
|
||||
|
||||
/// This function will return true if a constraint is interesting and false if a constraint
|
||||
/// is not. It is useful in filtering constraint paths to only interesting points.
|
||||
fn constraint_is_interesting(&self, constraint: OutlivesConstraint) -> bool {
|
||||
debug!(
|
||||
"constraint_is_interesting: locations={:?} constraint={:?}",
|
||||
constraint.locations, constraint
|
||||
);
|
||||
|
||||
match constraint.locations {
|
||||
Locations::Interesting(_) | Locations::All => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// This function classifies a constraint from a location.
|
||||
fn classify_constraint(
|
||||
&self,
|
||||
constraint: OutlivesConstraint,
|
||||
mir: &Mir<'tcx>,
|
||||
tcx: TyCtxt<'_, '_, 'tcx>,
|
||||
) -> (ConstraintCategory, Span) {
|
||||
debug!("classify_constraint: constraint={:?}", constraint);
|
||||
let span = constraint.locations.span(mir);
|
||||
let location = constraint
|
||||
.locations
|
||||
.from_location()
|
||||
.unwrap_or(Location::START);
|
||||
|
||||
if !self.constraint_is_interesting(constraint) {
|
||||
return (ConstraintCategory::Boring, span);
|
||||
}
|
||||
|
||||
let data = &mir[location.block];
|
||||
debug!(
|
||||
"classify_constraint: location={:?} data={:?}",
|
||||
location, data
|
||||
);
|
||||
let category = if location.statement_index == data.statements.len() {
|
||||
if let Some(ref terminator) = data.terminator {
|
||||
debug!("classify_constraint: terminator.kind={:?}", terminator.kind);
|
||||
match terminator.kind {
|
||||
TerminatorKind::DropAndReplace { .. } => ConstraintCategory::Assignment,
|
||||
// Classify calls differently depending on whether or not
|
||||
// the sub region appears in the destination type (so the
|
||||
// sup region is in the return type). If the return type
|
||||
// contains the sub-region, then this is either an
|
||||
// assignment or a return, depending on whether we are
|
||||
// writing to the RETURN_PLACE or not.
|
||||
//
|
||||
// The idea here is that the region is being propagated
|
||||
// from an input into the output place, so it's a kind of
|
||||
// assignment. Otherwise, if the sub-region only appears in
|
||||
// the argument types, then use the CallArgument
|
||||
// classification.
|
||||
TerminatorKind::Call { destination: Some((ref place, _)), .. } => {
|
||||
if tcx.any_free_region_meets(
|
||||
&place.ty(mir, tcx).to_ty(tcx),
|
||||
|region| self.to_region_vid(region) == constraint.sub,
|
||||
) {
|
||||
match place {
|
||||
Place::Local(mir::RETURN_PLACE) => ConstraintCategory::Return,
|
||||
_ => ConstraintCategory::Assignment,
|
||||
}
|
||||
} else {
|
||||
ConstraintCategory::CallArgument
|
||||
}
|
||||
}
|
||||
TerminatorKind::Call { destination: None, .. } => {
|
||||
ConstraintCategory::CallArgument
|
||||
}
|
||||
_ => ConstraintCategory::Other,
|
||||
}
|
||||
} else {
|
||||
ConstraintCategory::Other
|
||||
}
|
||||
} else {
|
||||
let statement = &data.statements[location.statement_index];
|
||||
debug!("classify_constraint: statement.kind={:?}", statement.kind);
|
||||
match statement.kind {
|
||||
StatementKind::Assign(ref place, ref rvalue) => {
|
||||
debug!("classify_constraint: place={:?} rvalue={:?}", place, rvalue);
|
||||
if *place == Place::Local(mir::RETURN_PLACE) {
|
||||
ConstraintCategory::Return
|
||||
} else {
|
||||
match rvalue {
|
||||
Rvalue::Cast(..) => ConstraintCategory::Cast,
|
||||
Rvalue::Use(..) | Rvalue::Aggregate(..) => {
|
||||
ConstraintCategory::Assignment
|
||||
}
|
||||
_ => ConstraintCategory::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => ConstraintCategory::Other,
|
||||
}
|
||||
};
|
||||
|
||||
(category, span)
|
||||
}
|
||||
|
||||
/// Report an error because the universal region `fr` was required to outlive
|
||||
/// `outlived_fr` but it is not known to do so. For example:
|
||||
///
|
||||
|
|
@ -341,7 +231,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
let (category, span, _) = self.best_blame_constraint(
|
||||
mir,
|
||||
infcx.tcx,
|
||||
fr,
|
||||
|r| r == outlived_fr
|
||||
);
|
||||
|
|
@ -574,11 +463,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
crate fn find_outlives_blame_span(
|
||||
&self,
|
||||
mir: &Mir<'tcx>,
|
||||
tcx: TyCtxt<'_, '_, 'tcx>,
|
||||
fr1: RegionVid,
|
||||
fr2: RegionVid,
|
||||
) -> Span {
|
||||
let (_, span, _) = self.best_blame_constraint(mir, tcx, fr1, |r| r == fr2);
|
||||
let (_, span, _) = self.best_blame_constraint(mir, fr1, |r| r == fr2);
|
||||
span
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1062,7 +1062,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
longer_fr, shorter_fr,
|
||||
);
|
||||
|
||||
let blame_span = self.find_outlives_blame_span(mir, infcx.tcx, longer_fr, shorter_fr);
|
||||
let blame_span = self.find_outlives_blame_span(mir, longer_fr, shorter_fr);
|
||||
|
||||
if let Some(propagated_outlives_requirements) = propagated_outlives_requirements {
|
||||
// Shrink `fr` until we find a non-local region (if we do).
|
||||
|
|
@ -1147,7 +1147,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
};
|
||||
|
||||
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
|
||||
let span = self.find_outlives_blame_span(mir, infcx.tcx, longer_fr, error_region);
|
||||
let span = self.find_outlives_blame_span(mir, longer_fr, error_region);
|
||||
|
||||
// Obviously, this error message is far from satisfactory.
|
||||
// At present, though, it only appears in unit tests --
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
// except according to those terms.
|
||||
|
||||
use borrow_check::location::LocationTable;
|
||||
use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint};
|
||||
use borrow_check::nll::constraints::{ConstraintCategory, ConstraintSet, OutlivesConstraint};
|
||||
use borrow_check::nll::facts::AllFacts;
|
||||
use borrow_check::nll::region_infer::{RegionTest, TypeTest};
|
||||
use borrow_check::nll::type_check::Locations;
|
||||
|
|
@ -30,6 +30,7 @@ crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
|||
implicit_region_bound: Option<ty::Region<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
outlives_constraints: &'a mut ConstraintSet,
|
||||
type_tests: &'a mut Vec<TypeTest<'tcx>>,
|
||||
all_facts: &'a mut Option<AllFacts>,
|
||||
|
|
@ -44,6 +45,7 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
|
|||
implicit_region_bound: Option<ty::Region<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
outlives_constraints: &'a mut ConstraintSet,
|
||||
type_tests: &'a mut Vec<TypeTest<'tcx>>,
|
||||
all_facts: &'a mut Option<AllFacts>,
|
||||
|
|
@ -56,6 +58,7 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
|
|||
implicit_region_bound,
|
||||
param_env,
|
||||
locations,
|
||||
category,
|
||||
outlives_constraints,
|
||||
type_tests,
|
||||
all_facts,
|
||||
|
|
@ -183,6 +186,7 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
|
|||
fn add_outlives(&mut self, sup: ty::RegionVid, sub: ty::RegionVid) {
|
||||
self.outlives_constraints.push(OutlivesConstraint {
|
||||
locations: self.locations,
|
||||
category: self.category,
|
||||
sub,
|
||||
sup,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use borrow_check::nll::type_check::constraint_conversion;
|
|||
use borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints};
|
||||
use borrow_check::nll::universal_regions::UniversalRegions;
|
||||
use borrow_check::nll::ToRegionVid;
|
||||
use borrow_check::nll::constraints::ConstraintCategory;
|
||||
use rustc::infer::canonical::QueryRegionConstraint;
|
||||
use rustc::infer::outlives::free_region_map::FreeRegionRelations;
|
||||
use rustc::infer::region_constraints::GenericKind;
|
||||
|
|
@ -23,6 +24,7 @@ use rustc::traits::query::type_op::{self, TypeOp};
|
|||
use rustc::ty::{self, RegionVid, Ty};
|
||||
use rustc_data_structures::transitive_relation::TransitiveRelation;
|
||||
use std::rc::Rc;
|
||||
use syntax_pos::DUMMY_SP;
|
||||
|
||||
#[derive(Debug)]
|
||||
crate struct UniversalRegionRelations<'tcx> {
|
||||
|
|
@ -283,7 +285,8 @@ impl UniversalRegionRelationsBuilder<'cx, 'gcx, 'tcx> {
|
|||
&self.region_bound_pairs,
|
||||
self.implicit_region_bound,
|
||||
self.param_env,
|
||||
Locations::All,
|
||||
Locations::All(DUMMY_SP),
|
||||
ConstraintCategory::Internal,
|
||||
&mut self.constraints.outlives_constraints,
|
||||
&mut self.constraints.type_tests,
|
||||
&mut self.all_facts,
|
||||
|
|
|
|||
|
|
@ -29,8 +29,9 @@ use rustc::ty::subst::Subst;
|
|||
use rustc::ty::Ty;
|
||||
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use syntax_pos::Span;
|
||||
|
||||
use super::{Locations, TypeChecker};
|
||||
use super::{ConstraintCategory, Locations, TypeChecker};
|
||||
|
||||
impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
||||
pub(super) fn equate_inputs_and_outputs(
|
||||
|
|
@ -56,7 +57,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
);
|
||||
|
||||
let mir_input_ty = mir.local_decls[local].ty;
|
||||
self.equate_normalized_input_or_output(normalized_input_ty, mir_input_ty);
|
||||
let mir_input_span = mir.local_decls[local].source_info.span;
|
||||
self.equate_normalized_input_or_output(
|
||||
normalized_input_ty,
|
||||
mir_input_ty,
|
||||
mir_input_span,
|
||||
);
|
||||
}
|
||||
|
||||
assert!(
|
||||
|
|
@ -65,16 +71,19 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
);
|
||||
if let Some(mir_yield_ty) = mir.yield_ty {
|
||||
let ur_yield_ty = universal_regions.yield_ty.unwrap();
|
||||
self.equate_normalized_input_or_output(ur_yield_ty, mir_yield_ty);
|
||||
let yield_span = mir.local_decls[RETURN_PLACE].source_info.span;
|
||||
self.equate_normalized_input_or_output(ur_yield_ty, mir_yield_ty, yield_span);
|
||||
}
|
||||
|
||||
// Return types are a bit more complex. They may contain existential `impl Trait`
|
||||
// types.
|
||||
let param_env = self.param_env;
|
||||
let mir_output_ty = mir.local_decls[RETURN_PLACE].ty;
|
||||
let output_span = mir.local_decls[RETURN_PLACE].source_info.span;
|
||||
let opaque_type_map =
|
||||
self.fully_perform_op(
|
||||
Locations::All,
|
||||
Locations::All(output_span),
|
||||
ConstraintCategory::BoringNoLocation,
|
||||
CustomTypeOp::new(
|
||||
|infcx| {
|
||||
let mut obligations = ObligationAccumulator::default();
|
||||
|
|
@ -152,26 +161,38 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
// prove that `T: Iterator` where `T` is the type we
|
||||
// instantiated it with).
|
||||
if let Some(opaque_type_map) = opaque_type_map {
|
||||
self.fully_perform_op(
|
||||
Locations::All,
|
||||
CustomTypeOp::new(
|
||||
|_cx| {
|
||||
infcx.constrain_opaque_types(&opaque_type_map, universal_region_relations);
|
||||
Ok(InferOk {
|
||||
value: (),
|
||||
obligations: vec![],
|
||||
})
|
||||
},
|
||||
|| "opaque_type_map".to_string(),
|
||||
),
|
||||
).unwrap();
|
||||
for (opaque_def_id, opaque_decl) in opaque_type_map {
|
||||
self.fully_perform_op(
|
||||
Locations::All(infcx.tcx.def_span(opaque_def_id)),
|
||||
ConstraintCategory::OpaqueType,
|
||||
CustomTypeOp::new(
|
||||
|_cx| {
|
||||
infcx.constrain_opaque_type(
|
||||
opaque_def_id,
|
||||
&opaque_decl,
|
||||
universal_region_relations
|
||||
);
|
||||
Ok(InferOk {
|
||||
value: (),
|
||||
obligations: vec![],
|
||||
})
|
||||
},
|
||||
|| "opaque_type_map".to_string(),
|
||||
),
|
||||
).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
|
||||
fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, span: Span) {
|
||||
debug!("equate_normalized_input_or_output(a={:?}, b={:?})", a, b);
|
||||
|
||||
if let Err(terr) = self.eq_types(a, b, Locations::All) {
|
||||
if let Err(terr) = self.eq_types(
|
||||
a,
|
||||
b,
|
||||
Locations::All(span),
|
||||
ConstraintCategory::BoringNoLocation,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
Location::START,
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use borrow_check::nll::constraints::ConstraintCategory;
|
||||
use borrow_check::nll::region_infer::values::{self, PointIndex, RegionValueElements};
|
||||
use borrow_check::nll::type_check::liveness::liveness_map::{LiveVar, NllLivenessMap};
|
||||
use borrow_check::nll::type_check::liveness::local_use_map::LocalUseMap;
|
||||
use borrow_check::nll::type_check::AtLocation;
|
||||
use borrow_check::nll::type_check::NormalizeLocation;
|
||||
use borrow_check::nll::type_check::TypeChecker;
|
||||
use dataflow::move_paths::indexes::MovePathIndex;
|
||||
use dataflow::move_paths::MoveData;
|
||||
|
|
@ -487,7 +488,11 @@ impl LivenessContext<'_, '_, '_, '_, 'tcx> {
|
|||
if let Some(data) = &drop_data.region_constraint_data {
|
||||
for &drop_location in drop_locations {
|
||||
self.typeck
|
||||
.push_region_constraints(drop_location.boring(), data);
|
||||
.push_region_constraints(
|
||||
drop_location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
data,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
use borrow_check::borrow_set::BorrowSet;
|
||||
use borrow_check::location::LocationTable;
|
||||
use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint};
|
||||
use borrow_check::nll::constraints::{ConstraintCategory, ConstraintSet, OutlivesConstraint};
|
||||
use borrow_check::nll::facts::AllFacts;
|
||||
use borrow_check::nll::region_infer::values::{LivenessValues, RegionValueElements};
|
||||
use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest};
|
||||
|
|
@ -252,7 +252,8 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
constant.ty,
|
||||
ty::Variance::Invariant,
|
||||
user_ty,
|
||||
location.boring(),
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
|
|
@ -281,7 +282,8 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
local_decl.ty,
|
||||
ty::Variance::Invariant,
|
||||
user_ty,
|
||||
Locations::All,
|
||||
Locations::All(local_decl.source_info.span),
|
||||
ConstraintCategory::TypeAnnotation,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
|
|
@ -364,14 +366,19 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
|
||||
type_checker.normalize_and_prove_instantiated_predicates(
|
||||
instantiated_predicates,
|
||||
location.boring(),
|
||||
location.to_locations(),
|
||||
);
|
||||
}
|
||||
|
||||
debug!("sanitize_constant: expected_ty={:?}", constant.literal.ty);
|
||||
|
||||
if let Err(terr) = self.cx
|
||||
.eq_types(constant.literal.ty, constant.ty, location.boring())
|
||||
.eq_types(
|
||||
constant.literal.ty,
|
||||
constant.ty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
)
|
||||
{
|
||||
span_mirbug!(
|
||||
self,
|
||||
|
|
@ -417,7 +424,12 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
let sty = self.sanitize_type(place, sty);
|
||||
let ty = self.tcx().type_of(def_id);
|
||||
let ty = self.cx.normalize(ty, location);
|
||||
if let Err(terr) = self.cx.eq_types(ty, sty, location.boring()) {
|
||||
if let Err(terr) = self.cx.eq_types(
|
||||
ty,
|
||||
sty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
place,
|
||||
|
|
@ -461,7 +473,11 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
// (e.g., #29149). Note that we decide to use Copy before knowing whether the bounds
|
||||
// fully apply: in effect, the rule is that if a value of some type could implement
|
||||
// Copy, then it must.
|
||||
self.cx.prove_trait_ref(trait_ref, location.interesting());
|
||||
self.cx.prove_trait_ref(
|
||||
trait_ref,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::CopyBound,
|
||||
);
|
||||
}
|
||||
place_ty
|
||||
}
|
||||
|
|
@ -560,7 +576,12 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
ProjectionElem::Field(field, fty) => {
|
||||
let fty = self.sanitize_type(place, fty);
|
||||
match self.field_ty(place, base, field, location) {
|
||||
Ok(ty) => if let Err(terr) = self.cx.eq_types(ty, fty, location.boring()) {
|
||||
Ok(ty) => if let Err(terr) = self.cx.eq_types(
|
||||
ty,
|
||||
fty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
place,
|
||||
|
|
@ -740,43 +761,32 @@ pub enum Locations {
|
|||
/// user-given type annotations; e.g., if the user wrote `let mut
|
||||
/// x: &'static u32 = ...`, we would ensure that all values
|
||||
/// assigned to `x` are of `'static` lifetime.
|
||||
All,
|
||||
|
||||
/// A "boring" constraint (caused by the given location) is one that
|
||||
/// the user probably doesn't want to see described in diagnostics,
|
||||
/// because it is kind of an artifact of the type system setup.
|
||||
///
|
||||
/// Example: `x = Foo { field: y }` technically creates
|
||||
/// intermediate regions representing the "type of `Foo { field: y
|
||||
/// }`", and data flows from `y` into those variables, but they
|
||||
/// are not very interesting. The assignment into `x` on the other
|
||||
/// hand might be.
|
||||
Boring(Location),
|
||||
/// The span points to the place the constraint arose. For example,
|
||||
/// it points to the type in a user-given type annotation. If
|
||||
/// there's no sensible span then it's DUMMY_SP.
|
||||
All(Span),
|
||||
|
||||
/// An *important* outlives constraint (caused by the given
|
||||
/// location) is one that would be useful to highlight in
|
||||
/// diagnostics, because it represents a point where references
|
||||
/// flow from one spot to another (e.g., `x = y`)
|
||||
Interesting(Location),
|
||||
/// An outlives constraint that only has to hold at a single location,
|
||||
/// usually it represents a point where references flow from one spot to
|
||||
/// another (e.g., `x = y`)
|
||||
Single(Location),
|
||||
}
|
||||
|
||||
impl Locations {
|
||||
pub fn from_location(&self) -> Option<Location> {
|
||||
match self {
|
||||
Locations::All => None,
|
||||
Locations::Boring(from_location) | Locations::Interesting(from_location) => {
|
||||
Some(*from_location)
|
||||
}
|
||||
Locations::All(_) => None,
|
||||
Locations::Single(from_location) => Some(*from_location),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a span representing the location.
|
||||
pub fn span(&self, mir: &Mir<'_>) -> Span {
|
||||
let span_location = match self {
|
||||
Locations::All => Location::START,
|
||||
Locations::Boring(l) | Locations::Interesting(l) => *l,
|
||||
};
|
||||
mir.source_info(span_location).span
|
||||
match self {
|
||||
Locations::All(span) => *span,
|
||||
Locations::Single(l) => mir.source_info(*l).span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -816,12 +826,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
fn fully_perform_op<R>(
|
||||
&mut self,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
op: impl type_op::TypeOp<'gcx, 'tcx, Output = R>,
|
||||
) -> Fallible<R> {
|
||||
let (r, opt_data) = op.fully_perform(self.infcx)?;
|
||||
|
||||
if let Some(data) = &opt_data {
|
||||
self.push_region_constraints(locations, data);
|
||||
self.push_region_constraints(locations, category, data);
|
||||
}
|
||||
|
||||
Ok(r)
|
||||
|
|
@ -830,6 +841,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
fn push_region_constraints(
|
||||
&mut self,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
data: &[QueryRegionConstraint<'tcx>],
|
||||
) {
|
||||
debug!(
|
||||
|
|
@ -846,6 +858,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
self.implicit_region_bound,
|
||||
self.param_env,
|
||||
locations,
|
||||
category,
|
||||
&mut borrowck_context.constraints.outlives_constraints,
|
||||
&mut borrowck_context.constraints.type_tests,
|
||||
&mut borrowck_context.all_facts,
|
||||
|
|
@ -853,22 +866,36 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>, locations: Locations) -> Fallible<()> {
|
||||
fn sub_types(
|
||||
&mut self,
|
||||
sub: Ty<'tcx>,
|
||||
sup: Ty<'tcx>,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
) -> Fallible<()> {
|
||||
relate_tys::sub_types(
|
||||
self.infcx,
|
||||
sub,
|
||||
sup,
|
||||
locations,
|
||||
category,
|
||||
self.borrowck_context.as_mut().map(|x| &mut **x),
|
||||
)
|
||||
}
|
||||
|
||||
fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> Fallible<()> {
|
||||
fn eq_types(
|
||||
&mut self,
|
||||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
) -> Fallible<()> {
|
||||
relate_tys::eq_types(
|
||||
self.infcx,
|
||||
a,
|
||||
b,
|
||||
locations,
|
||||
category,
|
||||
self.borrowck_context.as_mut().map(|x| &mut **x),
|
||||
)
|
||||
}
|
||||
|
|
@ -879,6 +906,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
v: ty::Variance,
|
||||
b: CanonicalTy<'tcx>,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
) -> Fallible<()> {
|
||||
relate_tys::relate_type_and_user_type(
|
||||
self.infcx,
|
||||
|
|
@ -886,6 +914,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
v,
|
||||
b,
|
||||
locations,
|
||||
category,
|
||||
self.borrowck_context.as_mut().map(|x| &mut **x),
|
||||
)
|
||||
}
|
||||
|
|
@ -903,21 +932,22 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
// they are not caused by the user, but rather artifacts
|
||||
// of lowering. Assignments to other sorts of places *are* interesting
|
||||
// though.
|
||||
let is_temp = if let Place::Local(l) = *place {
|
||||
l != RETURN_PLACE && !mir.local_decls[l].is_user_variable.is_some()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let locations = if is_temp {
|
||||
location.boring()
|
||||
} else {
|
||||
location.interesting()
|
||||
let category = match *place {
|
||||
Place::Local(RETURN_PLACE) => ConstraintCategory::Return,
|
||||
Place::Local(l) if !mir.local_decls[l].is_user_variable.is_some() => {
|
||||
ConstraintCategory::Boring
|
||||
}
|
||||
_ => ConstraintCategory::Assignment,
|
||||
};
|
||||
|
||||
let place_ty = place.ty(mir, tcx).to_ty(tcx);
|
||||
let rv_ty = rv.ty(mir, tcx);
|
||||
if let Err(terr) = self.sub_types(rv_ty, place_ty, locations) {
|
||||
if let Err(terr) = self.sub_types(
|
||||
rv_ty,
|
||||
place_ty,
|
||||
location.to_locations(),
|
||||
category,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
stmt,
|
||||
|
|
@ -933,7 +963,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
rv_ty,
|
||||
ty::Variance::Invariant,
|
||||
user_ty,
|
||||
location.boring(),
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
|
|
@ -952,7 +983,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
def_id: tcx.lang_items().sized_trait().unwrap(),
|
||||
substs: tcx.mk_substs_trait(place_ty, &[]),
|
||||
};
|
||||
self.prove_trait_ref(trait_ref, location.interesting());
|
||||
self.prove_trait_ref(
|
||||
trait_ref,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::SizedBound,
|
||||
);
|
||||
}
|
||||
}
|
||||
StatementKind::SetDiscriminant {
|
||||
|
|
@ -983,7 +1018,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
StatementKind::AscribeUserType(ref place, variance, c_ty) => {
|
||||
let place_ty = place.ty(mir, tcx).to_ty(tcx);
|
||||
if let Err(terr) =
|
||||
self.relate_type_and_user_type(place_ty, variance, c_ty, Locations::All)
|
||||
self.relate_type_and_user_type(
|
||||
place_ty,
|
||||
variance,
|
||||
c_ty,
|
||||
Locations::All(stmt.source_info.span),
|
||||
ConstraintCategory::TypeAnnotation,
|
||||
)
|
||||
{
|
||||
span_mirbug!(
|
||||
self,
|
||||
|
|
@ -1035,8 +1076,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
let place_ty = location.ty(mir, tcx).to_ty(tcx);
|
||||
let rv_ty = value.ty(mir, tcx);
|
||||
|
||||
let locations = term_location.interesting();
|
||||
if let Err(terr) = self.sub_types(rv_ty, place_ty, locations) {
|
||||
let locations = term_location.to_locations();
|
||||
if let Err(terr) = self.sub_types(
|
||||
rv_ty,
|
||||
place_ty,
|
||||
locations,
|
||||
ConstraintCategory::Assignment,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
term,
|
||||
|
|
@ -1053,7 +1099,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
..
|
||||
} => {
|
||||
let discr_ty = discr.ty(mir, tcx);
|
||||
if let Err(terr) = self.sub_types(discr_ty, switch_ty, term_location.boring()) {
|
||||
if let Err(terr) = self.sub_types(
|
||||
discr_ty,
|
||||
switch_ty,
|
||||
term_location.to_locations(),
|
||||
ConstraintCategory::Assignment,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
term,
|
||||
|
|
@ -1093,7 +1144,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
|
||||
self.prove_predicates(
|
||||
sig.inputs().iter().map(|ty| ty::Predicate::WellFormed(ty)),
|
||||
term_location.boring(),
|
||||
term_location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
);
|
||||
|
||||
// The ordinary liveness rules will ensure that all
|
||||
|
|
@ -1139,7 +1191,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
match mir.yield_ty {
|
||||
None => span_mirbug!(self, term, "yield in non-generator"),
|
||||
Some(ty) => {
|
||||
if let Err(terr) = self.sub_types(value_ty, ty, term_location.interesting())
|
||||
if let Err(terr) = self.sub_types(
|
||||
value_ty,
|
||||
ty,
|
||||
term_location.to_locations(),
|
||||
ConstraintCategory::Return,
|
||||
)
|
||||
{
|
||||
span_mirbug!(
|
||||
self,
|
||||
|
|
@ -1168,19 +1225,22 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
match *destination {
|
||||
Some((ref dest, _target_block)) => {
|
||||
let dest_ty = dest.ty(mir, tcx).to_ty(tcx);
|
||||
let is_temp = if let Place::Local(l) = *dest {
|
||||
l != RETURN_PLACE && !mir.local_decls[l].is_user_variable.is_some()
|
||||
} else {
|
||||
false
|
||||
let category = match *dest {
|
||||
Place::Local(RETURN_PLACE) => ConstraintCategory::Return,
|
||||
Place::Local(l) if !mir.local_decls[l].is_user_variable.is_some() => {
|
||||
ConstraintCategory::Boring
|
||||
}
|
||||
_ => ConstraintCategory::Assignment,
|
||||
};
|
||||
|
||||
let locations = if is_temp {
|
||||
term_location.boring()
|
||||
} else {
|
||||
term_location.interesting()
|
||||
};
|
||||
let locations = term_location.to_locations();
|
||||
|
||||
if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations) {
|
||||
if let Err(terr) = self.sub_types(
|
||||
sig.output(),
|
||||
dest_ty,
|
||||
locations,
|
||||
category,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
term,
|
||||
|
|
@ -1221,7 +1281,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() {
|
||||
let op_arg_ty = op_arg.ty(mir, self.tcx());
|
||||
if let Err(terr) = self.sub_types(op_arg_ty, fn_arg, term_location.interesting()) {
|
||||
if let Err(terr) = self.sub_types(
|
||||
op_arg_ty,
|
||||
fn_arg,
|
||||
term_location.to_locations(),
|
||||
ConstraintCategory::CallArgument,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
term,
|
||||
|
|
@ -1470,7 +1535,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
substs: tcx.mk_substs_trait(operand_ty, &[]),
|
||||
};
|
||||
|
||||
self.prove_trait_ref(trait_ref, location.interesting());
|
||||
self.prove_trait_ref(
|
||||
trait_ref,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::CopyBound,
|
||||
);
|
||||
},
|
||||
|
||||
Rvalue::NullaryOp(_, ty) => {
|
||||
|
|
@ -1485,24 +1554,34 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
substs: tcx.mk_substs_trait(ty, &[]),
|
||||
};
|
||||
|
||||
self.prove_trait_ref(trait_ref, location.interesting());
|
||||
self.prove_trait_ref(
|
||||
trait_ref,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::SizedBound,
|
||||
);
|
||||
}
|
||||
|
||||
Rvalue::Cast(cast_kind, op, ty) => match cast_kind {
|
||||
CastKind::ReifyFnPointer => {
|
||||
let fn_sig = op.ty(mir, tcx).fn_sig(tcx);
|
||||
Rvalue::Cast(cast_kind, op, ty) => {
|
||||
match cast_kind {
|
||||
CastKind::ReifyFnPointer => {
|
||||
let fn_sig = op.ty(mir, tcx).fn_sig(tcx);
|
||||
|
||||
// The type that we see in the fcx is like
|
||||
// `foo::<'a, 'b>`, where `foo` is the path to a
|
||||
// function definition. When we extract the
|
||||
// signature, it comes from the `fn_sig` query,
|
||||
// and hence may contain unnormalized results.
|
||||
let fn_sig = self.normalize(fn_sig, location);
|
||||
// The type that we see in the fcx is like
|
||||
// `foo::<'a, 'b>`, where `foo` is the path to a
|
||||
// function definition. When we extract the
|
||||
// signature, it comes from the `fn_sig` query,
|
||||
// and hence may contain unnormalized results.
|
||||
let fn_sig = self.normalize(fn_sig, location);
|
||||
|
||||
let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig);
|
||||
let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig);
|
||||
|
||||
if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.interesting()) {
|
||||
span_mirbug!(
|
||||
if let Err(terr) = self.eq_types(
|
||||
ty_fn_ptr_from,
|
||||
ty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
rvalue,
|
||||
"equating {:?} with {:?} yields {:?}",
|
||||
|
|
@ -1510,20 +1589,25 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
ty,
|
||||
terr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CastKind::ClosureFnPointer => {
|
||||
let sig = match op.ty(mir, tcx).sty {
|
||||
ty::Closure(def_id, substs) => {
|
||||
substs.closure_sig_ty(def_id, tcx).fn_sig(tcx)
|
||||
}
|
||||
_ => bug!(),
|
||||
};
|
||||
let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig);
|
||||
}
|
||||
|
||||
if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.interesting()) {
|
||||
span_mirbug!(
|
||||
CastKind::ClosureFnPointer => {
|
||||
let sig = match op.ty(mir, tcx).sty {
|
||||
ty::Closure(def_id, substs) => {
|
||||
substs.closure_sig_ty(def_id, tcx).fn_sig(tcx)
|
||||
}
|
||||
_ => bug!(),
|
||||
};
|
||||
let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig);
|
||||
|
||||
if let Err(terr) = self.eq_types(
|
||||
ty_fn_ptr_from,
|
||||
ty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
rvalue,
|
||||
"equating {:?} with {:?} yields {:?}",
|
||||
|
|
@ -1531,23 +1615,28 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
ty,
|
||||
terr
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CastKind::UnsafeFnPointer => {
|
||||
let fn_sig = op.ty(mir, tcx).fn_sig(tcx);
|
||||
CastKind::UnsafeFnPointer => {
|
||||
let fn_sig = op.ty(mir, tcx).fn_sig(tcx);
|
||||
|
||||
// The type that we see in the fcx is like
|
||||
// `foo::<'a, 'b>`, where `foo` is the path to a
|
||||
// function definition. When we extract the
|
||||
// signature, it comes from the `fn_sig` query,
|
||||
// and hence may contain unnormalized results.
|
||||
let fn_sig = self.normalize(fn_sig, location);
|
||||
// The type that we see in the fcx is like
|
||||
// `foo::<'a, 'b>`, where `foo` is the path to a
|
||||
// function definition. When we extract the
|
||||
// signature, it comes from the `fn_sig` query,
|
||||
// and hence may contain unnormalized results.
|
||||
let fn_sig = self.normalize(fn_sig, location);
|
||||
|
||||
let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig);
|
||||
let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig);
|
||||
|
||||
if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.interesting()) {
|
||||
span_mirbug!(
|
||||
if let Err(terr) = self.eq_types(
|
||||
ty_fn_ptr_from,
|
||||
ty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
rvalue,
|
||||
"equating {:?} with {:?} yields {:?}",
|
||||
|
|
@ -1555,21 +1644,26 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
ty,
|
||||
terr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CastKind::Unsize => {
|
||||
let &ty = ty;
|
||||
let trait_ref = ty::TraitRef {
|
||||
def_id: tcx.lang_items().coerce_unsized_trait().unwrap(),
|
||||
substs: tcx.mk_substs_trait(op.ty(mir, tcx), &[ty.into()]),
|
||||
};
|
||||
|
||||
self.prove_trait_ref(
|
||||
trait_ref,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast,
|
||||
);
|
||||
}
|
||||
|
||||
CastKind::Misc => {}
|
||||
}
|
||||
|
||||
CastKind::Unsize => {
|
||||
let &ty = ty;
|
||||
let trait_ref = ty::TraitRef {
|
||||
def_id: tcx.lang_items().coerce_unsized_trait().unwrap(),
|
||||
substs: tcx.mk_substs_trait(op.ty(mir, tcx), &[ty.into()]),
|
||||
};
|
||||
|
||||
self.prove_trait_ref(trait_ref, location.interesting());
|
||||
}
|
||||
|
||||
CastKind::Misc => {}
|
||||
},
|
||||
}
|
||||
|
||||
Rvalue::Ref(region, _borrow_kind, borrowed_place) => {
|
||||
self.add_reborrow_constraint(location, region, borrowed_place);
|
||||
|
|
@ -1644,7 +1738,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
};
|
||||
let operand_ty = operand.ty(mir, tcx);
|
||||
|
||||
if let Err(terr) = self.sub_types(operand_ty, field_ty, location.boring()) {
|
||||
if let Err(terr) = self.sub_types(
|
||||
operand_ty,
|
||||
field_ty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
rvalue,
|
||||
|
|
@ -1723,7 +1822,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
constraints.outlives_constraints.push(OutlivesConstraint {
|
||||
sup: ref_region.to_region_vid(),
|
||||
sub: borrow_region.to_region_vid(),
|
||||
locations: location.boring(),
|
||||
locations: location.to_locations(),
|
||||
category: ConstraintCategory::Boring,
|
||||
});
|
||||
|
||||
if let Some(all_facts) = all_facts {
|
||||
|
|
@ -1839,8 +1939,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
*substs,
|
||||
);
|
||||
|
||||
// Hmm, are these constraints *really* boring?
|
||||
self.push_region_constraints(location.boring(), &closure_constraints);
|
||||
self.push_region_constraints(
|
||||
location.to_locations(),
|
||||
ConstraintCategory::ClosureBounds,
|
||||
&closure_constraints,
|
||||
);
|
||||
}
|
||||
|
||||
tcx.predicates_of(*def_id).instantiate(tcx, substs.substs)
|
||||
|
|
@ -1855,16 +1958,22 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
|
||||
self.normalize_and_prove_instantiated_predicates(
|
||||
instantiated_predicates,
|
||||
location.boring(),
|
||||
location.to_locations(),
|
||||
);
|
||||
}
|
||||
|
||||
fn prove_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>, locations: Locations) {
|
||||
fn prove_trait_ref(
|
||||
&mut self,
|
||||
trait_ref: ty::TraitRef<'tcx>,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
) {
|
||||
self.prove_predicates(
|
||||
Some(ty::Predicate::Trait(
|
||||
trait_ref.to_poly_trait_ref().to_poly_trait_predicate(),
|
||||
)),
|
||||
locations,
|
||||
category,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1875,7 +1984,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
) {
|
||||
for predicate in instantiated_predicates.predicates {
|
||||
let predicate = self.normalize(predicate, locations);
|
||||
self.prove_predicate(predicate, locations);
|
||||
self.prove_predicate(predicate, locations, ConstraintCategory::Boring);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1883,6 +1992,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
&mut self,
|
||||
predicates: impl IntoIterator<Item = ty::Predicate<'tcx>>,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
) {
|
||||
for predicate in predicates {
|
||||
debug!(
|
||||
|
|
@ -1890,11 +2000,16 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
predicate, locations,
|
||||
);
|
||||
|
||||
self.prove_predicate(predicate, locations);
|
||||
self.prove_predicate(predicate, locations, category);
|
||||
}
|
||||
}
|
||||
|
||||
fn prove_predicate(&mut self, predicate: ty::Predicate<'tcx>, locations: Locations) {
|
||||
fn prove_predicate(
|
||||
&mut self,
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
) {
|
||||
debug!(
|
||||
"prove_predicate(predicate={:?}, location={:?})",
|
||||
predicate, locations,
|
||||
|
|
@ -1903,6 +2018,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
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);
|
||||
|
|
@ -1943,6 +2059,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
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);
|
||||
|
|
@ -1996,26 +2113,6 @@ impl MirPass for TypeckMir {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait AtLocation {
|
||||
/// Indicates a "boring" constraint that the user probably
|
||||
/// woudln't want to see highlights.
|
||||
fn boring(self) -> Locations;
|
||||
|
||||
/// Indicates an "interesting" edge, which is of significance only
|
||||
/// for diagnostics.
|
||||
fn interesting(self) -> Locations;
|
||||
}
|
||||
|
||||
impl AtLocation for Location {
|
||||
fn boring(self) -> Locations {
|
||||
Locations::Boring(self)
|
||||
}
|
||||
|
||||
fn interesting(self) -> Locations {
|
||||
Locations::Interesting(self)
|
||||
}
|
||||
}
|
||||
|
||||
trait NormalizeLocation: fmt::Debug + Copy {
|
||||
fn to_locations(self) -> Locations;
|
||||
}
|
||||
|
|
@ -2028,6 +2125,6 @@ impl NormalizeLocation for Locations {
|
|||
|
||||
impl NormalizeLocation for Location {
|
||||
fn to_locations(self) -> Locations {
|
||||
self.boring()
|
||||
Locations::Single(self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use borrow_check::nll::constraints::OutlivesConstraint;
|
||||
use borrow_check::nll::constraints::{ConstraintCategory, OutlivesConstraint};
|
||||
use borrow_check::nll::type_check::{BorrowCheckContext, Locations};
|
||||
use borrow_check::nll::universal_regions::UniversalRegions;
|
||||
use borrow_check::nll::ToRegionVid;
|
||||
|
|
@ -28,6 +28,7 @@ pub(super) fn sub_types<'tcx>(
|
|||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>,
|
||||
) -> Fallible<()> {
|
||||
debug!("sub_types(a={:?}, b={:?}, locations={:?})", a, b, locations);
|
||||
|
|
@ -35,6 +36,7 @@ pub(super) fn sub_types<'tcx>(
|
|||
infcx,
|
||||
ty::Variance::Covariant,
|
||||
locations,
|
||||
category,
|
||||
borrowck_context,
|
||||
ty::List::empty(),
|
||||
).relate(&a, &b)?;
|
||||
|
|
@ -47,6 +49,7 @@ pub(super) fn eq_types<'tcx>(
|
|||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>,
|
||||
) -> Fallible<()> {
|
||||
debug!("eq_types(a={:?}, b={:?}, locations={:?})", a, b, locations);
|
||||
|
|
@ -54,6 +57,7 @@ pub(super) fn eq_types<'tcx>(
|
|||
infcx,
|
||||
ty::Variance::Invariant,
|
||||
locations,
|
||||
category,
|
||||
borrowck_context,
|
||||
ty::List::empty(),
|
||||
).relate(&a, &b)?;
|
||||
|
|
@ -69,6 +73,7 @@ pub(super) fn relate_type_and_user_type<'tcx>(
|
|||
v: ty::Variance,
|
||||
b: CanonicalTy<'tcx>,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>,
|
||||
) -> Fallible<()> {
|
||||
debug!(
|
||||
|
|
@ -89,6 +94,7 @@ pub(super) fn relate_type_and_user_type<'tcx>(
|
|||
infcx,
|
||||
v1,
|
||||
locations,
|
||||
category,
|
||||
borrowck_context,
|
||||
b_variables,
|
||||
).relate(&b_value, &a)?;
|
||||
|
|
@ -124,6 +130,8 @@ struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> {
|
|||
/// Where (and why) is this relation taking place?
|
||||
locations: Locations,
|
||||
|
||||
category: ConstraintCategory,
|
||||
|
||||
/// This will be `Some` when we are running the type check as part
|
||||
/// of NLL, and `None` if we are running a "sanity check".
|
||||
borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>,
|
||||
|
|
@ -161,6 +169,7 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
|
|||
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
ambient_variance: ty::Variance,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory,
|
||||
borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>,
|
||||
canonical_var_infos: CanonicalVarInfos<'tcx>,
|
||||
) -> Self {
|
||||
|
|
@ -171,6 +180,7 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
|
|||
borrowck_context,
|
||||
locations,
|
||||
canonical_var_values,
|
||||
category,
|
||||
a_scopes: vec![],
|
||||
b_scopes: vec![],
|
||||
}
|
||||
|
|
@ -264,6 +274,7 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
|
|||
sup,
|
||||
sub,
|
||||
locations: self.locations,
|
||||
category: self.category,
|
||||
});
|
||||
|
||||
// FIXME all facts!
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue