Skip to content

Commit

Permalink
resolve region bounds from components of type projection
Browse files Browse the repository at this point in the history
  • Loading branch information
dingxiangfei2009 committed Feb 25, 2024
1 parent 8c0b1fc commit 3e320bc
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rustc_span::Span;
use rustc_trait_selection::solve::deeply_normalize;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
use smallvec::SmallVec;

use crate::{
constraints::OutlivesConstraint,
Expand Down Expand Up @@ -147,8 +148,14 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
region_bound_pairs,
implicit_region_bound,
known_type_outlives_obligations,
param_env,
..
} = *self;
let projection_predicates: SmallVec<[_; 4]> = param_env
.caller_bounds()
.iter()
.filter_map(|clause| clause.as_projection_clause())
.collect();

let mut outlives_predicates = vec![(predicate, constraint_category)];
for iteration in 0.. {
Expand Down Expand Up @@ -191,6 +198,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
region_bound_pairs,
Some(implicit_region_bound),
known_type_outlives_obligations,
&projection_predicates,
)
.type_must_outlive(
origin,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
data: &QueryRegionConstraints<'tcx>,
) {
debug!("constraints generated: {:#?}", data);
debug!(?self.param_env);

constraint_conversion::ConstraintConversion::new(
self.infcx,
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_borrowck/src/universal_regions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ struct UniversalRegionsBuilder<'cx, 'tcx> {
const FR: NllRegionVariableOrigin = NllRegionVariableOrigin::FreeRegion;

impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
#[instrument(level = "debug", skip(self))]
fn build(self) -> UniversalRegions<'tcx> {
debug!("build(mir_def={:?})", self.mir_def);

Expand Down Expand Up @@ -851,8 +852,8 @@ impl<'tcx> UniversalRegionIndices<'tcx> {
/// in later and instantiate the late-bound regions, and then we
/// insert the `ReLateParam` version of those into the map as
/// well. These are used for error reporting.
#[instrument(level = "debug", skip(self))]
fn insert_late_bound_region(&mut self, r: ty::Region<'tcx>, vid: ty::RegionVid) {
debug!("insert_late_bound_region({:?}, {:?})", r, vid);
self.indices.insert(r, vid);
}

Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_infer/src/infer/outlives/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,10 @@ pub(super) fn compute_alias_components_recursive<'tcx>(
let ty::Alias(kind, alias_ty) = alias_ty.kind() else {
unreachable!("can only call `compute_alias_components_recursive` on an alias type")
};
let opt_variances = if *kind == ty::Opaque { tcx.variances_of(alias_ty.def_id) } else { &[] };
let opt_variances =
if matches!(kind, ty::Opaque) { tcx.variances_of(alias_ty.def_id) } else { &[] };
for (index, child) in alias_ty.args.iter().enumerate() {
if opt_variances.get(index) == Some(&ty::Bivariant) {
if matches!(opt_variances.get(index), Some(ty::Bivariant)) {
continue;
}
if !visited.insert(child) {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/outlives/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use super::explicit_outlives_bounds;
/// refactoring here, since the setup with NLL is quite different.
/// For example, NLL has no need of `RegionRelations`, and is solely
/// interested in the `OutlivesEnvironment`. -nmatsakis
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct OutlivesEnvironment<'tcx> {
pub param_env: ty::ParamEnv<'tcx>,
free_region_map: FreeRegionMap<'tcx>,
Expand Down
20 changes: 14 additions & 6 deletions compiler/rustc_infer/src/infer/outlives/obligations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,13 @@ impl<'tcx> InferCtxt<'tcx> {
inner.region_obligations.push(obligation);
}

#[instrument(level = "debug", skip(self))]
pub fn register_region_obligation_with_cause(
&self,
sup_type: Ty<'tcx>,
sub_region: Region<'tcx>,
cause: &ObligationCause<'tcx>,
) {
debug!(?sup_type, ?sub_region, ?cause);
let origin = SubregionOrigin::from_obligation_cause(cause, || {
infer::RelateParamBound(
cause.span,
Expand Down Expand Up @@ -125,7 +125,7 @@ impl<'tcx> InferCtxt<'tcx> {
/// flow of the inferencer. The key point is that it is
/// invoked after all type-inference variables have been bound --
/// right before lexical region resolution.
#[instrument(level = "debug", skip(self, outlives_env, deeply_normalize_ty))]
#[instrument(level = "debug", skip(self, deeply_normalize_ty))]
pub fn process_registered_region_obligations(
&self,
outlives_env: &OutlivesEnvironment<'tcx>,
Expand Down Expand Up @@ -185,6 +185,7 @@ impl<'tcx> InferCtxt<'tcx> {
outlives_env.region_bound_pairs(),
None,
&normalized_caller_bounds,
&[],
);
let category = origin.to_constraint_category();
outlives.type_must_outlive(origin, sup_type, sub_region, category);
Expand Down Expand Up @@ -240,6 +241,7 @@ where
region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>],
projection_predicates: &'cx [ty::PolyTypeProjectionPredicate<'tcx>],
) -> Self {
Self {
delegate,
Expand All @@ -249,6 +251,7 @@ where
region_bound_pairs,
implicit_region_bound,
caller_bounds,
projection_predicates,
),
}
}
Expand Down Expand Up @@ -319,7 +322,10 @@ where
region: ty::Region<'tcx>,
param_ty: ty::ParamTy,
) {
let verify_bound = self.verify_bound.param_or_placeholder_bound(param_ty.to_ty(self.tcx));
let verify_bound = self
.verify_bound
.param_or_placeholder_bound(param_ty.to_ty(self.tcx), &mut Default::default());
debug!("param_ty_must_outlive: pushing {:?}", verify_bound);
self.delegate.push_verify(origin, GenericKind::Param(param_ty), region, verify_bound);
}

Expand All @@ -330,9 +336,11 @@ where
region: ty::Region<'tcx>,
placeholder_ty: ty::PlaceholderType,
) {
let verify_bound = self
.verify_bound
.param_or_placeholder_bound(Ty::new_placeholder(self.tcx, placeholder_ty));
let verify_bound = self.verify_bound.param_or_placeholder_bound(
Ty::new_placeholder(self.tcx, placeholder_ty),
&mut Default::default(),
);
debug!("placeholder_ty_must_outlive: pushing {:?}", verify_bound);
self.delegate.push_verify(
origin,
GenericKind::Placeholder(placeholder_ty),
Expand Down
45 changes: 40 additions & 5 deletions compiler/rustc_infer/src/infer/outlives/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub struct VerifyBoundCx<'cx, 'tcx> {
/// setting `'?0` to `'empty`.
implicit_region_bound: Option<ty::Region<'tcx>>,
caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>],
projection_predicates: &'cx [ty::PolyTypeProjectionPredicate<'tcx>],
}

impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
Expand All @@ -32,12 +33,23 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>],
projection_predicates: &'cx [ty::PolyTypeProjectionPredicate<'tcx>],
) -> Self {
Self { tcx, region_bound_pairs, implicit_region_bound, caller_bounds }
Self {
tcx,
region_bound_pairs,
implicit_region_bound,
caller_bounds,
projection_predicates,
}
}

#[instrument(level = "debug", skip(self))]
pub fn param_or_placeholder_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
pub fn param_or_placeholder_bound(
&self,
ty: Ty<'tcx>,
visited: &mut SsoHashSet<GenericArg<'tcx>>,
) -> VerifyBound<'tcx> {
// Start with anything like `T: 'a` we can scrape from the
// environment. If the environment contains something like
// `for<'a> T: 'a`, then we know that `T` outlives everything.
Expand All @@ -56,6 +68,28 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
}
}

// A parameter or placeholder bound can also be aliased to an associated type
// so we need to inspect in the "reverse"
for &predicate in self.projection_predicates {
let ty::ProjectionPredicate { term, projection_ty } =
self.tcx.erase_regions(predicate).skip_binder();
if let Some(term) = term.ty()
&& ty == term
{
debug!(?term, ?projection_ty, "found a matching predicate");
let alias_ty_as_ty = projection_ty.to_ty(self.tcx);
let mut components = smallvec![];
compute_alias_components_recursive(
self.tcx,
alias_ty_as_ty,
&mut components,
visited,
);
debug!(?components);
param_bounds.push(self.bound_from_components(&components, visited));
}
}

// Add in the default bound of fn body that applies to all in
// scope type parameters:
if let Some(r) = self.implicit_region_bound {
Expand Down Expand Up @@ -163,10 +197,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
) -> VerifyBound<'tcx> {
match *component {
Component::Region(lt) => VerifyBound::OutlivedBy(lt),
Component::Param(param_ty) => self.param_or_placeholder_bound(param_ty.to_ty(self.tcx)),
Component::Placeholder(placeholder_ty) => {
self.param_or_placeholder_bound(Ty::new_placeholder(self.tcx, placeholder_ty))
Component::Param(param_ty) => {
self.param_or_placeholder_bound(param_ty.to_ty(self.tcx), visited)
}
Component::Placeholder(placeholder_ty) => self
.param_or_placeholder_bound(Ty::new_placeholder(self.tcx, placeholder_ty), visited),
Component::Alias(alias_ty) => self.alias_bound(alias_ty, visited),
Component::EscapingAlias(ref components) => {
self.bound_from_components(components, visited)
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ pub use self::predicate::{
ExistentialTraitRef, NormalizesTo, OutlivesPredicate, PolyCoercePredicate,
PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef,
PolyProjectionPredicate, PolyRegionOutlivesPredicate, PolySubtypePredicate, PolyTraitPredicate,
PolyTraitRef, PolyTypeOutlivesPredicate, Predicate, PredicateKind, ProjectionPredicate,
RegionOutlivesPredicate, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitPredicate,
TraitRef, TypeOutlivesPredicate,
PolyTraitRef, PolyTypeOutlivesPredicate, PolyTypeProjectionPredicate, Predicate, PredicateKind,
ProjectionPredicate, RegionOutlivesPredicate, SubtypePredicate, ToPolyTraitRef, ToPredicate,
TraitPredicate, TraitRef, TypeOutlivesPredicate,
};
pub use self::region::{
BoundRegion, BoundRegionKind, BoundRegionKind::*, EarlyParamRegion, LateParamRegion, Region,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/predicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,7 @@ pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<ty::Region<'tcx>, ty:
pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>;
pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>;
pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>;
pub type PolyTypeProjectionPredicate<'tcx> = ty::Binder<'tcx, ProjectionPredicate<'tcx>>;

/// Encodes that `a` must be a subtype of `b`. The `a_is_expected` flag indicates
/// whether the `a` type is the type that we should label as "expected" when
Expand Down

0 comments on commit 3e320bc

Please sign in to comment.