summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_borrowck/src/type_check
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_borrowck/src/type_check')
-rw-r--r--compiler/rustc_borrowck/src/type_check/canonical.rs171
-rw-r--r--compiler/rustc_borrowck/src/type_check/constraint_conversion.rs204
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs374
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs245
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs170
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/mod.rs139
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/polonius.rs140
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/trace.rs578
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs2721
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs187
10 files changed, 4929 insertions, 0 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs
new file mode 100644
index 000000000..6cfe5efb6
--- /dev/null
+++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -0,0 +1,171 @@
+use std::fmt;
+
+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::def_id::DefId;
+use rustc_span::Span;
+use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput};
+use rustc_trait_selection::traits::query::Fallible;
+
+use crate::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!**
+ #[instrument(skip(self, category, op), level = "trace")]
+ pub(super) fn fully_perform_op<R, Op>(
+ &mut self,
+ locations: Locations,
+ category: ConstraintCategory<'tcx>,
+ op: Op,
+ ) -> Fallible<R>
+ where
+ Op: type_op::TypeOp<'tcx, Output = R>,
+ Op::ErrorInfo: ToUniverseInfo<'tcx>,
+ {
+ let old_universe = self.infcx.universe();
+
+ let TypeOpOutput { output, constraints, error_info } = 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 error_info {
+ Some(error_info) => error_info.to_universe_info(old_universe),
+ None => UniverseInfo::other(),
+ };
+ for u in old_universe..universe {
+ self.borrowck_context
+ .constraints
+ .universe_causes
+ .insert(u + 1, universe_info.clone());
+ }
+ }
+
+ 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 u in 0..canonical.max_universe.as_u32() {
+ let info = UniverseInfo::other();
+ self.borrowck_context
+ .constraints
+ .universe_causes
+ .insert(ty::UniverseIndex::from_u32(u), info);
+ }
+
+ instantiated
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ pub(super) fn prove_trait_ref(
+ &mut self,
+ trait_ref: ty::TraitRef<'tcx>,
+ locations: Locations,
+ category: ConstraintCategory<'tcx>,
+ ) {
+ self.prove_predicates(
+ Some(ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate {
+ trait_ref,
+ constness: ty::BoundConstness::NotConst,
+ polarity: ty::ImplPolarity::Positive,
+ }))),
+ locations,
+ category,
+ );
+ }
+
+ pub(super) fn normalize_and_prove_instantiated_predicates(
+ &mut self,
+ // Keep this parameter for now, in case we start using
+ // it in `ConstraintCategory` at some point.
+ _def_id: DefId,
+ instantiated_predicates: ty::InstantiatedPredicates<'tcx>,
+ locations: Locations,
+ ) {
+ for (predicate, span) in instantiated_predicates
+ .predicates
+ .into_iter()
+ .zip(instantiated_predicates.spans.into_iter())
+ {
+ debug!(?predicate);
+ let predicate = self.normalize(predicate, locations);
+ self.prove_predicate(predicate, locations, ConstraintCategory::Predicate(span));
+ }
+ }
+
+ pub(super) fn prove_predicates(
+ &mut self,
+ predicates: impl IntoIterator<Item = impl ToPredicate<'tcx>>,
+ locations: Locations,
+ category: ConstraintCategory<'tcx>,
+ ) {
+ for predicate in predicates {
+ let predicate = predicate.to_predicate(self.tcx());
+ debug!("prove_predicates(predicate={:?}, locations={:?})", predicate, locations,);
+
+ self.prove_predicate(predicate, locations, category);
+ }
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ pub(super) fn prove_predicate(
+ &mut self,
+ predicate: ty::Predicate<'tcx>,
+ locations: Locations,
+ category: ConstraintCategory<'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);
+ })
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ pub(super) fn normalize<T>(&mut self, value: T, location: impl NormalizeLocation) -> T
+ where
+ T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + '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);
+ value
+ })
+ }
+}
diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
new file mode 100644
index 000000000..167960918
--- /dev/null
+++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
@@ -0,0 +1,204 @@
+use rustc_infer::infer::canonical::QueryOutlivesConstraint;
+use rustc_infer::infer::canonical::QueryRegionConstraints;
+use rustc_infer::infer::outlives::env::RegionBoundPairs;
+use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate};
+use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound};
+use rustc_infer::infer::{self, InferCtxt, SubregionOrigin};
+use rustc_middle::mir::ConstraintCategory;
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::TypeVisitable;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::{Span, DUMMY_SP};
+
+use crate::{
+ constraints::OutlivesConstraint,
+ nll::ToRegionVid,
+ region_infer::TypeTest,
+ type_check::{Locations, MirTypeckRegionConstraints},
+ universal_regions::UniversalRegions,
+};
+
+pub(crate) struct ConstraintConversion<'a, 'tcx> {
+ infcx: &'a InferCtxt<'a, 'tcx>,
+ tcx: TyCtxt<'tcx>,
+ universal_regions: &'a UniversalRegions<'tcx>,
+ /// Each RBP `GK: 'a` is assumed to be true. These encode
+ /// relationships like `T: 'a` that are added via implicit bounds
+ /// or the `param_env`.
+ ///
+ /// Each region here is guaranteed to be a key in the `indices`
+ /// map. We use the "original" regions (i.e., the keys from the
+ /// map, and not the values) because the code in
+ /// `process_registered_region_obligations` has some special-cased
+ /// logic expecting to see (e.g.) `ReStatic`, and if we supplied
+ /// our special inference variable there, we would mess that up.
+ region_bound_pairs: &'a RegionBoundPairs<'tcx>,
+ implicit_region_bound: ty::Region<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ locations: Locations,
+ span: Span,
+ category: ConstraintCategory<'tcx>,
+ constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
+}
+
+impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
+ pub(crate) fn new(
+ infcx: &'a InferCtxt<'a, 'tcx>,
+ universal_regions: &'a UniversalRegions<'tcx>,
+ region_bound_pairs: &'a RegionBoundPairs<'tcx>,
+ implicit_region_bound: ty::Region<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ locations: Locations,
+ span: Span,
+ category: ConstraintCategory<'tcx>,
+ constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
+ ) -> Self {
+ Self {
+ infcx,
+ tcx: infcx.tcx,
+ universal_regions,
+ region_bound_pairs,
+ implicit_region_bound,
+ param_env,
+ locations,
+ span,
+ category,
+ constraints,
+ }
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) {
+ let QueryRegionConstraints { outlives, member_constraints } = query_constraints;
+
+ // Annoying: to invoke `self.to_region_vid`, we need access to
+ // `self.constraints`, but we also want to be mutating
+ // `self.member_constraints`. For now, just swap out the value
+ // we want and replace at the end.
+ let mut tmp = std::mem::take(&mut self.constraints.member_constraints);
+ for member_constraint in member_constraints {
+ tmp.push_constraint(member_constraint, |r| self.to_region_vid(r));
+ }
+ self.constraints.member_constraints = tmp;
+
+ for query_constraint in outlives {
+ self.convert(query_constraint);
+ }
+ }
+
+ pub(super) fn convert(&mut self, query_constraint: &QueryOutlivesConstraint<'tcx>) {
+ debug!("generate: constraints at: {:#?}", self.locations);
+
+ // Extract out various useful fields we'll need below.
+ let ConstraintConversion {
+ tcx, region_bound_pairs, implicit_region_bound, param_env, ..
+ } = *self;
+
+ // At the moment, we never generate any "higher-ranked"
+ // region constraints like `for<'a> 'a: 'b`. At some point
+ // when we move to universes, we will, and this assertion
+ // will start to fail.
+ let ty::OutlivesPredicate(k1, r2) = query_constraint.no_bound_vars().unwrap_or_else(|| {
+ bug!("query_constraint {:?} contained bound vars", query_constraint,);
+ });
+
+ match k1.unpack() {
+ GenericArgKind::Lifetime(r1) => {
+ let r1_vid = self.to_region_vid(r1);
+ let r2_vid = self.to_region_vid(r2);
+ self.add_outlives(r1_vid, r2_vid);
+ }
+
+ GenericArgKind::Type(mut t1) => {
+ // we don't actually use this for anything, but
+ // the `TypeOutlives` code needs an origin.
+ let origin = infer::RelateParamBound(DUMMY_SP, t1, None);
+
+ // Placeholder regions need to be converted now because it may
+ // create new region variables, which can't be done later when
+ // verifying these bounds.
+ if t1.has_placeholders() {
+ t1 = tcx.fold_regions(t1, |r, _| match *r {
+ ty::RePlaceholder(placeholder) => {
+ self.constraints.placeholder_region(self.infcx, placeholder)
+ }
+ _ => r,
+ });
+ }
+
+ TypeOutlives::new(
+ &mut *self,
+ tcx,
+ region_bound_pairs,
+ Some(implicit_region_bound),
+ param_env,
+ )
+ .type_must_outlive(origin, t1, r2);
+ }
+
+ GenericArgKind::Const(_) => {
+ // Consts cannot outlive one another, so we
+ // don't need to handle any relations here.
+ }
+ }
+ }
+
+ fn verify_to_type_test(
+ &mut self,
+ generic_kind: GenericKind<'tcx>,
+ region: ty::Region<'tcx>,
+ verify_bound: VerifyBound<'tcx>,
+ ) -> TypeTest<'tcx> {
+ let lower_bound = self.to_region_vid(region);
+
+ TypeTest { generic_kind, lower_bound, locations: self.locations, verify_bound }
+ }
+
+ fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> ty::RegionVid {
+ if let ty::RePlaceholder(placeholder) = *r {
+ self.constraints.placeholder_region(self.infcx, placeholder).to_region_vid()
+ } else {
+ self.universal_regions.to_region_vid(r)
+ }
+ }
+
+ fn add_outlives(&mut self, sup: ty::RegionVid, sub: ty::RegionVid) {
+ self.constraints.outlives_constraints.push(OutlivesConstraint {
+ locations: self.locations,
+ category: self.category,
+ span: self.span,
+ sub,
+ sup,
+ variance_info: ty::VarianceDiagInfo::default(),
+ });
+ }
+
+ fn add_type_test(&mut self, type_test: TypeTest<'tcx>) {
+ debug!("add_type_test(type_test={:?})", type_test);
+ self.constraints.type_tests.push(type_test);
+ }
+}
+
+impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'b, 'tcx> {
+ fn push_sub_region_constraint(
+ &mut self,
+ _origin: SubregionOrigin<'tcx>,
+ a: ty::Region<'tcx>,
+ b: ty::Region<'tcx>,
+ ) {
+ let b = self.to_region_vid(b);
+ let a = self.to_region_vid(a);
+ self.add_outlives(b, a);
+ }
+
+ fn push_verify(
+ &mut self,
+ _origin: SubregionOrigin<'tcx>,
+ kind: GenericKind<'tcx>,
+ a: ty::Region<'tcx>,
+ bound: VerifyBound<'tcx>,
+ ) {
+ let type_test = self.verify_to_type_test(kind, a, bound);
+ self.add_type_test(type_test);
+ }
+}
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
new file mode 100644
index 000000000..cc0318ede
--- /dev/null
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -0,0 +1,374 @@
+use rustc_data_structures::frozen::Frozen;
+use rustc_data_structures::transitive_relation::TransitiveRelation;
+use rustc_infer::infer::canonical::QueryRegionConstraints;
+use rustc_infer::infer::outlives;
+use rustc_infer::infer::outlives::env::RegionBoundPairs;
+use rustc_infer::infer::region_constraints::GenericKind;
+use rustc_infer::infer::InferCtxt;
+use rustc_middle::mir::ConstraintCategory;
+use rustc_middle::traits::query::OutlivesBound;
+use rustc_middle::ty::{self, RegionVid, Ty};
+use rustc_span::DUMMY_SP;
+use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
+use std::rc::Rc;
+use type_op::TypeOpOutput;
+
+use crate::{
+ type_check::constraint_conversion,
+ type_check::{Locations, MirTypeckRegionConstraints},
+ universal_regions::UniversalRegions,
+};
+
+#[derive(Debug)]
+pub(crate) struct UniversalRegionRelations<'tcx> {
+ universal_regions: Rc<UniversalRegions<'tcx>>,
+
+ /// Stores the outlives relations that are known to hold from the
+ /// implied bounds, in-scope where-clauses, and that sort of
+ /// thing.
+ outlives: TransitiveRelation<RegionVid>,
+
+ /// This is the `<=` relation; that is, if `a: b`, then `b <= a`,
+ /// and we store that here. This is useful when figuring out how
+ /// to express some local region in terms of external regions our
+ /// caller will understand.
+ inverse_outlives: TransitiveRelation<RegionVid>,
+}
+
+/// As part of computing the free region relations, we also have to
+/// normalize the input-output types, which we then need later. So we
+/// return those. This vector consists of first the input types and
+/// then the output type as the last element.
+type NormalizedInputsAndOutput<'tcx> = Vec<Ty<'tcx>>;
+
+pub(crate) struct CreateResult<'tcx> {
+ pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
+ pub(crate) region_bound_pairs: RegionBoundPairs<'tcx>,
+ pub(crate) normalized_inputs_and_output: NormalizedInputsAndOutput<'tcx>,
+}
+
+pub(crate) fn create<'tcx>(
+ infcx: &InferCtxt<'_, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ implicit_region_bound: ty::Region<'tcx>,
+ universal_regions: &Rc<UniversalRegions<'tcx>>,
+ constraints: &mut MirTypeckRegionConstraints<'tcx>,
+) -> CreateResult<'tcx> {
+ UniversalRegionRelationsBuilder {
+ infcx,
+ param_env,
+ implicit_region_bound,
+ constraints,
+ universal_regions: universal_regions.clone(),
+ region_bound_pairs: Default::default(),
+ relations: UniversalRegionRelations {
+ universal_regions: universal_regions.clone(),
+ outlives: Default::default(),
+ inverse_outlives: Default::default(),
+ },
+ }
+ .create()
+}
+
+impl UniversalRegionRelations<'_> {
+ /// Records in the `outlives_relation` (and
+ /// `inverse_outlives_relation`) that `fr_a: fr_b`. Invoked by the
+ /// builder below.
+ fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) {
+ debug!("relate_universal_regions: fr_a={:?} outlives fr_b={:?}", fr_a, fr_b);
+ self.outlives.add(fr_a, fr_b);
+ self.inverse_outlives.add(fr_b, fr_a);
+ }
+
+ /// Given two universal regions, returns the postdominating
+ /// upper-bound (effectively the least upper bound).
+ ///
+ /// (See `TransitiveRelation::postdom_upper_bound` for details on
+ /// the postdominating upper bound in general.)
+ pub(crate) fn postdom_upper_bound(&self, fr1: RegionVid, fr2: RegionVid) -> RegionVid {
+ assert!(self.universal_regions.is_universal_region(fr1));
+ assert!(self.universal_regions.is_universal_region(fr2));
+ self.inverse_outlives
+ .postdom_upper_bound(fr1, fr2)
+ .unwrap_or(self.universal_regions.fr_static)
+ }
+
+ /// Finds an "upper bound" for `fr` that is not local. In other
+ /// words, returns the smallest (*) known region `fr1` that (a)
+ /// outlives `fr` and (b) is not local.
+ ///
+ /// (*) If there are multiple competing choices, we return all of them.
+ pub(crate) fn non_local_upper_bounds<'a>(&'a self, fr: RegionVid) -> Vec<RegionVid> {
+ debug!("non_local_upper_bound(fr={:?})", fr);
+ let res = self.non_local_bounds(&self.inverse_outlives, fr);
+ assert!(!res.is_empty(), "can't find an upper bound!?");
+ res
+ }
+
+ /// Returns the "postdominating" bound of the set of
+ /// `non_local_upper_bounds` for the given region.
+ pub(crate) fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid {
+ let upper_bounds = self.non_local_upper_bounds(fr);
+
+ // In case we find more than one, reduce to one for
+ // convenience. This is to prevent us from generating more
+ // complex constraints, but it will cause spurious errors.
+ let post_dom = self.inverse_outlives.mutual_immediate_postdominator(upper_bounds);
+
+ debug!("non_local_bound: post_dom={:?}", post_dom);
+
+ post_dom
+ .and_then(|post_dom| {
+ // If the mutual immediate postdom is not local, then
+ // there is no non-local result we can return.
+ if !self.universal_regions.is_local_free_region(post_dom) {
+ Some(post_dom)
+ } else {
+ None
+ }
+ })
+ .unwrap_or(self.universal_regions.fr_static)
+ }
+
+ /// Finds a "lower bound" for `fr` that is not local. In other
+ /// words, returns the largest (*) known region `fr1` that (a) is
+ /// outlived by `fr` and (b) is not local.
+ ///
+ /// (*) If there are multiple competing choices, we pick the "postdominating"
+ /// one. See `TransitiveRelation::postdom_upper_bound` for details.
+ pub(crate) fn non_local_lower_bound(&self, fr: RegionVid) -> Option<RegionVid> {
+ debug!("non_local_lower_bound(fr={:?})", fr);
+ let lower_bounds = self.non_local_bounds(&self.outlives, fr);
+
+ // In case we find more than one, reduce to one for
+ // convenience. This is to prevent us from generating more
+ // complex constraints, but it will cause spurious errors.
+ let post_dom = self.outlives.mutual_immediate_postdominator(lower_bounds);
+
+ debug!("non_local_bound: post_dom={:?}", post_dom);
+
+ post_dom.and_then(|post_dom| {
+ // If the mutual immediate postdom is not local, then
+ // there is no non-local result we can return.
+ if !self.universal_regions.is_local_free_region(post_dom) {
+ Some(post_dom)
+ } else {
+ None
+ }
+ })
+ }
+
+ /// Helper for `non_local_upper_bounds` and `non_local_lower_bounds`.
+ /// Repeatedly invokes `postdom_parent` until we find something that is not
+ /// local. Returns `None` if we never do so.
+ fn non_local_bounds<'a>(
+ &self,
+ relation: &'a TransitiveRelation<RegionVid>,
+ fr0: RegionVid,
+ ) -> Vec<RegionVid> {
+ // This method assumes that `fr0` is one of the universally
+ // quantified region variables.
+ assert!(self.universal_regions.is_universal_region(fr0));
+
+ let mut external_parents = vec![];
+ let mut queue = vec![fr0];
+
+ // Keep expanding `fr` into its parents until we reach
+ // non-local regions.
+ while let Some(fr) = queue.pop() {
+ if !self.universal_regions.is_local_free_region(fr) {
+ external_parents.push(fr);
+ continue;
+ }
+
+ queue.extend(relation.parents(fr));
+ }
+
+ debug!("non_local_bound: external_parents={:?}", external_parents);
+
+ external_parents
+ }
+
+ /// Returns `true` if fr1 is known to outlive fr2.
+ ///
+ /// This will only ever be true for universally quantified regions.
+ pub(crate) fn outlives(&self, fr1: RegionVid, fr2: RegionVid) -> bool {
+ self.outlives.contains(fr1, fr2)
+ }
+
+ /// Returns a vector of free regions `x` such that `fr1: x` is
+ /// known to hold.
+ pub(crate) fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<RegionVid> {
+ self.outlives.reachable_from(fr1)
+ }
+
+ /// Returns the _non-transitive_ set of known `outlives` constraints between free regions.
+ pub(crate) fn known_outlives(&self) -> impl Iterator<Item = (RegionVid, RegionVid)> + '_ {
+ self.outlives.base_edges()
+ }
+}
+
+struct UniversalRegionRelationsBuilder<'this, 'tcx> {
+ infcx: &'this InferCtxt<'this, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ universal_regions: Rc<UniversalRegions<'tcx>>,
+ implicit_region_bound: ty::Region<'tcx>,
+ constraints: &'this mut MirTypeckRegionConstraints<'tcx>,
+
+ // outputs:
+ relations: UniversalRegionRelations<'tcx>,
+ region_bound_pairs: RegionBoundPairs<'tcx>,
+}
+
+impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
+ pub(crate) fn create(mut self) -> CreateResult<'tcx> {
+ let unnormalized_input_output_tys = self
+ .universal_regions
+ .unnormalized_input_tys
+ .iter()
+ .cloned()
+ .chain(Some(self.universal_regions.unnormalized_output_ty));
+
+ // For each of the input/output types:
+ // - Normalize the type. This will create some region
+ // constraints, which we buffer up because we are
+ // not ready to process them yet.
+ // - Then compute the implied bounds. This will adjust
+ // the `region_bound_pairs` and so forth.
+ // - After this is done, we'll process the constraints, once
+ // the `relations` is built.
+ let mut normalized_inputs_and_output =
+ Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1);
+ let constraint_sets: Vec<_> = unnormalized_input_output_tys
+ .flat_map(|ty| {
+ debug!("build: input_or_output={:?}", ty);
+ // We only add implied bounds for the normalized type as the unnormalized
+ // type may not actually get checked by the caller.
+ //
+ // Can otherwise be unsound, see #91068.
+ let TypeOpOutput { output: norm_ty, constraints: constraints1, .. } = self
+ .param_env
+ .and(type_op::normalize::Normalize::new(ty))
+ .fully_perform(self.infcx)
+ .unwrap_or_else(|_| {
+ self.infcx
+ .tcx
+ .sess
+ .delay_span_bug(DUMMY_SP, &format!("failed to normalize {:?}", ty));
+ TypeOpOutput {
+ output: self.infcx.tcx.ty_error(),
+ constraints: None,
+ error_info: None,
+ }
+ });
+ // Note: we need this in examples like
+ // ```
+ // trait Foo {
+ // type Bar;
+ // fn foo(&self) -> &Self::Bar;
+ // }
+ // impl Foo for () {
+ // type Bar = ();
+ // fn foo(&self) ->&() {}
+ // }
+ // ```
+ // Both &Self::Bar and &() are WF
+ let constraints_implied = self.add_implied_bounds(norm_ty);
+ normalized_inputs_and_output.push(norm_ty);
+ constraints1.into_iter().chain(constraints_implied)
+ })
+ .collect();
+
+ // Insert the facts we know from the predicates. Why? Why not.
+ let param_env = self.param_env;
+ self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env));
+
+ // Finally:
+ // - outlives is reflexive, so `'r: 'r` for every region `'r`
+ // - `'static: 'r` for every region `'r`
+ // - `'r: 'fn_body` for every (other) universally quantified
+ // region `'r`, all of which are provided by our caller
+ let fr_static = self.universal_regions.fr_static;
+ let fr_fn_body = self.universal_regions.fr_fn_body;
+ for fr in self.universal_regions.universal_regions() {
+ debug!("build: relating free region {:?} to itself and to 'static", fr);
+ self.relations.relate_universal_regions(fr, fr);
+ self.relations.relate_universal_regions(fr_static, fr);
+ self.relations.relate_universal_regions(fr, fr_fn_body);
+ }
+
+ for data in &constraint_sets {
+ constraint_conversion::ConstraintConversion::new(
+ self.infcx,
+ &self.universal_regions,
+ &self.region_bound_pairs,
+ self.implicit_region_bound,
+ self.param_env,
+ Locations::All(DUMMY_SP),
+ DUMMY_SP,
+ ConstraintCategory::Internal,
+ &mut self.constraints,
+ )
+ .convert_all(data);
+ }
+
+ CreateResult {
+ universal_region_relations: Frozen::freeze(self.relations),
+ region_bound_pairs: self.region_bound_pairs,
+ normalized_inputs_and_output,
+ }
+ }
+
+ /// Update the type of a single local, which should represent
+ /// either the return type of the MIR or one of its arguments. At
+ /// the same time, compute and add any implied bounds that come
+ /// from this local.
+ #[instrument(level = "debug", skip(self))]
+ fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option<&'tcx QueryRegionConstraints<'tcx>> {
+ let TypeOpOutput { output: bounds, constraints, .. } = self
+ .param_env
+ .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
+ .fully_perform(self.infcx)
+ .unwrap_or_else(|_| bug!("failed to compute implied bounds {:?}", ty));
+ self.add_outlives_bounds(bounds);
+ constraints
+ }
+
+ /// Registers the `OutlivesBound` items from `outlives_bounds` in
+ /// the outlives relation as well as the region-bound pairs
+ /// listing.
+ fn add_outlives_bounds<I>(&mut self, outlives_bounds: I)
+ where
+ I: IntoIterator<Item = OutlivesBound<'tcx>>,
+ {
+ for outlives_bound in outlives_bounds {
+ debug!("add_outlives_bounds(bound={:?})", outlives_bound);
+
+ match outlives_bound {
+ OutlivesBound::RegionSubRegion(r1, r2) => {
+ // `where Type:` is lowered to `where Type: 'empty` so that
+ // we check `Type` is well formed, but there's no use for
+ // this bound here.
+ if r1.is_empty() {
+ return;
+ }
+
+ // The bound says that `r1 <= r2`; we store `r2: r1`.
+ let r1 = self.universal_regions.to_region_vid(r1);
+ let r2 = self.universal_regions.to_region_vid(r2);
+ self.relations.relate_universal_regions(r2, r1);
+ }
+
+ OutlivesBound::RegionSubParam(r_a, param_b) => {
+ self.region_bound_pairs
+ .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
+ }
+
+ OutlivesBound::RegionSubProjection(r_a, projection_b) => {
+ self.region_bound_pairs
+ .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a));
+ }
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
new file mode 100644
index 000000000..4431a2e8e
--- /dev/null
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -0,0 +1,245 @@
+//! This module contains code to equate the input/output types appearing
+//! in the MIR with the expected input/output types from the function
+//! signature. This requires a bit of processing, as the expected types
+//! are supplied to us before normalization and may contain opaque
+//! `impl Trait` instances. In contrast, the input/output types found in
+//! the MIR (specifically, in the special local variables for the
+//! `RETURN_PLACE` the MIR arguments) are always fully normalized (and
+//! contain revealed `impl Trait` values).
+
+use crate::type_check::constraint_conversion::ConstraintConversion;
+use rustc_index::vec::Idx;
+use rustc_infer::infer::LateBoundRegionConversionTime;
+use rustc_middle::mir::*;
+use rustc_middle::ty::Ty;
+use rustc_span::Span;
+use rustc_span::DUMMY_SP;
+use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
+use rustc_trait_selection::traits::query::Fallible;
+use type_op::TypeOpOutput;
+
+use crate::universal_regions::UniversalRegions;
+
+use super::{Locations, TypeChecker};
+
+impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
+ #[instrument(skip(self, body, universal_regions), level = "debug")]
+ pub(super) fn equate_inputs_and_outputs(
+ &mut self,
+ body: &Body<'tcx>,
+ universal_regions: &UniversalRegions<'tcx>,
+ normalized_inputs_and_output: &[Ty<'tcx>],
+ ) {
+ let (&normalized_output_ty, normalized_input_tys) =
+ normalized_inputs_and_output.split_last().unwrap();
+
+ debug!(?normalized_output_ty);
+ debug!(?normalized_input_tys);
+
+ let mir_def_id = body.source.def_id().expect_local();
+
+ // If the user explicitly annotated the input types, extract
+ // those.
+ //
+ // e.g., `|x: FxHashMap<_, &'static u32>| ...`
+ let user_provided_sig;
+ if !self.tcx().is_closure(mir_def_id.to_def_id()) {
+ user_provided_sig = None;
+ } else {
+ let typeck_results = self.tcx().typeck(mir_def_id);
+ user_provided_sig = typeck_results.user_provided_sigs.get(&mir_def_id.to_def_id()).map(
+ |user_provided_poly_sig| {
+ // Instantiate the canonicalized variables from
+ // user-provided signature (e.g., the `_` in the code
+ // above) with fresh variables.
+ let poly_sig = self.instantiate_canonical_with_fresh_inference_vars(
+ body.span,
+ &user_provided_poly_sig,
+ );
+
+ // Replace the bound items in the fn sig with fresh
+ // variables, so that they represent the view from
+ // "inside" the closure.
+ self.infcx.replace_bound_vars_with_fresh_vars(
+ body.span,
+ LateBoundRegionConversionTime::FnCall,
+ poly_sig,
+ )
+ },
+ );
+ }
+
+ debug!(?normalized_input_tys, ?body.local_decls);
+
+ // Equate expected input tys with those in the MIR.
+ for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() {
+ if argument_index + 1 >= body.local_decls.len() {
+ self.tcx()
+ .sess
+ .delay_span_bug(body.span, "found more normalized_input_ty than local_decls");
+ break;
+ }
+
+ // In MIR, argument N is stored in local N+1.
+ let local = Local::new(argument_index + 1);
+
+ let mir_input_ty = body.local_decls[local].ty;
+
+ let mir_input_span = body.local_decls[local].source_info.span;
+ self.equate_normalized_input_or_output(
+ normalized_input_ty,
+ mir_input_ty,
+ mir_input_span,
+ );
+ }
+
+ if let Some(user_provided_sig) = user_provided_sig {
+ for (argument_index, &user_provided_input_ty) in
+ user_provided_sig.inputs().iter().enumerate()
+ {
+ // In MIR, closures begin an implicit `self`, so
+ // argument N is stored in local N+2.
+ let local = Local::new(argument_index + 2);
+ let mir_input_ty = body.local_decls[local].ty;
+ let mir_input_span = body.local_decls[local].source_info.span;
+
+ // If the user explicitly annotated the input types, enforce those.
+ let user_provided_input_ty =
+ self.normalize(user_provided_input_ty, Locations::All(mir_input_span));
+
+ self.equate_normalized_input_or_output(
+ user_provided_input_ty,
+ mir_input_ty,
+ mir_input_span,
+ );
+ }
+ }
+
+ debug!(
+ "equate_inputs_and_outputs: body.yield_ty {:?}, universal_regions.yield_ty {:?}",
+ body.yield_ty(),
+ universal_regions.yield_ty
+ );
+
+ // We will not have a universal_regions.yield_ty if we yield (by accident)
+ // outside of a generator and return an `impl Trait`, so emit a delay_span_bug
+ // because we don't want to panic in an assert here if we've already got errors.
+ if body.yield_ty().is_some() != universal_regions.yield_ty.is_some() {
+ self.tcx().sess.delay_span_bug(
+ body.span,
+ &format!(
+ "Expected body to have yield_ty ({:?}) iff we have a UR yield_ty ({:?})",
+ body.yield_ty(),
+ universal_regions.yield_ty,
+ ),
+ );
+ }
+
+ if let (Some(mir_yield_ty), Some(ur_yield_ty)) =
+ (body.yield_ty(), universal_regions.yield_ty)
+ {
+ let yield_span = body.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 opaque `impl Trait` types.
+ let mir_output_ty = body.local_decls[RETURN_PLACE].ty;
+ let output_span = body.local_decls[RETURN_PLACE].source_info.span;
+ if let Err(terr) = self.eq_types(
+ normalized_output_ty,
+ mir_output_ty,
+ Locations::All(output_span),
+ ConstraintCategory::BoringNoLocation,
+ ) {
+ span_mirbug!(
+ self,
+ Location::START,
+ "equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`",
+ normalized_output_ty,
+ mir_output_ty,
+ terr
+ );
+ };
+
+ // If the user explicitly annotated the output types, enforce those.
+ // Note that this only happens for closures.
+ if let Some(user_provided_sig) = user_provided_sig {
+ let user_provided_output_ty = user_provided_sig.output();
+ let user_provided_output_ty =
+ self.normalize(user_provided_output_ty, Locations::All(output_span));
+ if let Err(err) = self.eq_types(
+ user_provided_output_ty,
+ mir_output_ty,
+ Locations::All(output_span),
+ ConstraintCategory::BoringNoLocation,
+ ) {
+ span_mirbug!(
+ self,
+ Location::START,
+ "equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`",
+ mir_output_ty,
+ user_provided_output_ty,
+ err
+ );
+ }
+ }
+ }
+
+ #[instrument(skip(self, span), level = "debug")]
+ fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, span: Span) {
+ if let Err(_) =
+ self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation)
+ {
+ // FIXME(jackh726): This is a hack. It's somewhat like
+ // `rustc_traits::normalize_after_erasing_regions`. Ideally, we'd
+ // like to normalize *before* inserting into `local_decls`, but
+ // doing so ends up causing some other trouble.
+ let b = match self.normalize_and_add_constraints(b) {
+ Ok(n) => n,
+ Err(_) => {
+ debug!("equate_inputs_and_outputs: NoSolution");
+ b
+ }
+ };
+
+ // Note: if we have to introduce new placeholders during normalization above, then we won't have
+ // added those universes to the universe info, which we would want in `relate_tys`.
+ if let Err(terr) =
+ self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation)
+ {
+ span_mirbug!(
+ self,
+ Location::START,
+ "equate_normalized_input_or_output: `{:?}=={:?}` failed with `{:?}`",
+ a,
+ b,
+ terr
+ );
+ }
+ }
+ }
+
+ pub(crate) fn normalize_and_add_constraints(&mut self, t: Ty<'tcx>) -> Fallible<Ty<'tcx>> {
+ let TypeOpOutput { output: norm_ty, constraints, .. } =
+ self.param_env.and(type_op::normalize::Normalize::new(t)).fully_perform(self.infcx)?;
+
+ debug!("{:?} normalized to {:?}", t, norm_ty);
+
+ for data in constraints {
+ ConstraintConversion::new(
+ self.infcx,
+ &self.borrowck_context.universal_regions,
+ &self.region_bound_pairs,
+ self.implicit_region_bound,
+ self.param_env,
+ Locations::All(DUMMY_SP),
+ DUMMY_SP,
+ ConstraintCategory::Internal,
+ &mut self.borrowck_context.constraints,
+ )
+ .convert_all(&*data);
+ }
+
+ Ok(norm_ty)
+ }
+}
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs
new file mode 100644
index 000000000..fda2cee43
--- /dev/null
+++ b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs
@@ -0,0 +1,170 @@
+use rustc_data_structures::vec_linked_list as vll;
+use rustc_index::vec::IndexVec;
+use rustc_middle::mir::visit::{PlaceContext, Visitor};
+use rustc_middle::mir::{Body, Local, Location};
+
+use crate::def_use::{self, DefUse};
+use crate::region_infer::values::{PointIndex, RegionValueElements};
+
+/// A map that cross references each local with the locations where it
+/// is defined (assigned), used, or dropped. Used during liveness
+/// computation.
+///
+/// We keep track only of `Local`s we'll do the liveness analysis later,
+/// this means that our internal `IndexVec`s will only be sparsely populated.
+/// In the time-memory trade-off between keeping compact vectors with new
+/// indexes (and needing to continuously map the `Local` index to its compact
+/// counterpart) and having `IndexVec`s that we only use a fraction of, time
+/// (and code simplicity) was favored. The rationale is that we only keep
+/// a small number of `IndexVec`s throughout the entire analysis while, in
+/// contrast, we're accessing each `Local` *many* times.
+pub(crate) struct LocalUseMap {
+ /// Head of a linked list of **definitions** of each variable --
+ /// definition in this context means assignment, e.g., `x` is
+ /// defined in `x = y` but not `y`; that first def is the head of
+ /// a linked list that lets you enumerate all places the variable
+ /// is assigned.
+ first_def_at: IndexVec<Local, Option<AppearanceIndex>>,
+
+ /// Head of a linked list of **uses** of each variable -- use in
+ /// this context means that the existing value of the variable is
+ /// read or modified. e.g., `y` is used in `x = y` but not `x`.
+ /// Note that `DROP(x)` terminators are excluded from this list.
+ first_use_at: IndexVec<Local, Option<AppearanceIndex>>,
+
+ /// Head of a linked list of **drops** of each variable -- these
+ /// are a special category of uses corresponding to the drop that
+ /// we add for each local variable.
+ first_drop_at: IndexVec<Local, Option<AppearanceIndex>>,
+
+ appearances: IndexVec<AppearanceIndex, Appearance>,
+}
+
+struct Appearance {
+ point_index: PointIndex,
+ next: Option<AppearanceIndex>,
+}
+
+rustc_index::newtype_index! {
+ pub struct AppearanceIndex { .. }
+}
+
+impl vll::LinkElem for Appearance {
+ type LinkIndex = AppearanceIndex;
+
+ fn next(elem: &Self) -> Option<AppearanceIndex> {
+ elem.next
+ }
+}
+
+impl LocalUseMap {
+ pub(crate) fn build(
+ live_locals: &[Local],
+ elements: &RegionValueElements,
+ body: &Body<'_>,
+ ) -> Self {
+ let nones = IndexVec::from_elem_n(None, body.local_decls.len());
+ let mut local_use_map = LocalUseMap {
+ first_def_at: nones.clone(),
+ first_use_at: nones.clone(),
+ first_drop_at: nones,
+ appearances: IndexVec::new(),
+ };
+
+ if live_locals.is_empty() {
+ return local_use_map;
+ }
+
+ let mut locals_with_use_data: IndexVec<Local, bool> =
+ IndexVec::from_elem_n(false, body.local_decls.len());
+ live_locals.iter().for_each(|&local| locals_with_use_data[local] = true);
+
+ LocalUseMapBuild { local_use_map: &mut local_use_map, elements, locals_with_use_data }
+ .visit_body(&body);
+
+ local_use_map
+ }
+
+ pub(crate) fn defs(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ {
+ vll::iter(self.first_def_at[local], &self.appearances)
+ .map(move |aa| self.appearances[aa].point_index)
+ }
+
+ pub(crate) fn uses(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ {
+ vll::iter(self.first_use_at[local], &self.appearances)
+ .map(move |aa| self.appearances[aa].point_index)
+ }
+
+ pub(crate) fn drops(&self, local: Local) -> impl Iterator<Item = PointIndex> + '_ {
+ vll::iter(self.first_drop_at[local], &self.appearances)
+ .map(move |aa| self.appearances[aa].point_index)
+ }
+}
+
+struct LocalUseMapBuild<'me> {
+ local_use_map: &'me mut LocalUseMap,
+ elements: &'me RegionValueElements,
+
+ // Vector used in `visit_local` to signal which `Local`s do we need
+ // def/use/drop information on, constructed from `live_locals` (that
+ // contains the variables we'll do the liveness analysis for).
+ // This vector serves optimization purposes only: we could have
+ // obtained the same information from `live_locals` but we want to
+ // avoid repeatedly calling `Vec::contains()` (see `LocalUseMap` for
+ // the rationale on the time-memory trade-off we're favoring here).
+ locals_with_use_data: IndexVec<Local, bool>,
+}
+
+impl LocalUseMapBuild<'_> {
+ fn insert_def(&mut self, local: Local, location: Location) {
+ Self::insert(
+ self.elements,
+ &mut self.local_use_map.first_def_at[local],
+ &mut self.local_use_map.appearances,
+ location,
+ );
+ }
+
+ fn insert_use(&mut self, local: Local, location: Location) {
+ Self::insert(
+ self.elements,
+ &mut self.local_use_map.first_use_at[local],
+ &mut self.local_use_map.appearances,
+ location,
+ );
+ }
+
+ fn insert_drop(&mut self, local: Local, location: Location) {
+ Self::insert(
+ self.elements,
+ &mut self.local_use_map.first_drop_at[local],
+ &mut self.local_use_map.appearances,
+ location,
+ );
+ }
+
+ fn insert(
+ elements: &RegionValueElements,
+ first_appearance: &mut Option<AppearanceIndex>,
+ appearances: &mut IndexVec<AppearanceIndex, Appearance>,
+ location: Location,
+ ) {
+ let point_index = elements.point_from_location(location);
+ let appearance_index =
+ appearances.push(Appearance { point_index, next: *first_appearance });
+ *first_appearance = Some(appearance_index);
+ }
+}
+
+impl Visitor<'_> for LocalUseMapBuild<'_> {
+ fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
+ if self.locals_with_use_data[local] {
+ match def_use::categorize(context) {
+ Some(DefUse::Def) => self.insert_def(local, location),
+ Some(DefUse::Use) => self.insert_use(local, location),
+ Some(DefUse::Drop) => self.insert_drop(local, location),
+ _ => (),
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
new file mode 100644
index 000000000..d5c401ae1
--- /dev/null
+++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
@@ -0,0 +1,139 @@
+use itertools::{Either, Itertools};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_middle::mir::{Body, Local};
+use rustc_middle::ty::{RegionVid, TyCtxt};
+use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
+use rustc_mir_dataflow::move_paths::MoveData;
+use rustc_mir_dataflow::ResultsCursor;
+use std::rc::Rc;
+
+use crate::{
+ constraints::OutlivesConstraintSet,
+ facts::{AllFacts, AllFactsExt},
+ location::LocationTable,
+ nll::ToRegionVid,
+ region_infer::values::RegionValueElements,
+ universal_regions::UniversalRegions,
+};
+
+use super::TypeChecker;
+
+mod local_use_map;
+mod polonius;
+mod trace;
+
+/// Combines liveness analysis with initialization analysis to
+/// determine which variables are live at which points, both due to
+/// ordinary uses and drops. Returns a set of (ty, location) pairs
+/// that indicate which types must be live at which point in the CFG.
+/// This vector is consumed by `constraint_generation`.
+///
+/// N.B., this computation requires normalization; therefore, it must be
+/// performed before
+pub(super) fn generate<'mir, 'tcx>(
+ typeck: &mut TypeChecker<'_, 'tcx>,
+ body: &Body<'tcx>,
+ elements: &Rc<RegionValueElements>,
+ flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
+ move_data: &MoveData<'tcx>,
+ location_table: &LocationTable,
+ use_polonius: bool,
+) {
+ debug!("liveness::generate");
+
+ let free_regions = regions_that_outlive_free_regions(
+ typeck.infcx.num_region_vars(),
+ &typeck.borrowck_context.universal_regions,
+ &typeck.borrowck_context.constraints.outlives_constraints,
+ );
+ let (relevant_live_locals, boring_locals) =
+ compute_relevant_live_locals(typeck.tcx(), &free_regions, &body);
+ let facts_enabled = use_polonius || AllFacts::enabled(typeck.tcx());
+
+ let polonius_drop_used = if facts_enabled {
+ let mut drop_used = Vec::new();
+ polonius::populate_access_facts(typeck, body, location_table, move_data, &mut drop_used);
+ Some(drop_used)
+ } else {
+ None
+ };
+
+ trace::trace(
+ typeck,
+ body,
+ elements,
+ flow_inits,
+ move_data,
+ relevant_live_locals,
+ boring_locals,
+ polonius_drop_used,
+ );
+}
+
+// The purpose of `compute_relevant_live_locals` is to define the subset of `Local`
+// variables for which we need to do a liveness computation. We only need
+// to compute whether a variable `X` is live if that variable contains
+// some region `R` in its type where `R` is not known to outlive a free
+// region (i.e., where `R` may be valid for just a subset of the fn body).
+fn compute_relevant_live_locals<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ free_regions: &FxHashSet<RegionVid>,
+ body: &Body<'tcx>,
+) -> (Vec<Local>, Vec<Local>) {
+ let (boring_locals, relevant_live_locals): (Vec<_>, Vec<_>) =
+ body.local_decls.iter_enumerated().partition_map(|(local, local_decl)| {
+ if tcx.all_free_regions_meet(&local_decl.ty, |r| {
+ free_regions.contains(&r.to_region_vid())
+ }) {
+ Either::Left(local)
+ } else {
+ Either::Right(local)
+ }
+ });
+
+ debug!("{} total variables", body.local_decls.len());
+ debug!("{} variables need liveness", relevant_live_locals.len());
+ debug!("{} regions outlive free regions", free_regions.len());
+
+ (relevant_live_locals, boring_locals)
+}
+
+/// Computes all regions that are (currently) known to outlive free
+/// regions. For these regions, we do not need to compute
+/// liveness, since the outlives constraints will ensure that they
+/// are live over the whole fn body anyhow.
+fn regions_that_outlive_free_regions<'tcx>(
+ num_region_vars: usize,
+ universal_regions: &UniversalRegions<'tcx>,
+ constraint_set: &OutlivesConstraintSet<'tcx>,
+) -> FxHashSet<RegionVid> {
+ // Build a graph of the outlives constraints thus far. This is
+ // a reverse graph, so for each constraint `R1: R2` we have an
+ // edge `R2 -> R1`. Therefore, if we find all regions
+ // reachable from each free region, we will have all the
+ // regions that are forced to outlive some free region.
+ let rev_constraint_graph = constraint_set.reverse_graph(num_region_vars);
+ let fr_static = universal_regions.fr_static;
+ let rev_region_graph = rev_constraint_graph.region_graph(constraint_set, fr_static);
+
+ // Stack for the depth-first search. Start out with all the free regions.
+ let mut stack: Vec<_> = universal_regions.universal_regions().collect();
+
+ // Set of all free regions, plus anything that outlives them. Initially
+ // just contains the free regions.
+ let mut outlives_free_region: FxHashSet<_> = stack.iter().cloned().collect();
+
+ // Do the DFS -- for each thing in the stack, find all things
+ // that outlive it and add them to the set. If they are not,
+ // push them onto the stack for later.
+ while let Some(sub_region) = stack.pop() {
+ stack.extend(
+ rev_region_graph
+ .outgoing_regions(sub_region)
+ .filter(|&r| outlives_free_region.insert(r)),
+ );
+ }
+
+ // Return the final set of things we visited.
+ outlives_free_region
+}
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs
new file mode 100644
index 000000000..bc76a465e
--- /dev/null
+++ b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs
@@ -0,0 +1,140 @@
+use crate::def_use::{self, DefUse};
+use crate::location::{LocationIndex, LocationTable};
+use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
+use rustc_middle::mir::{Body, Local, Location, Place};
+use rustc_middle::ty::subst::GenericArg;
+use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex};
+
+use super::TypeChecker;
+
+type VarPointRelation = Vec<(Local, LocationIndex)>;
+type PathPointRelation = Vec<(MovePathIndex, LocationIndex)>;
+
+struct UseFactsExtractor<'me, 'tcx> {
+ var_defined_at: &'me mut VarPointRelation,
+ var_used_at: &'me mut VarPointRelation,
+ location_table: &'me LocationTable,
+ var_dropped_at: &'me mut VarPointRelation,
+ move_data: &'me MoveData<'tcx>,
+ path_accessed_at_base: &'me mut PathPointRelation,
+}
+
+// A Visitor to walk through the MIR and extract point-wise facts
+impl UseFactsExtractor<'_, '_> {
+ fn location_to_index(&self, location: Location) -> LocationIndex {
+ self.location_table.mid_index(location)
+ }
+
+ fn insert_def(&mut self, local: Local, location: Location) {
+ debug!("UseFactsExtractor::insert_def()");
+ self.var_defined_at.push((local, self.location_to_index(location)));
+ }
+
+ fn insert_use(&mut self, local: Local, location: Location) {
+ debug!("UseFactsExtractor::insert_use()");
+ self.var_used_at.push((local, self.location_to_index(location)));
+ }
+
+ fn insert_drop_use(&mut self, local: Local, location: Location) {
+ debug!("UseFactsExtractor::insert_drop_use()");
+ self.var_dropped_at.push((local, self.location_to_index(location)));
+ }
+
+ fn insert_path_access(&mut self, path: MovePathIndex, location: Location) {
+ debug!("UseFactsExtractor::insert_path_access({:?}, {:?})", path, location);
+ self.path_accessed_at_base.push((path, self.location_to_index(location)));
+ }
+
+ fn place_to_mpi(&self, place: &Place<'_>) -> Option<MovePathIndex> {
+ match self.move_data.rev_lookup.find(place.as_ref()) {
+ LookupResult::Exact(mpi) => Some(mpi),
+ LookupResult::Parent(mmpi) => mmpi,
+ }
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for UseFactsExtractor<'a, 'tcx> {
+ fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
+ match def_use::categorize(context) {
+ Some(DefUse::Def) => self.insert_def(local, location),
+ Some(DefUse::Use) => self.insert_use(local, location),
+ Some(DefUse::Drop) => self.insert_drop_use(local, location),
+ _ => (),
+ }
+ }
+
+ fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
+ self.super_place(place, context, location);
+ match context {
+ PlaceContext::NonMutatingUse(_) => {
+ if let Some(mpi) = self.place_to_mpi(place) {
+ self.insert_path_access(mpi, location);
+ }
+ }
+
+ PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
+ if let Some(mpi) = self.place_to_mpi(place) {
+ self.insert_path_access(mpi, location);
+ }
+ }
+ _ => (),
+ }
+ }
+}
+
+pub(super) fn populate_access_facts<'a, 'tcx>(
+ typeck: &mut TypeChecker<'a, 'tcx>,
+ body: &Body<'tcx>,
+ location_table: &LocationTable,
+ move_data: &MoveData<'tcx>,
+ dropped_at: &mut Vec<(Local, Location)>,
+) {
+ debug!("populate_access_facts()");
+
+ if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() {
+ let mut extractor = UseFactsExtractor {
+ var_defined_at: &mut facts.var_defined_at,
+ var_used_at: &mut facts.var_used_at,
+ var_dropped_at: &mut facts.var_dropped_at,
+ path_accessed_at_base: &mut facts.path_accessed_at_base,
+ location_table,
+ move_data,
+ };
+ extractor.visit_body(&body);
+
+ facts.var_dropped_at.extend(
+ dropped_at.iter().map(|&(local, location)| (local, location_table.mid_index(location))),
+ );
+
+ for (local, local_decl) in body.local_decls.iter_enumerated() {
+ debug!(
+ "add use_of_var_derefs_origin facts - local={:?}, type={:?}",
+ local, local_decl.ty
+ );
+ let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation");
+ let universal_regions = &typeck.borrowck_context.universal_regions;
+ typeck.infcx.tcx.for_each_free_region(&local_decl.ty, |region| {
+ let region_vid = universal_regions.to_region_vid(region);
+ facts.use_of_var_derefs_origin.push((local, region_vid));
+ });
+ }
+ }
+}
+
+// For every potentially drop()-touched region `region` in `local`'s type
+// (`kind`), emit a Polonius `use_of_var_derefs_origin(local, origin)` fact.
+pub(super) fn add_drop_of_var_derefs_origin<'tcx>(
+ typeck: &mut TypeChecker<'_, 'tcx>,
+ local: Local,
+ kind: &GenericArg<'tcx>,
+) {
+ debug!("add_drop_of_var_derefs_origin(local={:?}, kind={:?}", local, kind);
+ if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() {
+ let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation");
+ let universal_regions = &typeck.borrowck_context.universal_regions;
+ typeck.infcx.tcx.for_each_free_region(kind, |drop_live_region| {
+ let region_vid = universal_regions.to_region_vid(drop_live_region);
+ facts.drop_of_var_derefs_origin.push((local, region_vid));
+ });
+ }
+}
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
new file mode 100644
index 000000000..42b577175
--- /dev/null
+++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -0,0 +1,578 @@
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_index::bit_set::HybridBitSet;
+use rustc_index::interval::IntervalSet;
+use rustc_infer::infer::canonical::QueryRegionConstraints;
+use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
+use rustc_middle::ty::{Ty, TypeVisitable};
+use rustc_trait_selection::traits::query::dropck_outlives::DropckOutlivesResult;
+use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives;
+use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
+use std::rc::Rc;
+
+use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
+use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex};
+use rustc_mir_dataflow::ResultsCursor;
+
+use crate::{
+ region_infer::values::{self, PointIndex, RegionValueElements},
+ type_check::liveness::local_use_map::LocalUseMap,
+ type_check::liveness::polonius,
+ type_check::NormalizeLocation,
+ type_check::TypeChecker,
+};
+
+/// This is the heart of the liveness computation. For each variable X
+/// that requires a liveness computation, it walks over all the uses
+/// of X and does a reverse depth-first search ("trace") through the
+/// MIR. This search stops when we find a definition of that variable.
+/// The points visited in this search is the USE-LIVE set for the variable;
+/// of those points is added to all the regions that appear in the variable's
+/// type.
+///
+/// We then also walks through each *drop* of those variables and does
+/// another search, stopping when we reach a use or definition. This
+/// is the DROP-LIVE set of points. Each of the points in the
+/// DROP-LIVE set are to the liveness sets for regions found in the
+/// `dropck_outlives` result of the variable's type (in particular,
+/// this respects `#[may_dangle]` annotations).
+pub(super) fn trace<'mir, 'tcx>(
+ typeck: &mut TypeChecker<'_, 'tcx>,
+ body: &Body<'tcx>,
+ elements: &Rc<RegionValueElements>,
+ flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
+ move_data: &MoveData<'tcx>,
+ relevant_live_locals: Vec<Local>,
+ boring_locals: Vec<Local>,
+ polonius_drop_used: Option<Vec<(Local, Location)>>,
+) {
+ debug!("trace()");
+
+ let local_use_map = &LocalUseMap::build(&relevant_live_locals, elements, body);
+
+ let cx = LivenessContext {
+ typeck,
+ body,
+ flow_inits,
+ elements,
+ local_use_map,
+ move_data,
+ drop_data: FxHashMap::default(),
+ };
+
+ let mut results = LivenessResults::new(cx);
+
+ if let Some(drop_used) = polonius_drop_used {
+ results.add_extra_drop_facts(drop_used, relevant_live_locals.iter().copied().collect())
+ }
+
+ results.compute_for_all_locals(relevant_live_locals);
+
+ results.dropck_boring_locals(boring_locals);
+}
+
+/// Contextual state for the type-liveness generator.
+struct LivenessContext<'me, 'typeck, 'flow, 'tcx> {
+ /// Current type-checker, giving us our inference context etc.
+ typeck: &'me mut TypeChecker<'typeck, 'tcx>,
+
+ /// Defines the `PointIndex` mapping
+ elements: &'me RegionValueElements,
+
+ /// MIR we are analyzing.
+ body: &'me Body<'tcx>,
+
+ /// Mapping to/from the various indices used for initialization tracking.
+ move_data: &'me MoveData<'tcx>,
+
+ /// Cache for the results of `dropck_outlives` query.
+ drop_data: FxHashMap<Ty<'tcx>, DropData<'tcx>>,
+
+ /// Results of dataflow tracking which variables (and paths) have been
+ /// initialized.
+ flow_inits: &'me mut ResultsCursor<'flow, 'tcx, MaybeInitializedPlaces<'flow, 'tcx>>,
+
+ /// Index indicating where each variable is assigned, used, or
+ /// dropped.
+ local_use_map: &'me LocalUseMap,
+}
+
+struct DropData<'tcx> {
+ dropck_result: DropckOutlivesResult<'tcx>,
+ region_constraint_data: Option<&'tcx QueryRegionConstraints<'tcx>>,
+}
+
+struct LivenessResults<'me, 'typeck, 'flow, 'tcx> {
+ cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>,
+
+ /// Set of points that define the current local.
+ defs: HybridBitSet<PointIndex>,
+
+ /// Points where the current variable is "use live" -- meaning
+ /// that there is a future "full use" that may use its value.
+ use_live_at: IntervalSet<PointIndex>,
+
+ /// Points where the current variable is "drop live" -- meaning
+ /// that there is no future "full use" that may use its value, but
+ /// there is a future drop.
+ drop_live_at: IntervalSet<PointIndex>,
+
+ /// Locations where drops may occur.
+ drop_locations: Vec<Location>,
+
+ /// Stack used when doing (reverse) DFS.
+ stack: Vec<PointIndex>,
+}
+
+impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
+ fn new(cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>) -> Self {
+ let num_points = cx.elements.num_points();
+ LivenessResults {
+ cx,
+ defs: HybridBitSet::new_empty(num_points),
+ use_live_at: IntervalSet::new(num_points),
+ drop_live_at: IntervalSet::new(num_points),
+ drop_locations: vec![],
+ stack: vec![],
+ }
+ }
+
+ fn compute_for_all_locals(&mut self, relevant_live_locals: Vec<Local>) {
+ for local in relevant_live_locals {
+ self.reset_local_state();
+ self.add_defs_for(local);
+ self.compute_use_live_points_for(local);
+ self.compute_drop_live_points_for(local);
+
+ let local_ty = self.cx.body.local_decls[local].ty;
+
+ if !self.use_live_at.is_empty() {
+ self.cx.add_use_live_facts_for(local_ty, &self.use_live_at);
+ }
+
+ if !self.drop_live_at.is_empty() {
+ self.cx.add_drop_live_facts_for(
+ local,
+ local_ty,
+ &self.drop_locations,
+ &self.drop_live_at,
+ );
+ }
+ }
+ }
+
+ // Runs dropck for locals whose liveness isn't relevant. This is
+ // necessary to eagerly detect unbound recursion during drop glue computation.
+ fn dropck_boring_locals(&mut self, boring_locals: Vec<Local>) {
+ for local in boring_locals {
+ let local_ty = self.cx.body.local_decls[local].ty;
+ let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({
+ let typeck = &mut self.cx.typeck;
+ move || LivenessContext::compute_drop_data(typeck, local_ty)
+ });
+
+ drop_data.dropck_result.report_overflows(
+ self.cx.typeck.infcx.tcx,
+ self.cx.body.local_decls[local].source_info.span,
+ local_ty,
+ );
+ }
+ }
+
+ /// Add extra drop facts needed for Polonius.
+ ///
+ /// Add facts for all locals with free regions, since regions may outlive
+ /// the function body only at certain nodes in the CFG.
+ fn add_extra_drop_facts(
+ &mut self,
+ drop_used: Vec<(Local, Location)>,
+ relevant_live_locals: FxHashSet<Local>,
+ ) {
+ let locations = IntervalSet::new(self.cx.elements.num_points());
+
+ for (local, location) in drop_used {
+ if !relevant_live_locals.contains(&local) {
+ let local_ty = self.cx.body.local_decls[local].ty;
+ if local_ty.has_free_regions() {
+ self.cx.add_drop_live_facts_for(local, local_ty, &[location], &locations);
+ }
+ }
+ }
+ }
+
+ /// Clear the value of fields that are "per local variable".
+ fn reset_local_state(&mut self) {
+ self.defs.clear();
+ self.use_live_at.clear();
+ self.drop_live_at.clear();
+ self.drop_locations.clear();
+ assert!(self.stack.is_empty());
+ }
+
+ /// Adds the definitions of `local` into `self.defs`.
+ fn add_defs_for(&mut self, local: Local) {
+ for def in self.cx.local_use_map.defs(local) {
+ debug!("- defined at {:?}", def);
+ self.defs.insert(def);
+ }
+ }
+
+ /// Computes all points where local is "use live" -- meaning its
+ /// current value may be used later (except by a drop). This is
+ /// done by walking backwards from each use of `local` until we
+ /// find a `def` of local.
+ ///
+ /// Requires `add_defs_for(local)` to have been executed.
+ fn compute_use_live_points_for(&mut self, local: Local) {
+ debug!("compute_use_live_points_for(local={:?})", local);
+
+ self.stack.extend(self.cx.local_use_map.uses(local));
+ while let Some(p) = self.stack.pop() {
+ // We are live in this block from the closest to us of:
+ //
+ // * Inclusively, the block start
+ // * Exclusively, the previous definition (if it's in this block)
+ // * Exclusively, the previous live_at setting (an optimization)
+ let block_start = self.cx.elements.to_block_start(p);
+ let previous_defs = self.defs.last_set_in(block_start..=p);
+ let previous_live_at = self.use_live_at.last_set_in(block_start..=p);
+
+ let exclusive_start = match (previous_defs, previous_live_at) {
+ (Some(a), Some(b)) => Some(std::cmp::max(a, b)),
+ (Some(a), None) | (None, Some(a)) => Some(a),
+ (None, None) => None,
+ };
+
+ if let Some(exclusive) = exclusive_start {
+ self.use_live_at.insert_range(exclusive + 1..=p);
+
+ // If we have a bound after the start of the block, we should
+ // not add the predecessors for this block.
+ continue;
+ } else {
+ // Add all the elements of this block.
+ self.use_live_at.insert_range(block_start..=p);
+
+ // Then add the predecessors for this block, which are the
+ // terminators of predecessor basic blocks. Push those onto the
+ // stack so that the next iteration(s) will process them.
+
+ let block = self.cx.elements.to_location(block_start).block;
+ self.stack.extend(
+ self.cx.body.basic_blocks.predecessors()[block]
+ .iter()
+ .map(|&pred_bb| self.cx.body.terminator_loc(pred_bb))
+ .map(|pred_loc| self.cx.elements.point_from_location(pred_loc)),
+ );
+ }
+ }
+ }
+
+ /// Computes all points where local is "drop live" -- meaning its
+ /// current value may be dropped later (but not used). This is
+ /// done by iterating over the drops of `local` where `local` (or
+ /// some subpart of `local`) is initialized. For each such drop,
+ /// we walk backwards until we find a point where `local` is
+ /// either defined or use-live.
+ ///
+ /// Requires `compute_use_live_points_for` and `add_defs_for` to
+ /// have been executed.
+ fn compute_drop_live_points_for(&mut self, local: Local) {
+ debug!("compute_drop_live_points_for(local={:?})", local);
+
+ let mpi = self.cx.move_data.rev_lookup.find_local(local);
+ debug!("compute_drop_live_points_for: mpi = {:?}", mpi);
+
+ // Find the drops where `local` is initialized.
+ for drop_point in self.cx.local_use_map.drops(local) {
+ let location = self.cx.elements.to_location(drop_point);
+ debug_assert_eq!(self.cx.body.terminator_loc(location.block), location,);
+
+ if self.cx.initialized_at_terminator(location.block, mpi) {
+ if self.drop_live_at.insert(drop_point) {
+ self.drop_locations.push(location);
+ self.stack.push(drop_point);
+ }
+ }
+ }
+
+ debug!("compute_drop_live_points_for: drop_locations={:?}", self.drop_locations);
+
+ // Reverse DFS. But for drops, we do it a bit differently.
+ // The stack only ever stores *terminators of blocks*. Within
+ // a block, we walk back the statements in an inner loop.
+ while let Some(term_point) = self.stack.pop() {
+ self.compute_drop_live_points_for_block(mpi, term_point);
+ }
+ }
+
+ /// Executes one iteration of the drop-live analysis loop.
+ ///
+ /// The parameter `mpi` is the `MovePathIndex` of the local variable
+ /// we are currently analyzing.
+ ///
+ /// The point `term_point` represents some terminator in the MIR,
+ /// where the local `mpi` is drop-live on entry to that terminator.
+ ///
+ /// This method adds all drop-live points within the block and --
+ /// where applicable -- pushes the terminators of preceding blocks
+ /// onto `self.stack`.
+ fn compute_drop_live_points_for_block(&mut self, mpi: MovePathIndex, term_point: PointIndex) {
+ debug!(
+ "compute_drop_live_points_for_block(mpi={:?}, term_point={:?})",
+ self.cx.move_data.move_paths[mpi].place,
+ self.cx.elements.to_location(term_point),
+ );
+
+ // We are only invoked with terminators where `mpi` is
+ // drop-live on entry.
+ debug_assert!(self.drop_live_at.contains(term_point));
+
+ // Otherwise, scan backwards through the statements in the
+ // block. One of them may be either a definition or use
+ // live point.
+ let term_location = self.cx.elements.to_location(term_point);
+ debug_assert_eq!(self.cx.body.terminator_loc(term_location.block), term_location,);
+ let block = term_location.block;
+ let entry_point = self.cx.elements.entry_point(term_location.block);
+ for p in (entry_point..term_point).rev() {
+ debug!("compute_drop_live_points_for_block: p = {:?}", self.cx.elements.to_location(p));
+
+ if self.defs.contains(p) {
+ debug!("compute_drop_live_points_for_block: def site");
+ return;
+ }
+
+ if self.use_live_at.contains(p) {
+ debug!("compute_drop_live_points_for_block: use-live at {:?}", p);
+ return;
+ }
+
+ if !self.drop_live_at.insert(p) {
+ debug!("compute_drop_live_points_for_block: already drop-live");
+ return;
+ }
+ }
+
+ let body = self.cx.body;
+ for &pred_block in body.basic_blocks.predecessors()[block].iter() {
+ debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,);
+
+ // Check whether the variable is (at least partially)
+ // initialized at the exit of this predecessor. If so, we
+ // want to enqueue it on our list. If not, go check the
+ // next block.
+ //
+ // Note that we only need to check whether `live_local`
+ // became de-initialized at basic block boundaries. If it
+ // were to become de-initialized within the block, that
+ // would have been a "use-live" transition in the earlier
+ // loop, and we'd have returned already.
+ //
+ // NB. It's possible that the pred-block ends in a call
+ // which stores to the variable; in that case, the
+ // variable may be uninitialized "at exit" because this
+ // call only considers the *unconditional effects* of the
+ // terminator. *But*, in that case, the terminator is also
+ // a *definition* of the variable, in which case we want
+ // to stop the search anyhow. (But see Note 1 below.)
+ if !self.cx.initialized_at_exit(pred_block, mpi) {
+ debug!("compute_drop_live_points_for_block: not initialized");
+ continue;
+ }
+
+ let pred_term_loc = self.cx.body.terminator_loc(pred_block);
+ let pred_term_point = self.cx.elements.point_from_location(pred_term_loc);
+
+ // If the terminator of this predecessor either *assigns*
+ // our value or is a "normal use", then stop.
+ if self.defs.contains(pred_term_point) {
+ debug!("compute_drop_live_points_for_block: defined at {:?}", pred_term_loc);
+ continue;
+ }
+
+ if self.use_live_at.contains(pred_term_point) {
+ debug!("compute_drop_live_points_for_block: use-live at {:?}", pred_term_loc);
+ continue;
+ }
+
+ // Otherwise, we are drop-live on entry to the terminator,
+ // so walk it.
+ if self.drop_live_at.insert(pred_term_point) {
+ debug!("compute_drop_live_points_for_block: pushed to stack");
+ self.stack.push(pred_term_point);
+ }
+ }
+
+ // Note 1. There is a weird scenario that you might imagine
+ // being problematic here, but which actually cannot happen.
+ // The problem would be if we had a variable that *is* initialized
+ // (but dead) on entry to the terminator, and where the current value
+ // will be dropped in the case of unwind. In that case, we ought to
+ // consider `X` to be drop-live in between the last use and call.
+ // Here is the example:
+ //
+ // ```
+ // BB0 {
+ // X = ...
+ // use(X); // last use
+ // ... // <-- X ought to be drop-live here
+ // X = call() goto BB1 unwind BB2
+ // }
+ //
+ // BB1 {
+ // DROP(X)
+ // }
+ //
+ // BB2 {
+ // DROP(X)
+ // }
+ // ```
+ //
+ // However, the current code would, when walking back from BB2,
+ // simply stop and never explore BB0. This seems bad! But it turns
+ // out this code is flawed anyway -- note that the existing value of
+ // `X` would leak in the case where unwinding did *not* occur.
+ //
+ // What we *actually* generate is a store to a temporary
+ // for the call (`TMP = call()...`) and then a
+ // `DropAndReplace` to swap that with `X`
+ // (`DropAndReplace` has very particular semantics).
+ }
+}
+
+impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
+ /// Returns `true` if the local variable (or some part of it) is initialized at the current
+ /// cursor position. Callers should call one of the `seek` methods immediately before to point
+ /// the cursor to the desired location.
+ fn initialized_at_curr_loc(&self, mpi: MovePathIndex) -> bool {
+ let state = self.flow_inits.get();
+ if state.contains(mpi) {
+ return true;
+ }
+
+ let move_paths = &self.flow_inits.analysis().move_data().move_paths;
+ move_paths[mpi].find_descendant(&move_paths, |mpi| state.contains(mpi)).is_some()
+ }
+
+ /// Returns `true` if the local variable (or some part of it) is initialized in
+ /// the terminator of `block`. We need to check this to determine if a
+ /// DROP of some local variable will have an effect -- note that
+ /// drops, as they may unwind, are always terminators.
+ fn initialized_at_terminator(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool {
+ self.flow_inits.seek_before_primary_effect(self.body.terminator_loc(block));
+ self.initialized_at_curr_loc(mpi)
+ }
+
+ /// Returns `true` if the path `mpi` (or some part of it) is initialized at
+ /// the exit of `block`.
+ ///
+ /// **Warning:** Does not account for the result of `Call`
+ /// instructions.
+ fn initialized_at_exit(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool {
+ self.flow_inits.seek_after_primary_effect(self.body.terminator_loc(block));
+ self.initialized_at_curr_loc(mpi)
+ }
+
+ /// Stores the result that all regions in `value` are live for the
+ /// points `live_at`.
+ fn add_use_live_facts_for(
+ &mut self,
+ value: impl TypeVisitable<'tcx>,
+ live_at: &IntervalSet<PointIndex>,
+ ) {
+ debug!("add_use_live_facts_for(value={:?})", value);
+
+ Self::make_all_regions_live(self.elements, &mut self.typeck, value, live_at)
+ }
+
+ /// Some variable with type `live_ty` is "drop live" at `location`
+ /// -- i.e., it may be dropped later. This means that *some* of
+ /// the regions in its type must be live at `location`. The
+ /// precise set will depend on the dropck constraints, and in
+ /// particular this takes `#[may_dangle]` into account.
+ fn add_drop_live_facts_for(
+ &mut self,
+ dropped_local: Local,
+ dropped_ty: Ty<'tcx>,
+ drop_locations: &[Location],
+ live_at: &IntervalSet<PointIndex>,
+ ) {
+ debug!(
+ "add_drop_live_constraint(\
+ dropped_local={:?}, \
+ dropped_ty={:?}, \
+ drop_locations={:?}, \
+ live_at={:?})",
+ dropped_local,
+ dropped_ty,
+ drop_locations,
+ values::location_set_str(self.elements, live_at.iter()),
+ );
+
+ let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({
+ let typeck = &mut self.typeck;
+ move || Self::compute_drop_data(typeck, dropped_ty)
+ });
+
+ if let Some(data) = &drop_data.region_constraint_data {
+ for &drop_location in drop_locations {
+ self.typeck.push_region_constraints(
+ drop_location.to_locations(),
+ ConstraintCategory::Boring,
+ data,
+ );
+ }
+ }
+
+ drop_data.dropck_result.report_overflows(
+ self.typeck.infcx.tcx,
+ self.body.source_info(*drop_locations.first().unwrap()).span,
+ dropped_ty,
+ );
+
+ // All things in the `outlives` array may be touched by
+ // the destructor and must be live at this point.
+ for &kind in &drop_data.dropck_result.kinds {
+ Self::make_all_regions_live(self.elements, &mut self.typeck, kind, live_at);
+
+ polonius::add_drop_of_var_derefs_origin(&mut self.typeck, dropped_local, &kind);
+ }
+ }
+
+ fn make_all_regions_live(
+ elements: &RegionValueElements,
+ typeck: &mut TypeChecker<'_, 'tcx>,
+ value: impl TypeVisitable<'tcx>,
+ live_at: &IntervalSet<PointIndex>,
+ ) {
+ debug!("make_all_regions_live(value={:?})", value);
+ debug!(
+ "make_all_regions_live: live_at={}",
+ values::location_set_str(elements, live_at.iter()),
+ );
+
+ let tcx = typeck.tcx();
+ tcx.for_each_free_region(&value, |live_region| {
+ let live_region_vid =
+ typeck.borrowck_context.universal_regions.to_region_vid(live_region);
+ typeck
+ .borrowck_context
+ .constraints
+ .liveness_constraints
+ .add_elements(live_region_vid, live_at);
+ });
+ }
+
+ fn compute_drop_data(
+ typeck: &mut TypeChecker<'_, 'tcx>,
+ dropped_ty: Ty<'tcx>,
+ ) -> DropData<'tcx> {
+ debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,);
+
+ let param_env = typeck.param_env;
+ let TypeOpOutput { output, constraints, .. } =
+ param_env.and(DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx).unwrap();
+
+ DropData { dropck_result: output, region_constraint_data: constraints }
+ }
+}
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
new file mode 100644
index 000000000..d32b1edcd
--- /dev/null
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -0,0 +1,2721 @@
+//! This pass type-checks the MIR to ensure it is not broken.
+
+use std::rc::Rc;
+use std::{fmt, iter, mem};
+
+use either::Either;
+
+use hir::OpaqueTyOrigin;
+use rustc_data_structures::frozen::Frozen;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::vec_map::VecMap;
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::lang_items::LangItem;
+use rustc_index::vec::{Idx, IndexVec};
+use rustc_infer::infer::canonical::QueryRegionConstraints;
+use rustc_infer::infer::outlives::env::RegionBoundPairs;
+use rustc_infer::infer::region_constraints::RegionConstraintData;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::{
+ InferCtxt, InferOk, LateBoundRegion, LateBoundRegionConversionTime, NllRegionVariableOrigin,
+};
+use rustc_middle::mir::tcx::PlaceTy;
+use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
+use rustc_middle::mir::AssertKind;
+use rustc_middle::mir::*;
+use rustc_middle::ty::adjustment::PointerCast;
+use rustc_middle::ty::cast::CastTy;
+use rustc_middle::ty::subst::{GenericArgKind, SubstsRef, UserSubsts};
+use rustc_middle::ty::visit::TypeVisitable;
+use rustc_middle::ty::{
+ self, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, OpaqueHiddenType,
+ OpaqueTypeKey, RegionVid, ToPredicate, Ty, TyCtxt, UserType, UserTypeAnnotationIndex,
+};
+use rustc_span::def_id::CRATE_DEF_ID;
+use rustc_span::{Span, DUMMY_SP};
+use rustc_target::abi::VariantIdx;
+use rustc_trait_selection::traits::query::type_op;
+use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
+use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
+use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
+use rustc_trait_selection::traits::query::Fallible;
+use rustc_trait_selection::traits::PredicateObligation;
+
+use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
+use rustc_mir_dataflow::move_paths::MoveData;
+use rustc_mir_dataflow::ResultsCursor;
+
+use crate::session_diagnostics::MoveUnsized;
+use crate::{
+ borrow_set::BorrowSet,
+ constraints::{OutlivesConstraint, OutlivesConstraintSet},
+ diagnostics::UniverseInfo,
+ facts::AllFacts,
+ location::LocationTable,
+ member_constraints::MemberConstraintSet,
+ nll::ToRegionVid,
+ path_utils,
+ region_infer::values::{
+ LivenessValues, PlaceholderIndex, PlaceholderIndices, RegionValueElements,
+ },
+ region_infer::{ClosureRegionRequirementsExt, TypeTest},
+ type_check::free_region_relations::{CreateResult, UniversalRegionRelations},
+ universal_regions::{DefiningTy, UniversalRegions},
+ Upvar,
+};
+
+macro_rules! span_mirbug {
+ ($context:expr, $elem:expr, $($message:tt)*) => ({
+ $crate::type_check::mirbug(
+ $context.tcx(),
+ $context.last_span,
+ &format!(
+ "broken MIR in {:?} ({:?}): {}",
+ $context.body().source.def_id(),
+ $elem,
+ format_args!($($message)*),
+ ),
+ )
+ })
+}
+
+macro_rules! span_mirbug_and_err {
+ ($context:expr, $elem:expr, $($message:tt)*) => ({
+ {
+ span_mirbug!($context, $elem, $($message)*);
+ $context.error()
+ }
+ })
+}
+
+mod canonical;
+mod constraint_conversion;
+pub mod free_region_relations;
+mod input_output;
+pub(crate) mod liveness;
+mod relate_tys;
+
+/// Type checks the given `mir` in the context of the inference
+/// context `infcx`. Returns any region constraints that have yet to
+/// be proven. This result includes liveness constraints that
+/// ensure that regions appearing in the types of all local variables
+/// are live at all points where that local variable may later be
+/// used.
+///
+/// This phase of type-check ought to be infallible -- this is because
+/// the original, HIR-based type-check succeeded. So if any errors
+/// occur here, we will get a `bug!` reported.
+///
+/// # Parameters
+///
+/// - `infcx` -- inference context to use
+/// - `param_env` -- parameter environment to use for trait solving
+/// - `body` -- MIR body to type-check
+/// - `promoted` -- map of promoted constants within `body`
+/// - `universal_regions` -- the universal regions from `body`s function signature
+/// - `location_table` -- MIR location map of `body`
+/// - `borrow_set` -- information about borrows occurring in `body`
+/// - `all_facts` -- when using Polonius, this is the generated set of Polonius facts
+/// - `flow_inits` -- results of a maybe-init dataflow analysis
+/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis
+/// - `elements` -- MIR region map
+pub(crate) fn type_check<'mir, 'tcx>(
+ infcx: &InferCtxt<'_, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ body: &Body<'tcx>,
+ promoted: &IndexVec<Promoted, Body<'tcx>>,
+ universal_regions: &Rc<UniversalRegions<'tcx>>,
+ location_table: &LocationTable,
+ borrow_set: &BorrowSet<'tcx>,
+ all_facts: &mut Option<AllFacts>,
+ flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
+ move_data: &MoveData<'tcx>,
+ elements: &Rc<RegionValueElements>,
+ upvars: &[Upvar<'tcx>],
+ use_polonius: bool,
+) -> MirTypeckResults<'tcx> {
+ let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body));
+ let mut universe_causes = FxHashMap::default();
+ universe_causes.insert(ty::UniverseIndex::from_u32(0), UniverseInfo::other());
+ let mut constraints = MirTypeckRegionConstraints {
+ placeholder_indices: PlaceholderIndices::default(),
+ placeholder_index_to_region: IndexVec::default(),
+ liveness_constraints: LivenessValues::new(elements.clone()),
+ outlives_constraints: OutlivesConstraintSet::default(),
+ member_constraints: MemberConstraintSet::default(),
+ closure_bounds_mapping: Default::default(),
+ type_tests: Vec::default(),
+ universe_causes,
+ };
+
+ let CreateResult {
+ universal_region_relations,
+ region_bound_pairs,
+ normalized_inputs_and_output,
+ } = free_region_relations::create(
+ infcx,
+ param_env,
+ implicit_region_bound,
+ universal_regions,
+ &mut constraints,
+ );
+
+ debug!(?normalized_inputs_and_output);
+
+ for u in ty::UniverseIndex::ROOT..infcx.universe() {
+ let info = UniverseInfo::other();
+ constraints.universe_causes.insert(u, info);
+ }
+
+ let mut borrowck_context = BorrowCheckContext {
+ universal_regions,
+ location_table,
+ borrow_set,
+ all_facts,
+ constraints: &mut constraints,
+ upvars,
+ };
+
+ let opaque_type_values = type_check_internal(
+ infcx,
+ param_env,
+ body,
+ promoted,
+ &region_bound_pairs,
+ implicit_region_bound,
+ &mut borrowck_context,
+ |mut cx| {
+ debug!("inside extra closure of type_check_internal");
+ cx.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output);
+ liveness::generate(
+ &mut cx,
+ body,
+ elements,
+ flow_inits,
+ move_data,
+ location_table,
+ use_polonius,
+ );
+
+ translate_outlives_facts(&mut cx);
+ let opaque_type_values =
+ infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+
+ opaque_type_values
+ .into_iter()
+ .map(|(opaque_type_key, decl)| {
+ cx.fully_perform_op(
+ Locations::All(body.span),
+ ConstraintCategory::OpaqueType,
+ CustomTypeOp::new(
+ |infcx| {
+ infcx.register_member_constraints(
+ param_env,
+ opaque_type_key,
+ decl.hidden_type.ty,
+ decl.hidden_type.span,
+ );
+ Ok(InferOk { value: (), obligations: vec![] })
+ },
+ || "opaque_type_map".to_string(),
+ ),
+ )
+ .unwrap();
+ let mut hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type);
+ trace!(
+ "finalized opaque type {:?} to {:#?}",
+ opaque_type_key,
+ hidden_type.ty.kind()
+ );
+ if hidden_type.has_infer_types_or_consts() {
+ infcx.tcx.sess.delay_span_bug(
+ decl.hidden_type.span,
+ &format!("could not resolve {:#?}", hidden_type.ty.kind()),
+ );
+ hidden_type.ty = infcx.tcx.ty_error();
+ }
+
+ (opaque_type_key, (hidden_type, decl.origin))
+ })
+ .collect()
+ },
+ );
+
+ MirTypeckResults { constraints, universal_region_relations, opaque_type_values }
+}
+
+#[instrument(
+ skip(infcx, body, promoted, region_bound_pairs, borrowck_context, extra),
+ level = "debug"
+)]
+fn type_check_internal<'a, 'tcx, R>(
+ infcx: &'a InferCtxt<'a, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ body: &'a Body<'tcx>,
+ promoted: &'a IndexVec<Promoted, Body<'tcx>>,
+ region_bound_pairs: &'a RegionBoundPairs<'tcx>,
+ implicit_region_bound: ty::Region<'tcx>,
+ borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>,
+ extra: impl FnOnce(TypeChecker<'a, 'tcx>) -> R,
+) -> R {
+ debug!("body: {:#?}", body);
+ let mut checker = TypeChecker::new(
+ infcx,
+ body,
+ param_env,
+ region_bound_pairs,
+ implicit_region_bound,
+ borrowck_context,
+ );
+ let errors_reported = {
+ let mut verifier = TypeVerifier::new(&mut checker, promoted);
+ verifier.visit_body(&body);
+ verifier.errors_reported
+ };
+
+ if !errors_reported {
+ // if verifier failed, don't do further checks to avoid ICEs
+ checker.typeck_mir(body);
+ }
+
+ extra(checker)
+}
+
+fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) {
+ let cx = &mut typeck.borrowck_context;
+ if let Some(facts) = cx.all_facts {
+ let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation");
+ let location_table = cx.location_table;
+ facts.subset_base.extend(cx.constraints.outlives_constraints.outlives().iter().flat_map(
+ |constraint: &OutlivesConstraint<'_>| {
+ if let Some(from_location) = constraint.locations.from_location() {
+ Either::Left(iter::once((
+ constraint.sup,
+ constraint.sub,
+ location_table.mid_index(from_location),
+ )))
+ } else {
+ Either::Right(
+ location_table
+ .all_points()
+ .map(move |location| (constraint.sup, constraint.sub, location)),
+ )
+ }
+ },
+ ));
+ }
+}
+
+#[track_caller]
+fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: &str) {
+ // We sometimes see MIR failures (notably predicate failures) due to
+ // the fact that we check rvalue sized predicates here. So use `delay_span_bug`
+ // to avoid reporting bugs in those cases.
+ tcx.sess.diagnostic().delay_span_bug(span, msg);
+}
+
+enum FieldAccessError {
+ OutOfRange { field_count: usize },
+}
+
+/// Verifies that MIR types are sane to not crash further checks.
+///
+/// The sanitize_XYZ methods here take an MIR object and compute its
+/// type, calling `span_mirbug` and returning an error type if there
+/// is a problem.
+struct TypeVerifier<'a, 'b, 'tcx> {
+ cx: &'a mut TypeChecker<'b, 'tcx>,
+ promoted: &'b IndexVec<Promoted, Body<'tcx>>,
+ last_span: Span,
+ errors_reported: bool,
+}
+
+impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
+ fn visit_span(&mut self, span: Span) {
+ if !span.is_dummy() {
+ self.last_span = span;
+ }
+ }
+
+ fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
+ self.sanitize_place(place, location, context);
+ }
+
+ fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
+ self.super_constant(constant, location);
+ let ty = self.sanitize_type(constant, constant.literal.ty());
+
+ self.cx.infcx.tcx.for_each_free_region(&ty, |live_region| {
+ let live_region_vid =
+ self.cx.borrowck_context.universal_regions.to_region_vid(live_region);
+ self.cx
+ .borrowck_context
+ .constraints
+ .liveness_constraints
+ .add_element(live_region_vid, location);
+ });
+
+ // HACK(compiler-errors): Constants that are gathered into Body.required_consts
+ // have their locations erased...
+ let locations = if location != Location::START {
+ location.to_locations()
+ } else {
+ Locations::All(constant.span)
+ };
+
+ if let Some(annotation_index) = constant.user_ty {
+ if let Err(terr) = self.cx.relate_type_and_user_type(
+ constant.literal.ty(),
+ ty::Variance::Invariant,
+ &UserTypeProjection { base: annotation_index, projs: vec![] },
+ locations,
+ ConstraintCategory::Boring,
+ ) {
+ let annotation = &self.cx.user_type_annotations[annotation_index];
+ span_mirbug!(
+ self,
+ constant,
+ "bad constant user type {:?} vs {:?}: {:?}",
+ annotation,
+ constant.literal.ty(),
+ terr,
+ );
+ }
+ } else {
+ let tcx = self.tcx();
+ let maybe_uneval = match constant.literal {
+ ConstantKind::Ty(ct) => match ct.kind() {
+ ty::ConstKind::Unevaluated(uv) => Some(uv),
+ _ => None,
+ },
+ _ => None,
+ };
+ if let Some(uv) = maybe_uneval {
+ if let Some(promoted) = uv.promoted {
+ let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>,
+ promoted: &Body<'tcx>,
+ ty,
+ san_ty| {
+ if let Err(terr) =
+ verifier.cx.eq_types(ty, san_ty, locations, ConstraintCategory::Boring)
+ {
+ span_mirbug!(
+ verifier,
+ promoted,
+ "bad promoted type ({:?}: {:?}): {:?}",
+ ty,
+ san_ty,
+ terr
+ );
+ };
+ };
+
+ if !self.errors_reported {
+ let promoted_body = &self.promoted[promoted];
+ self.sanitize_promoted(promoted_body, location);
+
+ let promoted_ty = promoted_body.return_ty();
+ check_err(self, promoted_body, ty, promoted_ty);
+ }
+ } else {
+ if let Err(terr) = self.cx.fully_perform_op(
+ locations,
+ ConstraintCategory::Boring,
+ self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
+ constant.literal.ty(),
+ uv.def.did,
+ UserSubsts { substs: uv.substs, user_self_ty: None },
+ )),
+ ) {
+ span_mirbug!(
+ self,
+ constant,
+ "bad constant type {:?} ({:?})",
+ constant,
+ terr
+ );
+ }
+ }
+ } else if let Some(static_def_id) = constant.check_static_ptr(tcx) {
+ let unnormalized_ty = tcx.type_of(static_def_id);
+ let normalized_ty = self.cx.normalize(unnormalized_ty, locations);
+ let literal_ty = constant.literal.ty().builtin_deref(true).unwrap().ty;
+
+ if let Err(terr) = self.cx.eq_types(
+ literal_ty,
+ normalized_ty,
+ locations,
+ ConstraintCategory::Boring,
+ ) {
+ span_mirbug!(self, constant, "bad static type {:?} ({:?})", constant, terr);
+ }
+ }
+
+ if let ty::FnDef(def_id, substs) = *constant.literal.ty().kind() {
+ let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
+ self.cx.normalize_and_prove_instantiated_predicates(
+ def_id,
+ instantiated_predicates,
+ locations,
+ );
+ }
+ }
+ }
+
+ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
+ self.super_rvalue(rvalue, location);
+ let rval_ty = rvalue.ty(self.body(), self.tcx());
+ self.sanitize_type(rvalue, rval_ty);
+ }
+
+ fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) {
+ self.super_local_decl(local, local_decl);
+ self.sanitize_type(local_decl, local_decl.ty);
+
+ if let Some(user_ty) = &local_decl.user_ty {
+ for (user_ty, span) in user_ty.projections_and_spans() {
+ let ty = if !local_decl.is_nonref_binding() {
+ // If we have a binding of the form `let ref x: T = ..`
+ // then remove the outermost reference so we can check the
+ // type annotation for the remaining type.
+ if let ty::Ref(_, rty, _) = local_decl.ty.kind() {
+ *rty
+ } else {
+ bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty);
+ }
+ } else {
+ local_decl.ty
+ };
+
+ if let Err(terr) = self.cx.relate_type_and_user_type(
+ ty,
+ ty::Variance::Invariant,
+ user_ty,
+ Locations::All(*span),
+ ConstraintCategory::TypeAnnotation,
+ ) {
+ span_mirbug!(
+ self,
+ local,
+ "bad user type on variable {:?}: {:?} != {:?} ({:?})",
+ local,
+ local_decl.ty,
+ local_decl.user_ty,
+ terr,
+ );
+ }
+ }
+ }
+ }
+
+ fn visit_body(&mut self, body: &Body<'tcx>) {
+ self.sanitize_type(&"return type", body.return_ty());
+ for local_decl in &body.local_decls {
+ self.sanitize_type(local_decl, local_decl.ty);
+ }
+ if self.errors_reported {
+ return;
+ }
+ self.super_body(body);
+ }
+}
+
+impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
+ fn new(
+ cx: &'a mut TypeChecker<'b, 'tcx>,
+ promoted: &'b IndexVec<Promoted, Body<'tcx>>,
+ ) -> Self {
+ TypeVerifier { promoted, last_span: cx.body.span, cx, errors_reported: false }
+ }
+
+ fn body(&self) -> &Body<'tcx> {
+ self.cx.body
+ }
+
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.cx.infcx.tcx
+ }
+
+ fn sanitize_type(&mut self, parent: &dyn fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> {
+ if ty.has_escaping_bound_vars() || ty.references_error() {
+ span_mirbug_and_err!(self, parent, "bad type {:?}", ty)
+ } else {
+ ty
+ }
+ }
+
+ /// Checks that the types internal to the `place` match up with
+ /// what would be expected.
+ fn sanitize_place(
+ &mut self,
+ place: &Place<'tcx>,
+ location: Location,
+ context: PlaceContext,
+ ) -> PlaceTy<'tcx> {
+ debug!("sanitize_place: {:?}", place);
+
+ let mut place_ty = PlaceTy::from_ty(self.body().local_decls[place.local].ty);
+
+ for elem in place.projection.iter() {
+ if place_ty.variant_index.is_none() {
+ if place_ty.ty.references_error() {
+ assert!(self.errors_reported);
+ return PlaceTy::from_ty(self.tcx().ty_error());
+ }
+ }
+ place_ty = self.sanitize_projection(place_ty, elem, place, location);
+ }
+
+ if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context {
+ let tcx = self.tcx();
+ let trait_ref = ty::TraitRef {
+ def_id: tcx.require_lang_item(LangItem::Copy, Some(self.last_span)),
+ substs: tcx.mk_substs_trait(place_ty.ty, &[]),
+ };
+
+ // To have a `Copy` operand, the type `T` of the
+ // value must be `Copy`. Note that we prove that `T: Copy`,
+ // rather than using the `is_copy_modulo_regions`
+ // test. This is important because
+ // `is_copy_modulo_regions` ignores the resulting region
+ // obligations and assumes they pass. This can result in
+ // bounds from `Copy` impls being unsoundly ignored (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.to_locations(),
+ ConstraintCategory::CopyBound,
+ );
+ }
+
+ place_ty
+ }
+
+ fn sanitize_promoted(&mut self, promoted_body: &'b Body<'tcx>, location: Location) {
+ // Determine the constraints from the promoted MIR by running the type
+ // checker on the promoted MIR, then transfer the constraints back to
+ // the main MIR, changing the locations to the provided location.
+
+ let parent_body = mem::replace(&mut self.cx.body, promoted_body);
+
+ // Use new sets of constraints and closure bounds so that we can
+ // modify their locations.
+ let all_facts = &mut None;
+ let mut constraints = Default::default();
+ let mut closure_bounds = Default::default();
+ let mut liveness_constraints =
+ LivenessValues::new(Rc::new(RegionValueElements::new(&promoted_body)));
+ // Don't try to add borrow_region facts for the promoted MIR
+
+ let mut swap_constraints = |this: &mut Self| {
+ mem::swap(this.cx.borrowck_context.all_facts, all_facts);
+ mem::swap(
+ &mut this.cx.borrowck_context.constraints.outlives_constraints,
+ &mut constraints,
+ );
+ mem::swap(
+ &mut this.cx.borrowck_context.constraints.closure_bounds_mapping,
+ &mut closure_bounds,
+ );
+ mem::swap(
+ &mut this.cx.borrowck_context.constraints.liveness_constraints,
+ &mut liveness_constraints,
+ );
+ };
+
+ swap_constraints(self);
+
+ self.visit_body(&promoted_body);
+
+ if !self.errors_reported {
+ // if verifier failed, don't do further checks to avoid ICEs
+ self.cx.typeck_mir(promoted_body);
+ }
+
+ self.cx.body = parent_body;
+ // Merge the outlives constraints back in, at the given location.
+ swap_constraints(self);
+
+ let locations = location.to_locations();
+ for constraint in constraints.outlives().iter() {
+ let mut constraint = constraint.clone();
+ constraint.locations = locations;
+ if let ConstraintCategory::Return(_)
+ | ConstraintCategory::UseAsConst
+ | ConstraintCategory::UseAsStatic = constraint.category
+ {
+ // "Returning" from a promoted is an assignment to a
+ // temporary from the user's point of view.
+ constraint.category = ConstraintCategory::Boring;
+ }
+ self.cx.borrowck_context.constraints.outlives_constraints.push(constraint)
+ }
+ for region in liveness_constraints.rows() {
+ // If the region is live at at least one location in the promoted MIR,
+ // then add a liveness constraint to the main MIR for this region
+ // at the location provided as an argument to this method
+ if liveness_constraints.get_elements(region).next().is_some() {
+ self.cx
+ .borrowck_context
+ .constraints
+ .liveness_constraints
+ .add_element(region, location);
+ }
+ }
+
+ if !closure_bounds.is_empty() {
+ let combined_bounds_mapping =
+ closure_bounds.into_iter().flat_map(|(_, value)| value).collect();
+ let existing = self
+ .cx
+ .borrowck_context
+ .constraints
+ .closure_bounds_mapping
+ .insert(location, combined_bounds_mapping);
+ assert!(existing.is_none(), "Multiple promoteds/closures at the same location.");
+ }
+ }
+
+ fn sanitize_projection(
+ &mut self,
+ base: PlaceTy<'tcx>,
+ pi: PlaceElem<'tcx>,
+ place: &Place<'tcx>,
+ location: Location,
+ ) -> PlaceTy<'tcx> {
+ debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place);
+ let tcx = self.tcx();
+ let base_ty = base.ty;
+ match pi {
+ ProjectionElem::Deref => {
+ let deref_ty = base_ty.builtin_deref(true);
+ PlaceTy::from_ty(deref_ty.map(|t| t.ty).unwrap_or_else(|| {
+ span_mirbug_and_err!(self, place, "deref of non-pointer {:?}", base_ty)
+ }))
+ }
+ ProjectionElem::Index(i) => {
+ let index_ty = Place::from(i).ty(self.body(), tcx).ty;
+ if index_ty != tcx.types.usize {
+ PlaceTy::from_ty(span_mirbug_and_err!(self, i, "index by non-usize {:?}", i))
+ } else {
+ PlaceTy::from_ty(base_ty.builtin_index().unwrap_or_else(|| {
+ span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty)
+ }))
+ }
+ }
+ ProjectionElem::ConstantIndex { .. } => {
+ // consider verifying in-bounds
+ PlaceTy::from_ty(base_ty.builtin_index().unwrap_or_else(|| {
+ span_mirbug_and_err!(self, place, "index of non-array {:?}", base_ty)
+ }))
+ }
+ ProjectionElem::Subslice { from, to, from_end } => {
+ PlaceTy::from_ty(match base_ty.kind() {
+ ty::Array(inner, _) => {
+ assert!(!from_end, "array subslices should not use from_end");
+ tcx.mk_array(*inner, to - from)
+ }
+ ty::Slice(..) => {
+ assert!(from_end, "slice subslices should use from_end");
+ base_ty
+ }
+ _ => span_mirbug_and_err!(self, place, "slice of non-array {:?}", base_ty),
+ })
+ }
+ ProjectionElem::Downcast(maybe_name, index) => match base_ty.kind() {
+ ty::Adt(adt_def, _substs) if adt_def.is_enum() => {
+ if index.as_usize() >= adt_def.variants().len() {
+ PlaceTy::from_ty(span_mirbug_and_err!(
+ self,
+ place,
+ "cast to variant #{:?} but enum only has {:?}",
+ index,
+ adt_def.variants().len()
+ ))
+ } else {
+ PlaceTy { ty: base_ty, variant_index: Some(index) }
+ }
+ }
+ // We do not need to handle generators here, because this runs
+ // before the generator transform stage.
+ _ => {
+ let ty = if let Some(name) = maybe_name {
+ span_mirbug_and_err!(
+ self,
+ place,
+ "can't downcast {:?} as {:?}",
+ base_ty,
+ name
+ )
+ } else {
+ span_mirbug_and_err!(self, place, "can't downcast {:?}", base_ty)
+ };
+ PlaceTy::from_ty(ty)
+ }
+ },
+ ProjectionElem::Field(field, fty) => {
+ let fty = self.sanitize_type(place, fty);
+ let fty = self.cx.normalize(fty, location);
+ match self.field_ty(place, base, field, location) {
+ Ok(ty) => {
+ let ty = self.cx.normalize(ty, location);
+ if let Err(terr) = self.cx.eq_types(
+ ty,
+ fty,
+ location.to_locations(),
+ ConstraintCategory::Boring,
+ ) {
+ span_mirbug!(
+ self,
+ place,
+ "bad field access ({:?}: {:?}): {:?}",
+ ty,
+ fty,
+ terr
+ );
+ }
+ }
+ Err(FieldAccessError::OutOfRange { field_count }) => span_mirbug!(
+ self,
+ place,
+ "accessed field #{} but variant only has {}",
+ field.index(),
+ field_count
+ ),
+ }
+ PlaceTy::from_ty(fty)
+ }
+ }
+ }
+
+ fn error(&mut self) -> Ty<'tcx> {
+ self.errors_reported = true;
+ self.tcx().ty_error()
+ }
+
+ fn field_ty(
+ &mut self,
+ parent: &dyn fmt::Debug,
+ base_ty: PlaceTy<'tcx>,
+ field: Field,
+ location: Location,
+ ) -> Result<Ty<'tcx>, FieldAccessError> {
+ let tcx = self.tcx();
+
+ let (variant, substs) = match base_ty {
+ PlaceTy { ty, variant_index: Some(variant_index) } => match *ty.kind() {
+ ty::Adt(adt_def, substs) => (adt_def.variant(variant_index), substs),
+ ty::Generator(def_id, substs, _) => {
+ let mut variants = substs.as_generator().state_tys(def_id, tcx);
+ let Some(mut variant) = variants.nth(variant_index.into()) else {
+ bug!(
+ "variant_index of generator out of range: {:?}/{:?}",
+ variant_index,
+ substs.as_generator().state_tys(def_id, tcx).count()
+ );
+ };
+ return match variant.nth(field.index()) {
+ Some(ty) => Ok(ty),
+ None => Err(FieldAccessError::OutOfRange { field_count: variant.count() }),
+ };
+ }
+ _ => bug!("can't have downcast of non-adt non-generator type"),
+ },
+ PlaceTy { ty, variant_index: None } => match *ty.kind() {
+ ty::Adt(adt_def, substs) if !adt_def.is_enum() => {
+ (adt_def.variant(VariantIdx::new(0)), substs)
+ }
+ ty::Closure(_, substs) => {
+ return match substs
+ .as_closure()
+ .tupled_upvars_ty()
+ .tuple_fields()
+ .get(field.index())
+ {
+ Some(&ty) => Ok(ty),
+ None => Err(FieldAccessError::OutOfRange {
+ field_count: substs.as_closure().upvar_tys().count(),
+ }),
+ };
+ }
+ ty::Generator(_, substs, _) => {
+ // Only prefix fields (upvars and current state) are
+ // accessible without a variant index.
+ return match substs.as_generator().prefix_tys().nth(field.index()) {
+ Some(ty) => Ok(ty),
+ None => Err(FieldAccessError::OutOfRange {
+ field_count: substs.as_generator().prefix_tys().count(),
+ }),
+ };
+ }
+ ty::Tuple(tys) => {
+ return match tys.get(field.index()) {
+ Some(&ty) => Ok(ty),
+ None => Err(FieldAccessError::OutOfRange { field_count: tys.len() }),
+ };
+ }
+ _ => {
+ return Ok(span_mirbug_and_err!(
+ self,
+ parent,
+ "can't project out of {:?}",
+ base_ty
+ ));
+ }
+ },
+ };
+
+ if let Some(field) = variant.fields.get(field.index()) {
+ Ok(self.cx.normalize(field.ty(tcx, substs), location))
+ } else {
+ Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
+ }
+ }
+}
+
+/// The MIR type checker. Visits the MIR and enforces all the
+/// constraints needed for it to be valid and well-typed. Along the
+/// way, it accrues region constraints -- these can later be used by
+/// NLL region checking.
+struct TypeChecker<'a, 'tcx> {
+ infcx: &'a InferCtxt<'a, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ last_span: Span,
+ body: &'a Body<'tcx>,
+ /// User type annotations are shared between the main MIR and the MIR of
+ /// all of the promoted items.
+ user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>,
+ region_bound_pairs: &'a RegionBoundPairs<'tcx>,
+ implicit_region_bound: ty::Region<'tcx>,
+ reported_errors: FxHashSet<(Ty<'tcx>, Span)>,
+ borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>,
+}
+
+struct BorrowCheckContext<'a, 'tcx> {
+ pub(crate) universal_regions: &'a UniversalRegions<'tcx>,
+ location_table: &'a LocationTable,
+ all_facts: &'a mut Option<AllFacts>,
+ borrow_set: &'a BorrowSet<'tcx>,
+ pub(crate) constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
+ upvars: &'a [Upvar<'tcx>],
+}
+
+pub(crate) struct MirTypeckResults<'tcx> {
+ pub(crate) constraints: MirTypeckRegionConstraints<'tcx>,
+ pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
+ pub(crate) opaque_type_values:
+ VecMap<OpaqueTypeKey<'tcx>, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)>,
+}
+
+/// A collection of region constraints that must be satisfied for the
+/// program to be considered well-typed.
+pub(crate) struct MirTypeckRegionConstraints<'tcx> {
+ /// Maps from a `ty::Placeholder` to the corresponding
+ /// `PlaceholderIndex` bit that we will use for it.
+ ///
+ /// To keep everything in sync, do not insert this set
+ /// directly. Instead, use the `placeholder_region` helper.
+ pub(crate) placeholder_indices: PlaceholderIndices,
+
+ /// Each time we add a placeholder to `placeholder_indices`, we
+ /// also create a corresponding "representative" region vid for
+ /// that wraps it. This vector tracks those. This way, when we
+ /// convert the same `ty::RePlaceholder(p)` twice, we can map to
+ /// the same underlying `RegionVid`.
+ pub(crate) placeholder_index_to_region: IndexVec<PlaceholderIndex, ty::Region<'tcx>>,
+
+ /// In general, the type-checker is not responsible for enforcing
+ /// liveness constraints; this job falls to the region inferencer,
+ /// which performs a liveness analysis. However, in some limited
+ /// cases, the MIR type-checker creates temporary regions that do
+ /// not otherwise appear in the MIR -- in particular, the
+ /// late-bound regions that it instantiates at call-sites -- and
+ /// hence it must report on their liveness constraints.
+ pub(crate) liveness_constraints: LivenessValues<RegionVid>,
+
+ pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>,
+
+ pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>,
+
+ pub(crate) closure_bounds_mapping:
+ FxHashMap<Location, FxHashMap<(RegionVid, RegionVid), (ConstraintCategory<'tcx>, Span)>>,
+
+ pub(crate) universe_causes: FxHashMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
+
+ pub(crate) type_tests: Vec<TypeTest<'tcx>>,
+}
+
+impl<'tcx> MirTypeckRegionConstraints<'tcx> {
+ fn placeholder_region(
+ &mut self,
+ infcx: &InferCtxt<'_, 'tcx>,
+ placeholder: ty::PlaceholderRegion,
+ ) -> ty::Region<'tcx> {
+ let placeholder_index = self.placeholder_indices.insert(placeholder);
+ match self.placeholder_index_to_region.get(placeholder_index) {
+ Some(&v) => v,
+ None => {
+ let origin = NllRegionVariableOrigin::Placeholder(placeholder);
+ let region = infcx.next_nll_region_var_in_universe(origin, placeholder.universe);
+ self.placeholder_index_to_region.push(region);
+ region
+ }
+ }
+ }
+}
+
+/// The `Locations` type summarizes *where* region constraints are
+/// required to hold. Normally, this is at a particular point which
+/// created the obligation, but for constraints that the user gave, we
+/// want the constraint to hold at all points.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+pub enum Locations {
+ /// Indicates that a type constraint should always be true. This
+ /// is particularly important in the new borrowck analysis for
+ /// things like the type of the return slot. Consider this
+ /// example:
+ ///
+ /// ```compile_fail,E0515
+ /// fn foo<'a>(x: &'a u32) -> &'a u32 {
+ /// let y = 22;
+ /// return &y; // error
+ /// }
+ /// ```
+ ///
+ /// Here, we wind up with the signature from the return type being
+ /// something like `&'1 u32` where `'1` is a universal region. But
+ /// the type of the return slot `_0` is something like `&'2 u32`
+ /// where `'2` is an existential region variable. The type checker
+ /// requires that `&'2 u32 = &'1 u32` -- but at what point? In the
+ /// older NLL analysis, we required this only at the entry point
+ /// to the function. By the nature of the constraints, this wound
+ /// up propagating to all points reachable from start (because
+ /// `'1` -- as a universal region -- is live everywhere). In the
+ /// newer analysis, though, this doesn't work: `_0` is considered
+ /// dead at the start (it has no usable value) and hence this type
+ /// equality is basically a no-op. Then, later on, when we do `_0
+ /// = &'3 y`, that region `'3` never winds up related to the
+ /// universal region `'1` and hence no error occurs. Therefore, we
+ /// use Locations::All instead, which ensures that the `'1` and
+ /// `'2` are equal everything. We also use this for other
+ /// 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.
+ ///
+ /// 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 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::Single(from_location) => Some(*from_location),
+ }
+ }
+
+ /// Gets a span representing the location.
+ pub fn span(&self, body: &Body<'_>) -> Span {
+ match self {
+ Locations::All(span) => *span,
+ Locations::Single(l) => body.source_info(*l).span,
+ }
+ }
+}
+
+impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
+ fn new(
+ infcx: &'a InferCtxt<'a, 'tcx>,
+ body: &'a Body<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ region_bound_pairs: &'a RegionBoundPairs<'tcx>,
+ implicit_region_bound: ty::Region<'tcx>,
+ borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>,
+ ) -> Self {
+ let mut checker = Self {
+ infcx,
+ last_span: DUMMY_SP,
+ body,
+ user_type_annotations: &body.user_type_annotations,
+ param_env,
+ region_bound_pairs,
+ implicit_region_bound,
+ borrowck_context,
+ reported_errors: Default::default(),
+ };
+ checker.check_user_type_annotations();
+ checker
+ }
+
+ fn body(&self) -> &Body<'tcx> {
+ self.body
+ }
+
+ fn unsized_feature_enabled(&self) -> bool {
+ let features = self.tcx().features();
+ features.unsized_locals || features.unsized_fn_params
+ }
+
+ /// Equate the inferred type and the annotated type for user type annotations
+ #[instrument(skip(self), level = "debug")]
+ fn check_user_type_annotations(&mut self) {
+ debug!(?self.user_type_annotations);
+ for user_annotation in self.user_type_annotations {
+ let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
+ let inferred_ty = self.normalize(inferred_ty, Locations::All(span));
+ let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty);
+ match annotation {
+ UserType::Ty(mut ty) => {
+ ty = self.normalize(ty, Locations::All(span));
+
+ if let Err(terr) = self.eq_types(
+ ty,
+ inferred_ty,
+ Locations::All(span),
+ ConstraintCategory::BoringNoLocation,
+ ) {
+ span_mirbug!(
+ self,
+ user_annotation,
+ "bad user type ({:?} = {:?}): {:?}",
+ ty,
+ inferred_ty,
+ terr
+ );
+ }
+
+ self.prove_predicate(
+ ty::Binder::dummy(ty::PredicateKind::WellFormed(inferred_ty.into()))
+ .to_predicate(self.tcx()),
+ Locations::All(span),
+ ConstraintCategory::TypeAnnotation,
+ );
+ }
+ UserType::TypeOf(def_id, user_substs) => {
+ if let Err(terr) = self.fully_perform_op(
+ Locations::All(span),
+ ConstraintCategory::BoringNoLocation,
+ self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
+ inferred_ty,
+ def_id,
+ user_substs,
+ )),
+ ) {
+ span_mirbug!(
+ self,
+ user_annotation,
+ "bad user type AscribeUserType({:?}, {:?} {:?}, type_of={:?}): {:?}",
+ inferred_ty,
+ def_id,
+ user_substs,
+ self.tcx().type_of(def_id),
+ terr,
+ );
+ }
+ }
+ }
+ }
+ }
+
+ #[instrument(skip(self, data), level = "debug")]
+ fn push_region_constraints(
+ &mut self,
+ locations: Locations,
+ category: ConstraintCategory<'tcx>,
+ data: &QueryRegionConstraints<'tcx>,
+ ) {
+ debug!("constraints generated: {:#?}", data);
+
+ constraint_conversion::ConstraintConversion::new(
+ self.infcx,
+ self.borrowck_context.universal_regions,
+ self.region_bound_pairs,
+ self.implicit_region_bound,
+ self.param_env,
+ locations,
+ locations.span(self.body),
+ category,
+ &mut self.borrowck_context.constraints,
+ )
+ .convert_all(data);
+ }
+
+ /// Try to relate `sub <: sup`
+ fn sub_types(
+ &mut self,
+ sub: Ty<'tcx>,
+ sup: Ty<'tcx>,
+ locations: Locations,
+ category: ConstraintCategory<'tcx>,
+ ) -> Fallible<()> {
+ // Use this order of parameters because the sup type is usually the
+ // "expected" type in diagnostics.
+ self.relate_types(sup, ty::Variance::Contravariant, sub, locations, category)
+ }
+
+ #[instrument(skip(self, category), level = "debug")]
+ fn eq_types(
+ &mut self,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ locations: Locations,
+ category: ConstraintCategory<'tcx>,
+ ) -> Fallible<()> {
+ self.relate_types(expected, ty::Variance::Invariant, found, locations, category)
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ fn relate_type_and_user_type(
+ &mut self,
+ a: Ty<'tcx>,
+ v: ty::Variance,
+ user_ty: &UserTypeProjection,
+ locations: Locations,
+ category: ConstraintCategory<'tcx>,
+ ) -> Fallible<()> {
+ let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty;
+ let mut curr_projected_ty = PlaceTy::from_ty(annotated_type);
+
+ let tcx = self.infcx.tcx;
+
+ for proj in &user_ty.projs {
+ let projected_ty = curr_projected_ty.projection_ty_core(
+ tcx,
+ self.param_env,
+ proj,
+ |this, field, ()| {
+ let ty = this.field_ty(tcx, field);
+ self.normalize(ty, locations)
+ },
+ );
+ curr_projected_ty = projected_ty;
+ }
+ debug!(
+ "user_ty base: {:?} freshened: {:?} projs: {:?} yields: {:?}",
+ user_ty.base, annotated_type, user_ty.projs, curr_projected_ty
+ );
+
+ let ty = curr_projected_ty.ty;
+ self.relate_types(ty, v.xform(ty::Variance::Contravariant), a, locations, category)?;
+
+ Ok(())
+ }
+
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.infcx.tcx
+ }
+
+ #[instrument(skip(self, body, location), level = "debug")]
+ fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) {
+ let tcx = self.tcx();
+ debug!("stmt kind: {:?}", stmt.kind);
+ match stmt.kind {
+ StatementKind::Assign(box (ref place, ref rv)) => {
+ // Assignments to temporaries are not "interesting";
+ // they are not caused by the user, but rather artifacts
+ // of lowering. Assignments to other sorts of places *are* interesting
+ // though.
+ let category = match place.as_local() {
+ Some(RETURN_PLACE) => {
+ let defining_ty = &self.borrowck_context.universal_regions.defining_ty;
+ if defining_ty.is_const() {
+ if tcx.is_static(defining_ty.def_id()) {
+ ConstraintCategory::UseAsStatic
+ } else {
+ ConstraintCategory::UseAsConst
+ }
+ } else {
+ ConstraintCategory::Return(ReturnConstraint::Normal)
+ }
+ }
+ Some(l)
+ if matches!(
+ body.local_decls[l].local_info,
+ Some(box LocalInfo::AggregateTemp)
+ ) =>
+ {
+ ConstraintCategory::Usage
+ }
+ Some(l) if !body.local_decls[l].is_user_variable() => {
+ ConstraintCategory::Boring
+ }
+ _ => ConstraintCategory::Assignment,
+ };
+ debug!(
+ "assignment category: {:?} {:?}",
+ category,
+ place.as_local().map(|l| &body.local_decls[l])
+ );
+
+ let place_ty = place.ty(body, tcx).ty;
+ debug!(?place_ty);
+ let place_ty = self.normalize(place_ty, location);
+ debug!("place_ty normalized: {:?}", place_ty);
+ let rv_ty = rv.ty(body, tcx);
+ debug!(?rv_ty);
+ let rv_ty = self.normalize(rv_ty, location);
+ debug!("normalized rv_ty: {:?}", rv_ty);
+ if let Err(terr) =
+ self.sub_types(rv_ty, place_ty, location.to_locations(), category)
+ {
+ span_mirbug!(
+ self,
+ stmt,
+ "bad assignment ({:?} = {:?}): {:?}",
+ place_ty,
+ rv_ty,
+ terr
+ );
+ }
+
+ if let Some(annotation_index) = self.rvalue_user_ty(rv) {
+ if let Err(terr) = self.relate_type_and_user_type(
+ rv_ty,
+ ty::Variance::Invariant,
+ &UserTypeProjection { base: annotation_index, projs: vec![] },
+ location.to_locations(),
+ ConstraintCategory::Boring,
+ ) {
+ let annotation = &self.user_type_annotations[annotation_index];
+ span_mirbug!(
+ self,
+ stmt,
+ "bad user type on rvalue ({:?} = {:?}): {:?}",
+ annotation,
+ rv_ty,
+ terr
+ );
+ }
+ }
+
+ self.check_rvalue(body, rv, location);
+ if !self.unsized_feature_enabled() {
+ let trait_ref = ty::TraitRef {
+ def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)),
+ substs: tcx.mk_substs_trait(place_ty, &[]),
+ };
+ self.prove_trait_ref(
+ trait_ref,
+ location.to_locations(),
+ ConstraintCategory::SizedBound,
+ );
+ }
+ }
+ StatementKind::AscribeUserType(box (ref place, ref projection), variance) => {
+ let place_ty = place.ty(body, tcx).ty;
+ if let Err(terr) = self.relate_type_and_user_type(
+ place_ty,
+ variance,
+ projection,
+ Locations::All(stmt.source_info.span),
+ ConstraintCategory::TypeAnnotation,
+ ) {
+ let annotation = &self.user_type_annotations[projection.base];
+ span_mirbug!(
+ self,
+ stmt,
+ "bad type assert ({:?} <: {:?} with projections {:?}): {:?}",
+ place_ty,
+ annotation,
+ projection.projs,
+ terr
+ );
+ }
+ }
+ StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping {
+ ..
+ }) => span_bug!(
+ stmt.source_info.span,
+ "Unexpected StatementKind::CopyNonOverlapping, should only appear after lowering_intrinsics",
+ ),
+ StatementKind::FakeRead(..)
+ | StatementKind::StorageLive(..)
+ | StatementKind::StorageDead(..)
+ | StatementKind::Retag { .. }
+ | StatementKind::Coverage(..)
+ | StatementKind::Nop => {}
+ StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => {
+ bug!("Statement not allowed in this MIR phase")
+ }
+ }
+ }
+
+ #[instrument(skip(self, body, term_location), level = "debug")]
+ fn check_terminator(
+ &mut self,
+ body: &Body<'tcx>,
+ term: &Terminator<'tcx>,
+ term_location: Location,
+ ) {
+ let tcx = self.tcx();
+ debug!("terminator kind: {:?}", term.kind);
+ match term.kind {
+ TerminatorKind::Goto { .. }
+ | TerminatorKind::Resume
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::Unreachable
+ | TerminatorKind::Drop { .. }
+ | TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. }
+ | TerminatorKind::InlineAsm { .. } => {
+ // no checks needed for these
+ }
+
+ TerminatorKind::DropAndReplace { ref place, ref value, target: _, unwind: _ } => {
+ let place_ty = place.ty(body, tcx).ty;
+ let rv_ty = value.ty(body, tcx);
+
+ let locations = term_location.to_locations();
+ if let Err(terr) =
+ self.sub_types(rv_ty, place_ty, locations, ConstraintCategory::Assignment)
+ {
+ span_mirbug!(
+ self,
+ term,
+ "bad DropAndReplace ({:?} = {:?}): {:?}",
+ place_ty,
+ rv_ty,
+ terr
+ );
+ }
+ }
+ TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => {
+ self.check_operand(discr, term_location);
+
+ let discr_ty = discr.ty(body, tcx);
+ if let Err(terr) = self.sub_types(
+ discr_ty,
+ switch_ty,
+ term_location.to_locations(),
+ ConstraintCategory::Assignment,
+ ) {
+ span_mirbug!(
+ self,
+ term,
+ "bad SwitchInt ({:?} on {:?}): {:?}",
+ switch_ty,
+ discr_ty,
+ terr
+ );
+ }
+ if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() {
+ span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty);
+ }
+ // FIXME: check the values
+ }
+ TerminatorKind::Call {
+ ref func,
+ ref args,
+ ref destination,
+ from_hir_call,
+ target,
+ ..
+ } => {
+ self.check_operand(func, term_location);
+ for arg in args {
+ self.check_operand(arg, term_location);
+ }
+
+ let func_ty = func.ty(body, tcx);
+ debug!("func_ty.kind: {:?}", func_ty.kind());
+
+ let sig = match func_ty.kind() {
+ ty::FnDef(..) | ty::FnPtr(_) => func_ty.fn_sig(tcx),
+ _ => {
+ span_mirbug!(self, term, "call to non-function {:?}", func_ty);
+ return;
+ }
+ };
+ let (sig, map) = tcx.replace_late_bound_regions(sig, |br| {
+ self.infcx.next_region_var(LateBoundRegion(
+ term.source_info.span,
+ br.kind,
+ LateBoundRegionConversionTime::FnCall,
+ ))
+ });
+ debug!(?sig);
+ let sig = self.normalize(sig, term_location);
+ self.check_call_dest(body, term, &sig, *destination, target, term_location);
+
+ self.prove_predicates(
+ sig.inputs_and_output
+ .iter()
+ .map(|ty| ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into()))),
+ term_location.to_locations(),
+ ConstraintCategory::Boring,
+ );
+
+ // The ordinary liveness rules will ensure that all
+ // regions in the type of the callee are live here. We
+ // then further constrain the late-bound regions that
+ // were instantiated at the call site to be live as
+ // well. The resulting is that all the input (and
+ // output) types in the signature must be live, since
+ // all the inputs that fed into it were live.
+ for &late_bound_region in map.values() {
+ let region_vid =
+ self.borrowck_context.universal_regions.to_region_vid(late_bound_region);
+ self.borrowck_context
+ .constraints
+ .liveness_constraints
+ .add_element(region_vid, term_location);
+ }
+
+ self.check_call_inputs(body, term, &sig, args, term_location, from_hir_call);
+ }
+ TerminatorKind::Assert { ref cond, ref msg, .. } => {
+ self.check_operand(cond, term_location);
+
+ let cond_ty = cond.ty(body, tcx);
+ if cond_ty != tcx.types.bool {
+ span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
+ }
+
+ if let AssertKind::BoundsCheck { ref len, ref index } = *msg {
+ if len.ty(body, tcx) != tcx.types.usize {
+ span_mirbug!(self, len, "bounds-check length non-usize {:?}", len)
+ }
+ if index.ty(body, tcx) != tcx.types.usize {
+ span_mirbug!(self, index, "bounds-check index non-usize {:?}", index)
+ }
+ }
+ }
+ TerminatorKind::Yield { ref value, .. } => {
+ self.check_operand(value, term_location);
+
+ let value_ty = value.ty(body, tcx);
+ match body.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.to_locations(),
+ ConstraintCategory::Yield,
+ ) {
+ span_mirbug!(
+ self,
+ term,
+ "type of yield value is {:?}, but the yield type is {:?}: {:?}",
+ value_ty,
+ ty,
+ terr
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fn check_call_dest(
+ &mut self,
+ body: &Body<'tcx>,
+ term: &Terminator<'tcx>,
+ sig: &ty::FnSig<'tcx>,
+ destination: Place<'tcx>,
+ target: Option<BasicBlock>,
+ term_location: Location,
+ ) {
+ let tcx = self.tcx();
+ match target {
+ Some(_) => {
+ let dest_ty = destination.ty(body, tcx).ty;
+ let dest_ty = self.normalize(dest_ty, term_location);
+ let category = match destination.as_local() {
+ Some(RETURN_PLACE) => {
+ if let BorrowCheckContext {
+ universal_regions:
+ UniversalRegions {
+ defining_ty:
+ DefiningTy::Const(def_id, _)
+ | DefiningTy::InlineConst(def_id, _),
+ ..
+ },
+ ..
+ } = self.borrowck_context
+ {
+ if tcx.is_static(*def_id) {
+ ConstraintCategory::UseAsStatic
+ } else {
+ ConstraintCategory::UseAsConst
+ }
+ } else {
+ ConstraintCategory::Return(ReturnConstraint::Normal)
+ }
+ }
+ Some(l) if !body.local_decls[l].is_user_variable() => {
+ ConstraintCategory::Boring
+ }
+ _ => ConstraintCategory::Assignment,
+ };
+
+ let locations = term_location.to_locations();
+
+ if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) {
+ span_mirbug!(
+ self,
+ term,
+ "call dest mismatch ({:?} <- {:?}): {:?}",
+ dest_ty,
+ sig.output(),
+ terr
+ );
+ }
+
+ // When `unsized_fn_params` and `unsized_locals` are both not enabled,
+ // this check is done at `check_local`.
+ if self.unsized_feature_enabled() {
+ let span = term.source_info.span;
+ self.ensure_place_sized(dest_ty, span);
+ }
+ }
+ None => {
+ if !self
+ .tcx()
+ .conservative_is_privately_uninhabited(self.param_env.and(sig.output()))
+ {
+ span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
+ }
+ }
+ }
+ }
+
+ fn check_call_inputs(
+ &mut self,
+ body: &Body<'tcx>,
+ term: &Terminator<'tcx>,
+ sig: &ty::FnSig<'tcx>,
+ args: &[Operand<'tcx>],
+ term_location: Location,
+ from_hir_call: bool,
+ ) {
+ debug!("check_call_inputs({:?}, {:?})", sig, args);
+ if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.c_variadic) {
+ span_mirbug!(self, term, "call to {:?} with wrong # of args", sig);
+ }
+
+ let func_ty = if let TerminatorKind::Call { func, .. } = &term.kind {
+ Some(func.ty(body, self.infcx.tcx))
+ } else {
+ None
+ };
+ debug!(?func_ty);
+
+ for (n, (fn_arg, op_arg)) in iter::zip(sig.inputs(), args).enumerate() {
+ let op_arg_ty = op_arg.ty(body, self.tcx());
+
+ let op_arg_ty = self.normalize(op_arg_ty, term_location);
+ let category = if from_hir_call {
+ ConstraintCategory::CallArgument(func_ty)
+ } else {
+ ConstraintCategory::Boring
+ };
+ if let Err(terr) =
+ self.sub_types(op_arg_ty, *fn_arg, term_location.to_locations(), category)
+ {
+ span_mirbug!(
+ self,
+ term,
+ "bad arg #{:?} ({:?} <- {:?}): {:?}",
+ n,
+ fn_arg,
+ op_arg_ty,
+ terr
+ );
+ }
+ }
+ }
+
+ fn check_iscleanup(&mut self, body: &Body<'tcx>, block_data: &BasicBlockData<'tcx>) {
+ let is_cleanup = block_data.is_cleanup;
+ self.last_span = block_data.terminator().source_info.span;
+ match block_data.terminator().kind {
+ TerminatorKind::Goto { target } => {
+ self.assert_iscleanup(body, block_data, target, is_cleanup)
+ }
+ TerminatorKind::SwitchInt { ref targets, .. } => {
+ for target in targets.all_targets() {
+ self.assert_iscleanup(body, block_data, *target, is_cleanup);
+ }
+ }
+ TerminatorKind::Resume => {
+ if !is_cleanup {
+ span_mirbug!(self, block_data, "resume on non-cleanup block!")
+ }
+ }
+ TerminatorKind::Abort => {
+ if !is_cleanup {
+ span_mirbug!(self, block_data, "abort on non-cleanup block!")
+ }
+ }
+ TerminatorKind::Return => {
+ if is_cleanup {
+ span_mirbug!(self, block_data, "return on cleanup block")
+ }
+ }
+ TerminatorKind::GeneratorDrop { .. } => {
+ if is_cleanup {
+ span_mirbug!(self, block_data, "generator_drop in cleanup block")
+ }
+ }
+ TerminatorKind::Yield { resume, drop, .. } => {
+ if is_cleanup {
+ span_mirbug!(self, block_data, "yield in cleanup block")
+ }
+ self.assert_iscleanup(body, block_data, resume, is_cleanup);
+ if let Some(drop) = drop {
+ self.assert_iscleanup(body, block_data, drop, is_cleanup);
+ }
+ }
+ TerminatorKind::Unreachable => {}
+ TerminatorKind::Drop { target, unwind, .. }
+ | TerminatorKind::DropAndReplace { target, unwind, .. }
+ | TerminatorKind::Assert { target, cleanup: unwind, .. } => {
+ self.assert_iscleanup(body, block_data, target, is_cleanup);
+ if let Some(unwind) = unwind {
+ if is_cleanup {
+ span_mirbug!(self, block_data, "unwind on cleanup block")
+ }
+ self.assert_iscleanup(body, block_data, unwind, true);
+ }
+ }
+ TerminatorKind::Call { ref target, cleanup, .. } => {
+ if let &Some(target) = target {
+ self.assert_iscleanup(body, block_data, target, is_cleanup);
+ }
+ if let Some(cleanup) = cleanup {
+ if is_cleanup {
+ span_mirbug!(self, block_data, "cleanup on cleanup block")
+ }
+ self.assert_iscleanup(body, block_data, cleanup, true);
+ }
+ }
+ TerminatorKind::FalseEdge { real_target, imaginary_target } => {
+ self.assert_iscleanup(body, block_data, real_target, is_cleanup);
+ self.assert_iscleanup(body, block_data, imaginary_target, is_cleanup);
+ }
+ TerminatorKind::FalseUnwind { real_target, unwind } => {
+ self.assert_iscleanup(body, block_data, real_target, is_cleanup);
+ if let Some(unwind) = unwind {
+ if is_cleanup {
+ span_mirbug!(self, block_data, "cleanup in cleanup block via false unwind");
+ }
+ self.assert_iscleanup(body, block_data, unwind, true);
+ }
+ }
+ TerminatorKind::InlineAsm { destination, cleanup, .. } => {
+ if let Some(target) = destination {
+ self.assert_iscleanup(body, block_data, target, is_cleanup);
+ }
+ if let Some(cleanup) = cleanup {
+ if is_cleanup {
+ span_mirbug!(self, block_data, "cleanup on cleanup block")
+ }
+ self.assert_iscleanup(body, block_data, cleanup, true);
+ }
+ }
+ }
+ }
+
+ fn assert_iscleanup(
+ &mut self,
+ body: &Body<'tcx>,
+ ctxt: &dyn fmt::Debug,
+ bb: BasicBlock,
+ iscleanuppad: bool,
+ ) {
+ if body[bb].is_cleanup != iscleanuppad {
+ span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}", bb, iscleanuppad);
+ }
+ }
+
+ fn check_local(&mut self, body: &Body<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) {
+ match body.local_kind(local) {
+ LocalKind::ReturnPointer | LocalKind::Arg => {
+ // return values of normal functions are required to be
+ // sized by typeck, but return values of ADT constructors are
+ // not because we don't include a `Self: Sized` bounds on them.
+ //
+ // Unbound parts of arguments were never required to be Sized
+ // - maybe we should make that a warning.
+ return;
+ }
+ LocalKind::Var | LocalKind::Temp => {}
+ }
+
+ // When `unsized_fn_params` or `unsized_locals` is enabled, only function calls
+ // and nullary ops are checked in `check_call_dest`.
+ if !self.unsized_feature_enabled() {
+ let span = local_decl.source_info.span;
+ let ty = local_decl.ty;
+ self.ensure_place_sized(ty, span);
+ }
+ }
+
+ fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) {
+ let tcx = self.tcx();
+
+ // Erase the regions from `ty` to get a global type. The
+ // `Sized` bound in no way depends on precise regions, so this
+ // shouldn't affect `is_sized`.
+ let erased_ty = tcx.erase_regions(ty);
+ if !erased_ty.is_sized(tcx.at(span), self.param_env) {
+ // in current MIR construction, all non-control-flow rvalue
+ // expressions evaluate through `as_temp` or `into` a return
+ // slot or local, so to find all unsized rvalues it is enough
+ // to check all temps, return slots and locals.
+ if self.reported_errors.replace((ty, span)).is_none() {
+ // While this is located in `nll::typeck` this error is not
+ // an NLL error, it's a required check to prevent creation
+ // of unsized rvalues in a call expression.
+ self.tcx().sess.emit_err(MoveUnsized { ty, span });
+ }
+ }
+ }
+
+ fn aggregate_field_ty(
+ &mut self,
+ ak: &AggregateKind<'tcx>,
+ field_index: usize,
+ location: Location,
+ ) -> Result<Ty<'tcx>, FieldAccessError> {
+ let tcx = self.tcx();
+
+ match *ak {
+ AggregateKind::Adt(adt_did, variant_index, substs, _, active_field_index) => {
+ let def = tcx.adt_def(adt_did);
+ let variant = &def.variant(variant_index);
+ let adj_field_index = active_field_index.unwrap_or(field_index);
+ if let Some(field) = variant.fields.get(adj_field_index) {
+ Ok(self.normalize(field.ty(tcx, substs), location))
+ } else {
+ Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
+ }
+ }
+ AggregateKind::Closure(_, substs) => {
+ match substs.as_closure().upvar_tys().nth(field_index) {
+ Some(ty) => Ok(ty),
+ None => Err(FieldAccessError::OutOfRange {
+ field_count: substs.as_closure().upvar_tys().count(),
+ }),
+ }
+ }
+ AggregateKind::Generator(_, substs, _) => {
+ // It doesn't make sense to look at a field beyond the prefix;
+ // these require a variant index, and are not initialized in
+ // aggregate rvalues.
+ match substs.as_generator().prefix_tys().nth(field_index) {
+ Some(ty) => Ok(ty),
+ None => Err(FieldAccessError::OutOfRange {
+ field_count: substs.as_generator().prefix_tys().count(),
+ }),
+ }
+ }
+ AggregateKind::Array(ty) => Ok(ty),
+ AggregateKind::Tuple => {
+ unreachable!("This should have been covered in check_rvalues");
+ }
+ }
+ }
+
+ fn check_operand(&mut self, op: &Operand<'tcx>, location: Location) {
+ if let Operand::Constant(constant) = op {
+ let maybe_uneval = match constant.literal {
+ ConstantKind::Ty(ct) => match ct.kind() {
+ ty::ConstKind::Unevaluated(uv) => Some(uv),
+ _ => None,
+ },
+ _ => None,
+ };
+ if let Some(uv) = maybe_uneval {
+ if uv.promoted.is_none() {
+ let tcx = self.tcx();
+ let def_id = uv.def.def_id_for_type_of();
+ if tcx.def_kind(def_id) == DefKind::InlineConst {
+ let def_id = def_id.expect_local();
+ let predicates =
+ self.prove_closure_bounds(tcx, def_id, uv.substs, location);
+ self.normalize_and_prove_instantiated_predicates(
+ def_id.to_def_id(),
+ predicates,
+ location.to_locations(),
+ );
+ }
+ }
+ }
+ }
+ }
+
+ #[instrument(skip(self, body), level = "debug")]
+ fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
+ let tcx = self.tcx();
+
+ match rvalue {
+ Rvalue::Aggregate(ak, ops) => {
+ for op in ops {
+ self.check_operand(op, location);
+ }
+ self.check_aggregate_rvalue(&body, rvalue, ak, ops, location)
+ }
+
+ Rvalue::Repeat(operand, len) => {
+ self.check_operand(operand, location);
+
+ // If the length cannot be evaluated we must assume that the length can be larger
+ // than 1.
+ // If the length is larger than 1, the repeat expression will need to copy the
+ // element, so we require the `Copy` trait.
+ if len.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) {
+ match operand {
+ Operand::Copy(..) | Operand::Constant(..) => {
+ // These are always okay: direct use of a const, or a value that can evidently be copied.
+ }
+ Operand::Move(place) => {
+ // Make sure that repeated elements implement `Copy`.
+ let span = body.source_info(location).span;
+ let ty = place.ty(body, tcx).ty;
+ let trait_ref = ty::TraitRef::new(
+ tcx.require_lang_item(LangItem::Copy, Some(span)),
+ tcx.mk_substs_trait(ty, &[]),
+ );
+
+ self.prove_trait_ref(
+ trait_ref,
+ Locations::Single(location),
+ ConstraintCategory::CopyBound,
+ );
+ }
+ }
+ }
+ }
+
+ &Rvalue::NullaryOp(_, ty) => {
+ let trait_ref = ty::TraitRef {
+ def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)),
+ substs: tcx.mk_substs_trait(ty, &[]),
+ };
+
+ self.prove_trait_ref(
+ trait_ref,
+ location.to_locations(),
+ ConstraintCategory::SizedBound,
+ );
+ }
+
+ Rvalue::ShallowInitBox(operand, ty) => {
+ self.check_operand(operand, location);
+
+ let trait_ref = ty::TraitRef {
+ def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)),
+ substs: tcx.mk_substs_trait(*ty, &[]),
+ };
+
+ self.prove_trait_ref(
+ trait_ref,
+ location.to_locations(),
+ ConstraintCategory::SizedBound,
+ );
+ }
+
+ Rvalue::Cast(cast_kind, op, ty) => {
+ self.check_operand(op, location);
+
+ match cast_kind {
+ CastKind::Pointer(PointerCast::ReifyFnPointer) => {
+ let fn_sig = op.ty(body, 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);
+
+ let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig);
+
+ if let Err(terr) = self.eq_types(
+ *ty,
+ ty_fn_ptr_from,
+ location.to_locations(),
+ ConstraintCategory::Cast,
+ ) {
+ span_mirbug!(
+ self,
+ rvalue,
+ "equating {:?} with {:?} yields {:?}",
+ ty_fn_ptr_from,
+ ty,
+ terr
+ );
+ }
+ }
+
+ CastKind::Pointer(PointerCast::ClosureFnPointer(unsafety)) => {
+ let sig = match op.ty(body, tcx).kind() {
+ ty::Closure(_, substs) => substs.as_closure().sig(),
+ _ => bug!(),
+ };
+ let ty_fn_ptr_from = tcx.mk_fn_ptr(tcx.signature_unclosure(sig, *unsafety));
+
+ if let Err(terr) = self.eq_types(
+ *ty,
+ ty_fn_ptr_from,
+ location.to_locations(),
+ ConstraintCategory::Cast,
+ ) {
+ span_mirbug!(
+ self,
+ rvalue,
+ "equating {:?} with {:?} yields {:?}",
+ ty_fn_ptr_from,
+ ty,
+ terr
+ );
+ }
+ }
+
+ CastKind::Pointer(PointerCast::UnsafeFnPointer) => {
+ let fn_sig = op.ty(body, 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);
+
+ let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig);
+
+ if let Err(terr) = self.eq_types(
+ *ty,
+ ty_fn_ptr_from,
+ location.to_locations(),
+ ConstraintCategory::Cast,
+ ) {
+ span_mirbug!(
+ self,
+ rvalue,
+ "equating {:?} with {:?} yields {:?}",
+ ty_fn_ptr_from,
+ ty,
+ terr
+ );
+ }
+ }
+
+ CastKind::Pointer(PointerCast::Unsize) => {
+ let &ty = ty;
+ let trait_ref = ty::TraitRef {
+ def_id: tcx
+ .require_lang_item(LangItem::CoerceUnsized, Some(self.last_span)),
+ substs: tcx.mk_substs_trait(op.ty(body, tcx), &[ty.into()]),
+ };
+
+ self.prove_trait_ref(
+ trait_ref,
+ location.to_locations(),
+ ConstraintCategory::Cast,
+ );
+ }
+
+ CastKind::Pointer(PointerCast::MutToConstPointer) => {
+ let ty::RawPtr(ty::TypeAndMut {
+ ty: ty_from,
+ mutbl: hir::Mutability::Mut,
+ }) = op.ty(body, tcx).kind() else {
+ span_mirbug!(
+ self,
+ rvalue,
+ "unexpected base type for cast {:?}",
+ ty,
+ );
+ return;
+ };
+ let ty::RawPtr(ty::TypeAndMut {
+ ty: ty_to,
+ mutbl: hir::Mutability::Not,
+ }) = ty.kind() else {
+ span_mirbug!(
+ self,
+ rvalue,
+ "unexpected target type for cast {:?}",
+ ty,
+ );
+ return;
+ };
+ if let Err(terr) = self.sub_types(
+ *ty_from,
+ *ty_to,
+ location.to_locations(),
+ ConstraintCategory::Cast,
+ ) {
+ span_mirbug!(
+ self,
+ rvalue,
+ "relating {:?} with {:?} yields {:?}",
+ ty_from,
+ ty_to,
+ terr
+ );
+ }
+ }
+
+ CastKind::Pointer(PointerCast::ArrayToPointer) => {
+ let ty_from = op.ty(body, tcx);
+
+ let opt_ty_elem_mut = match ty_from.kind() {
+ ty::RawPtr(ty::TypeAndMut { mutbl: array_mut, ty: array_ty }) => {
+ match array_ty.kind() {
+ ty::Array(ty_elem, _) => Some((ty_elem, *array_mut)),
+ _ => None,
+ }
+ }
+ _ => None,
+ };
+
+ let Some((ty_elem, ty_mut)) = opt_ty_elem_mut else {
+ span_mirbug!(
+ self,
+ rvalue,
+ "ArrayToPointer cast from unexpected type {:?}",
+ ty_from,
+ );
+ return;
+ };
+
+ let (ty_to, ty_to_mut) = match ty.kind() {
+ ty::RawPtr(ty::TypeAndMut { mutbl: ty_to_mut, ty: ty_to }) => {
+ (ty_to, *ty_to_mut)
+ }
+ _ => {
+ span_mirbug!(
+ self,
+ rvalue,
+ "ArrayToPointer cast to unexpected type {:?}",
+ ty,
+ );
+ return;
+ }
+ };
+
+ if ty_to_mut == Mutability::Mut && ty_mut == Mutability::Not {
+ span_mirbug!(
+ self,
+ rvalue,
+ "ArrayToPointer cast from const {:?} to mut {:?}",
+ ty,
+ ty_to
+ );
+ return;
+ }
+
+ if let Err(terr) = self.sub_types(
+ *ty_elem,
+ *ty_to,
+ location.to_locations(),
+ ConstraintCategory::Cast,
+ ) {
+ span_mirbug!(
+ self,
+ rvalue,
+ "relating {:?} with {:?} yields {:?}",
+ ty_elem,
+ ty_to,
+ terr
+ )
+ }
+ }
+
+ CastKind::PointerExposeAddress => {
+ let ty_from = op.ty(body, tcx);
+ let cast_ty_from = CastTy::from_ty(ty_from);
+ let cast_ty_to = CastTy::from_ty(*ty);
+ match (cast_ty_from, cast_ty_to) {
+ (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => (),
+ _ => {
+ span_mirbug!(
+ self,
+ rvalue,
+ "Invalid PointerExposeAddress cast {:?} -> {:?}",
+ ty_from,
+ ty
+ )
+ }
+ }
+ }
+
+ CastKind::PointerFromExposedAddress => {
+ let ty_from = op.ty(body, tcx);
+ let cast_ty_from = CastTy::from_ty(ty_from);
+ let cast_ty_to = CastTy::from_ty(*ty);
+ match (cast_ty_from, cast_ty_to) {
+ (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => (),
+ _ => {
+ span_mirbug!(
+ self,
+ rvalue,
+ "Invalid PointerFromExposedAddress cast {:?} -> {:?}",
+ ty_from,
+ ty
+ )
+ }
+ }
+ }
+
+ CastKind::Misc => {
+ let ty_from = op.ty(body, tcx);
+ let cast_ty_from = CastTy::from_ty(ty_from);
+ let cast_ty_to = CastTy::from_ty(*ty);
+ // Misc casts are either between floats and ints, or one ptr type to another.
+ match (cast_ty_from, cast_ty_to) {
+ (
+ Some(CastTy::Int(_) | CastTy::Float),
+ Some(CastTy::Int(_) | CastTy::Float),
+ )
+ | (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Ptr(_))) => (),
+ _ => {
+ span_mirbug!(
+ self,
+ rvalue,
+ "Invalid Misc cast {:?} -> {:?}",
+ ty_from,
+ ty,
+ )
+ }
+ }
+ }
+ }
+ }
+
+ Rvalue::Ref(region, _borrow_kind, borrowed_place) => {
+ self.add_reborrow_constraint(&body, location, *region, borrowed_place);
+ }
+
+ Rvalue::BinaryOp(
+ BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge,
+ box (left, right),
+ ) => {
+ self.check_operand(left, location);
+ self.check_operand(right, location);
+
+ let ty_left = left.ty(body, tcx);
+ match ty_left.kind() {
+ // Types with regions are comparable if they have a common super-type.
+ ty::RawPtr(_) | ty::FnPtr(_) => {
+ let ty_right = right.ty(body, tcx);
+ let common_ty = self.infcx.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::MiscVariable,
+ span: body.source_info(location).span,
+ });
+ self.sub_types(
+ ty_left,
+ common_ty,
+ location.to_locations(),
+ ConstraintCategory::Boring,
+ )
+ .unwrap_or_else(|err| {
+ bug!("Could not equate type variable with {:?}: {:?}", ty_left, err)
+ });
+ if let Err(terr) = self.sub_types(
+ ty_right,
+ common_ty,
+ location.to_locations(),
+ ConstraintCategory::Boring,
+ ) {
+ span_mirbug!(
+ self,
+ rvalue,
+ "unexpected comparison types {:?} and {:?} yields {:?}",
+ ty_left,
+ ty_right,
+ terr
+ )
+ }
+ }
+ // For types with no regions we can just check that the
+ // both operands have the same type.
+ ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_)
+ if ty_left == right.ty(body, tcx) => {}
+ // Other types are compared by trait methods, not by
+ // `Rvalue::BinaryOp`.
+ _ => span_mirbug!(
+ self,
+ rvalue,
+ "unexpected comparison types {:?} and {:?}",
+ ty_left,
+ right.ty(body, tcx)
+ ),
+ }
+ }
+
+ Rvalue::Use(operand) | Rvalue::UnaryOp(_, operand) => {
+ self.check_operand(operand, location);
+ }
+ Rvalue::CopyForDeref(place) => {
+ let op = &Operand::Copy(*place);
+ self.check_operand(op, location);
+ }
+
+ Rvalue::BinaryOp(_, box (left, right))
+ | Rvalue::CheckedBinaryOp(_, box (left, right)) => {
+ self.check_operand(left, location);
+ self.check_operand(right, location);
+ }
+
+ Rvalue::AddressOf(..)
+ | Rvalue::ThreadLocalRef(..)
+ | Rvalue::Len(..)
+ | Rvalue::Discriminant(..) => {}
+ }
+ }
+
+ /// If this rvalue supports a user-given type annotation, then
+ /// extract and return it. This represents the final type of the
+ /// rvalue and will be unified with the inferred type.
+ fn rvalue_user_ty(&self, rvalue: &Rvalue<'tcx>) -> Option<UserTypeAnnotationIndex> {
+ match rvalue {
+ Rvalue::Use(_)
+ | Rvalue::ThreadLocalRef(_)
+ | Rvalue::Repeat(..)
+ | Rvalue::Ref(..)
+ | Rvalue::AddressOf(..)
+ | Rvalue::Len(..)
+ | Rvalue::Cast(..)
+ | Rvalue::ShallowInitBox(..)
+ | Rvalue::BinaryOp(..)
+ | Rvalue::CheckedBinaryOp(..)
+ | Rvalue::NullaryOp(..)
+ | Rvalue::CopyForDeref(..)
+ | Rvalue::UnaryOp(..)
+ | Rvalue::Discriminant(..) => None,
+
+ Rvalue::Aggregate(aggregate, _) => match **aggregate {
+ AggregateKind::Adt(_, _, _, user_ty, _) => user_ty,
+ AggregateKind::Array(_) => None,
+ AggregateKind::Tuple => None,
+ AggregateKind::Closure(_, _) => None,
+ AggregateKind::Generator(_, _, _) => None,
+ },
+ }
+ }
+
+ fn check_aggregate_rvalue(
+ &mut self,
+ body: &Body<'tcx>,
+ rvalue: &Rvalue<'tcx>,
+ aggregate_kind: &AggregateKind<'tcx>,
+ operands: &[Operand<'tcx>],
+ location: Location,
+ ) {
+ let tcx = self.tcx();
+
+ self.prove_aggregate_predicates(aggregate_kind, location);
+
+ if *aggregate_kind == AggregateKind::Tuple {
+ // tuple rvalue field type is always the type of the op. Nothing to check here.
+ return;
+ }
+
+ for (i, operand) in operands.iter().enumerate() {
+ let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) {
+ Ok(field_ty) => field_ty,
+ Err(FieldAccessError::OutOfRange { field_count }) => {
+ span_mirbug!(
+ self,
+ rvalue,
+ "accessed field #{} but variant only has {}",
+ i,
+ field_count
+ );
+ continue;
+ }
+ };
+ let operand_ty = operand.ty(body, tcx);
+ let operand_ty = self.normalize(operand_ty, location);
+
+ if let Err(terr) = self.sub_types(
+ operand_ty,
+ field_ty,
+ location.to_locations(),
+ ConstraintCategory::Boring,
+ ) {
+ span_mirbug!(
+ self,
+ rvalue,
+ "{:?} is not a subtype of {:?}: {:?}",
+ operand_ty,
+ field_ty,
+ terr
+ );
+ }
+ }
+ }
+
+ /// Adds the constraints that arise from a borrow expression `&'a P` at the location `L`.
+ ///
+ /// # Parameters
+ ///
+ /// - `location`: the location `L` where the borrow expression occurs
+ /// - `borrow_region`: the region `'a` associated with the borrow
+ /// - `borrowed_place`: the place `P` being borrowed
+ fn add_reborrow_constraint(
+ &mut self,
+ body: &Body<'tcx>,
+ location: Location,
+ borrow_region: ty::Region<'tcx>,
+ borrowed_place: &Place<'tcx>,
+ ) {
+ // These constraints are only meaningful during borrowck:
+ let BorrowCheckContext { borrow_set, location_table, all_facts, constraints, .. } =
+ self.borrowck_context;
+
+ // In Polonius mode, we also push a `loan_issued_at` fact
+ // linking the loan to the region (in some cases, though,
+ // there is no loan associated with this borrow expression --
+ // that occurs when we are borrowing an unsafe place, for
+ // example).
+ if let Some(all_facts) = all_facts {
+ let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation");
+ if let Some(borrow_index) = borrow_set.get_index_of(&location) {
+ let region_vid = borrow_region.to_region_vid();
+ all_facts.loan_issued_at.push((
+ region_vid,
+ borrow_index,
+ location_table.mid_index(location),
+ ));
+ }
+ }
+
+ // If we are reborrowing the referent of another reference, we
+ // need to add outlives relationships. In a case like `&mut
+ // *p`, where the `p` has type `&'b mut Foo`, for example, we
+ // need to ensure that `'b: 'a`.
+
+ debug!(
+ "add_reborrow_constraint({:?}, {:?}, {:?})",
+ location, borrow_region, borrowed_place
+ );
+
+ let mut cursor = borrowed_place.projection.as_ref();
+ let tcx = self.infcx.tcx;
+ let field = path_utils::is_upvar_field_projection(
+ tcx,
+ &self.borrowck_context.upvars,
+ borrowed_place.as_ref(),
+ body,
+ );
+ let category = if let Some(field) = field {
+ ConstraintCategory::ClosureUpvar(field)
+ } else {
+ ConstraintCategory::Boring
+ };
+
+ while let [proj_base @ .., elem] = cursor {
+ cursor = proj_base;
+
+ debug!("add_reborrow_constraint - iteration {:?}", elem);
+
+ match elem {
+ ProjectionElem::Deref => {
+ let base_ty = Place::ty_from(borrowed_place.local, proj_base, body, tcx).ty;
+
+ debug!("add_reborrow_constraint - base_ty = {:?}", base_ty);
+ match base_ty.kind() {
+ ty::Ref(ref_region, _, mutbl) => {
+ constraints.outlives_constraints.push(OutlivesConstraint {
+ sup: ref_region.to_region_vid(),
+ sub: borrow_region.to_region_vid(),
+ locations: location.to_locations(),
+ span: location.to_locations().span(body),
+ category,
+ variance_info: ty::VarianceDiagInfo::default(),
+ });
+
+ match mutbl {
+ hir::Mutability::Not => {
+ // Immutable reference. We don't need the base
+ // to be valid for the entire lifetime of
+ // the borrow.
+ break;
+ }
+ hir::Mutability::Mut => {
+ // Mutable reference. We *do* need the base
+ // to be valid, because after the base becomes
+ // invalid, someone else can use our mutable deref.
+
+ // This is in order to make the following function
+ // illegal:
+ // ```
+ // fn unsafe_deref<'a, 'b>(x: &'a &'b mut T) -> &'b mut T {
+ // &mut *x
+ // }
+ // ```
+ //
+ // As otherwise you could clone `&mut T` using the
+ // following function:
+ // ```
+ // fn bad(x: &mut T) -> (&mut T, &mut T) {
+ // let my_clone = unsafe_deref(&'a x);
+ // ENDREGION 'a;
+ // (my_clone, x)
+ // }
+ // ```
+ }
+ }
+ }
+ ty::RawPtr(..) => {
+ // deref of raw pointer, guaranteed to be valid
+ break;
+ }
+ ty::Adt(def, _) if def.is_box() => {
+ // deref of `Box`, need the base to be valid - propagate
+ }
+ _ => bug!("unexpected deref ty {:?} in {:?}", base_ty, borrowed_place),
+ }
+ }
+ ProjectionElem::Field(..)
+ | ProjectionElem::Downcast(..)
+ | ProjectionElem::Index(..)
+ | ProjectionElem::ConstantIndex { .. }
+ | ProjectionElem::Subslice { .. } => {
+ // other field access
+ }
+ }
+ }
+ }
+
+ fn prove_aggregate_predicates(
+ &mut self,
+ aggregate_kind: &AggregateKind<'tcx>,
+ location: Location,
+ ) {
+ let tcx = self.tcx();
+
+ debug!(
+ "prove_aggregate_predicates(aggregate_kind={:?}, location={:?})",
+ aggregate_kind, location
+ );
+
+ let (def_id, instantiated_predicates) = match *aggregate_kind {
+ AggregateKind::Adt(adt_did, _, substs, _, _) => {
+ (adt_did, tcx.predicates_of(adt_did).instantiate(tcx, substs))
+ }
+
+ // For closures, we have some **extra requirements** we
+ //
+ // have to check. In particular, in their upvars and
+ // signatures, closures often reference various regions
+ // from the surrounding function -- we call those the
+ // closure's free regions. When we borrow-check (and hence
+ // region-check) closures, we may find that the closure
+ // requires certain relationships between those free
+ // regions. However, because those free regions refer to
+ // portions of the CFG of their caller, the closure is not
+ // in a position to verify those relationships. In that
+ // case, the requirements get "propagated" to us, and so
+ // we have to solve them here where we instantiate the
+ // closure.
+ //
+ // Despite the opacity of the previous paragraph, this is
+ // actually relatively easy to understand in terms of the
+ // desugaring. A closure gets desugared to a struct, and
+ // these extra requirements are basically like where
+ // clauses on the struct.
+ AggregateKind::Closure(def_id, substs)
+ | AggregateKind::Generator(def_id, substs, _) => {
+ (def_id.to_def_id(), self.prove_closure_bounds(tcx, def_id, substs, location))
+ }
+
+ AggregateKind::Array(_) | AggregateKind::Tuple => {
+ (CRATE_DEF_ID.to_def_id(), ty::InstantiatedPredicates::empty())
+ }
+ };
+
+ self.normalize_and_prove_instantiated_predicates(
+ def_id,
+ instantiated_predicates,
+ location.to_locations(),
+ );
+ }
+
+ fn prove_closure_bounds(
+ &mut self,
+ tcx: TyCtxt<'tcx>,
+ def_id: LocalDefId,
+ substs: SubstsRef<'tcx>,
+ location: Location,
+ ) -> ty::InstantiatedPredicates<'tcx> {
+ if let Some(ref closure_region_requirements) = tcx.mir_borrowck(def_id).closure_requirements
+ {
+ let closure_constraints = QueryRegionConstraints {
+ outlives: closure_region_requirements.apply_requirements(
+ tcx,
+ def_id.to_def_id(),
+ substs,
+ ),
+
+ // Presently, closures never propagate member
+ // constraints to their parents -- they are enforced
+ // locally. This is largely a non-issue as member
+ // constraints only come from `-> impl Trait` and
+ // friends which don't appear (thus far...) in
+ // closures.
+ member_constraints: vec![],
+ };
+
+ let bounds_mapping = closure_constraints
+ .outlives
+ .iter()
+ .enumerate()
+ .filter_map(|(idx, constraint)| {
+ let ty::OutlivesPredicate(k1, r2) =
+ constraint.no_bound_vars().unwrap_or_else(|| {
+ bug!("query_constraint {:?} contained bound vars", constraint,);
+ });
+
+ match k1.unpack() {
+ GenericArgKind::Lifetime(r1) => {
+ // constraint is r1: r2
+ let r1_vid = self.borrowck_context.universal_regions.to_region_vid(r1);
+ let r2_vid = self.borrowck_context.universal_regions.to_region_vid(r2);
+ let outlives_requirements =
+ &closure_region_requirements.outlives_requirements[idx];
+ Some((
+ (r1_vid, r2_vid),
+ (outlives_requirements.category, outlives_requirements.blame_span),
+ ))
+ }
+ GenericArgKind::Type(_) | GenericArgKind::Const(_) => None,
+ }
+ })
+ .collect();
+
+ let existing = self
+ .borrowck_context
+ .constraints
+ .closure_bounds_mapping
+ .insert(location, bounds_mapping);
+ assert!(existing.is_none(), "Multiple closures at the same location.");
+
+ self.push_region_constraints(
+ location.to_locations(),
+ ConstraintCategory::ClosureBounds,
+ &closure_constraints,
+ );
+ }
+
+ // Now equate closure substs to regions inherited from `typeck_root_def_id`. Fixes #98589.
+ let typeck_root_def_id = tcx.typeck_root_def_id(self.body.source.def_id());
+ let typeck_root_substs = ty::InternalSubsts::identity_for_item(tcx, typeck_root_def_id);
+
+ let parent_substs = match tcx.def_kind(def_id) {
+ DefKind::Closure => substs.as_closure().parent_substs(),
+ DefKind::Generator => substs.as_generator().parent_substs(),
+ DefKind::InlineConst => substs.as_inline_const().parent_substs(),
+ other => bug!("unexpected item {:?}", other),
+ };
+ let parent_substs = tcx.mk_substs(parent_substs.iter());
+
+ assert_eq!(typeck_root_substs.len(), parent_substs.len());
+ if let Err(_) = self.eq_substs(
+ typeck_root_substs,
+ parent_substs,
+ location.to_locations(),
+ ConstraintCategory::BoringNoLocation,
+ ) {
+ span_mirbug!(
+ self,
+ def_id,
+ "could not relate closure to parent {:?} != {:?}",
+ typeck_root_substs,
+ parent_substs
+ );
+ }
+
+ tcx.predicates_of(def_id).instantiate(tcx, substs)
+ }
+
+ #[instrument(skip(self, body), level = "debug")]
+ fn typeck_mir(&mut self, body: &Body<'tcx>) {
+ self.last_span = body.span;
+ debug!(?body.span);
+
+ for (local, local_decl) in body.local_decls.iter_enumerated() {
+ self.check_local(&body, local, local_decl);
+ }
+
+ for (block, block_data) in body.basic_blocks().iter_enumerated() {
+ let mut location = Location { block, statement_index: 0 };
+ for stmt in &block_data.statements {
+ if !stmt.source_info.span.is_dummy() {
+ self.last_span = stmt.source_info.span;
+ }
+ self.check_stmt(body, stmt, location);
+ location.statement_index += 1;
+ }
+
+ self.check_terminator(&body, block_data.terminator(), location);
+ self.check_iscleanup(&body, block_data);
+ }
+ }
+}
+
+trait NormalizeLocation: fmt::Debug + Copy {
+ fn to_locations(self) -> Locations;
+}
+
+impl NormalizeLocation for Locations {
+ fn to_locations(self) -> Locations {
+ self
+ }
+}
+
+impl NormalizeLocation for Location {
+ fn to_locations(self) -> Locations {
+ Locations::Single(self)
+ }
+}
+
+/// Runs `infcx.instantiate_opaque_types`. Unlike other `TypeOp`s,
+/// this is not canonicalized - it directly affects the main `InferCtxt`
+/// that we use during MIR borrowchecking.
+#[derive(Debug)]
+pub(super) struct InstantiateOpaqueType<'tcx> {
+ pub base_universe: Option<ty::UniverseIndex>,
+ pub region_constraints: Option<RegionConstraintData<'tcx>>,
+ pub obligations: Vec<PredicateObligation<'tcx>>,
+}
+
+impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> {
+ type Output = ();
+ /// We use this type itself to store the information used
+ /// when reporting errors. Since this is not a query, we don't
+ /// re-run anything during error reporting - we just use the information
+ /// we saved to help extract an error from the already-existing region
+ /// constraints in our `InferCtxt`
+ type ErrorInfo = InstantiateOpaqueType<'tcx>;
+
+ fn fully_perform(mut self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> {
+ let (mut output, region_constraints) = scrape_region_constraints(infcx, || {
+ Ok(InferOk { value: (), obligations: self.obligations.clone() })
+ })?;
+ self.region_constraints = Some(region_constraints);
+ output.error_info = Some(self);
+ Ok(output)
+ }
+}
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
new file mode 100644
index 000000000..c97a6a1a6
--- /dev/null
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -0,0 +1,187 @@
+use rustc_infer::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate};
+use rustc_infer::infer::NllRegionVariableOrigin;
+use rustc_infer::traits::ObligationCause;
+use rustc_middle::mir::ConstraintCategory;
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::relate::TypeRelation;
+use rustc_middle::ty::{self, Const, Ty};
+use rustc_span::Span;
+use rustc_trait_selection::traits::query::Fallible;
+
+use crate::constraints::OutlivesConstraint;
+use crate::diagnostics::UniverseInfo;
+use crate::type_check::{InstantiateOpaqueType, Locations, TypeChecker};
+
+impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
+ /// Adds sufficient constraints to ensure that `a R b` where `R` depends on `v`:
+ ///
+ /// - "Covariant" `a <: b`
+ /// - "Invariant" `a == b`
+ /// - "Contravariant" `a :> b`
+ ///
+ /// N.B., the type `a` is permitted to have unresolved inference
+ /// variables, but not the type `b`.
+ #[instrument(skip(self), level = "debug")]
+ pub(super) fn relate_types(
+ &mut self,
+ a: Ty<'tcx>,
+ v: ty::Variance,
+ b: Ty<'tcx>,
+ locations: Locations,
+ category: ConstraintCategory<'tcx>,
+ ) -> Fallible<()> {
+ TypeRelating::new(
+ self.infcx,
+ NllTypeRelatingDelegate::new(self, locations, category, UniverseInfo::relate(a, b)),
+ v,
+ )
+ .relate(a, b)?;
+ Ok(())
+ }
+
+ /// Add sufficient constraints to ensure `a == b`. See also [Self::relate_types].
+ pub(super) fn eq_substs(
+ &mut self,
+ a: ty::SubstsRef<'tcx>,
+ b: ty::SubstsRef<'tcx>,
+ locations: Locations,
+ category: ConstraintCategory<'tcx>,
+ ) -> Fallible<()> {
+ TypeRelating::new(
+ self.infcx,
+ NllTypeRelatingDelegate::new(self, locations, category, UniverseInfo::other()),
+ ty::Variance::Invariant,
+ )
+ .relate(a, b)?;
+ Ok(())
+ }
+}
+
+struct NllTypeRelatingDelegate<'me, 'bccx, 'tcx> {
+ type_checker: &'me mut TypeChecker<'bccx, 'tcx>,
+
+ /// Where (and why) is this relation taking place?
+ locations: Locations,
+
+ /// What category do we assign the resulting `'a: 'b` relationships?
+ category: ConstraintCategory<'tcx>,
+
+ /// Information so that error reporting knows what types we are relating
+ /// when reporting a bound region error.
+ universe_info: UniverseInfo<'tcx>,
+}
+
+impl<'me, 'bccx, 'tcx> NllTypeRelatingDelegate<'me, 'bccx, 'tcx> {
+ fn new(
+ type_checker: &'me mut TypeChecker<'bccx, 'tcx>,
+ locations: Locations,
+ category: ConstraintCategory<'tcx>,
+ universe_info: UniverseInfo<'tcx>,
+ ) -> Self {
+ Self { type_checker, locations, category, universe_info }
+ }
+}
+
+impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> {
+ fn span(&self) -> Span {
+ self.locations.span(self.type_checker.body)
+ }
+
+ fn param_env(&self) -> ty::ParamEnv<'tcx> {
+ self.type_checker.param_env
+ }
+
+ fn create_next_universe(&mut self) -> ty::UniverseIndex {
+ let universe = self.type_checker.infcx.create_next_universe();
+ self.type_checker
+ .borrowck_context
+ .constraints
+ .universe_causes
+ .insert(universe, self.universe_info.clone());
+ universe
+ }
+
+ fn next_existential_region_var(&mut self, from_forall: bool) -> ty::Region<'tcx> {
+ let origin = NllRegionVariableOrigin::Existential { from_forall };
+ self.type_checker.infcx.next_nll_region_var(origin)
+ }
+
+ fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx> {
+ self.type_checker
+ .borrowck_context
+ .constraints
+ .placeholder_region(self.type_checker.infcx, placeholder)
+ }
+
+ fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
+ self.type_checker.infcx.next_nll_region_var_in_universe(
+ NllRegionVariableOrigin::Existential { from_forall: false },
+ universe,
+ )
+ }
+
+ fn push_outlives(
+ &mut self,
+ sup: ty::Region<'tcx>,
+ sub: ty::Region<'tcx>,
+ info: ty::VarianceDiagInfo<'tcx>,
+ ) {
+ let sub = self.type_checker.borrowck_context.universal_regions.to_region_vid(sub);
+ let sup = self.type_checker.borrowck_context.universal_regions.to_region_vid(sup);
+ self.type_checker.borrowck_context.constraints.outlives_constraints.push(
+ OutlivesConstraint {
+ sup,
+ sub,
+ locations: self.locations,
+ span: self.locations.span(self.type_checker.body),
+ category: self.category,
+ variance_info: info,
+ },
+ );
+ }
+
+ // We don't have to worry about the equality of consts during borrow checking
+ // as consts always have a static lifetime.
+ // FIXME(oli-obk): is this really true? We can at least have HKL and with
+ // inline consts we may have further lifetimes that may be unsound to treat as
+ // 'static.
+ fn const_equate(&mut self, _a: Const<'tcx>, _b: Const<'tcx>) {}
+
+ fn normalization() -> NormalizationStrategy {
+ NormalizationStrategy::Eager
+ }
+
+ fn forbid_inference_vars() -> bool {
+ true
+ }
+
+ fn register_opaque_type(
+ &mut self,
+ a: Ty<'tcx>,
+ b: Ty<'tcx>,
+ a_is_expected: bool,
+ ) -> Result<(), TypeError<'tcx>> {
+ let param_env = self.param_env();
+ let span = self.span();
+ let def_id = self.type_checker.body.source.def_id().expect_local();
+ let body_id = self.type_checker.tcx().hir().local_def_id_to_hir_id(def_id);
+ let cause = ObligationCause::misc(span, body_id);
+ self.type_checker
+ .fully_perform_op(
+ self.locations,
+ self.category,
+ InstantiateOpaqueType {
+ obligations: self
+ .type_checker
+ .infcx
+ .handle_opaque_type(a, b, a_is_expected, &cause, param_env)?
+ .obligations,
+ // These fields are filled in during execution of the operation
+ base_universe: None,
+ region_constraints: None,
+ },
+ )
+ .unwrap();
+ Ok(())
+ }
+}