Skip to content

Commit

Permalink
Auto merge of rust-lang#106910 - aliemjay:alias-ty-in-regionck, r=oli…
Browse files Browse the repository at this point in the history
…-obk

even more unify Projection/Opaque handling in region outlives code

edit: This continues ate the same pace as rust-lang#106829. New changes are described in rust-lang#106910 (comment).

~This touches `OutlivesBound`, `Component`, `GenericKind` enums.~

r? `@oli-obk` (because of overlap with rust-lang#95474)
  • Loading branch information
bors committed Jan 19, 2023
2 parents 79335f1 + e40567b commit 19423b5
Show file tree
Hide file tree
Showing 13 changed files with 108 additions and 151 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
.insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
}

OutlivesBound::RegionSubAlias(r_a, kind, alias_b) => {
OutlivesBound::RegionSubAlias(r_a, alias_b) => {
self.region_bound_pairs
.insert(ty::OutlivesPredicate(GenericKind::Alias(kind, alias_b), r_a));
.insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a));
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_hir_analysis/src/outlives/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>(
.or_insert(span);
}

Component::Alias(kind, alias) => {
Component::Alias(alias_ty) => {
// This would either arise from something like:
//
// ```
Expand All @@ -99,13 +99,13 @@ pub(crate) fn insert_outlives_predicate<'tcx>(
//
// Here we want to add an explicit `where <T as Iterator>::Item: 'a`
// or `Opaque<T>: 'a` depending on the alias kind.
let ty: Ty<'tcx> = tcx.mk_ty(ty::Alias(kind, alias));
let ty = alias_ty.to_ty(tcx);
required_predicates
.entry(ty::OutlivesPredicate(ty.into(), outlived_region))
.or_insert(span);
}

Component::EscapingProjection(_) => {
Component::EscapingAlias(_) => {
// As above, but the projection involves
// late-bound regions. Therefore, the WF
// requirement is not checked in type definition
Expand Down
11 changes: 4 additions & 7 deletions compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2272,13 +2272,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {

let labeled_user_string = match bound_kind {
GenericKind::Param(ref p) => format!("the parameter type `{}`", p),
GenericKind::Alias(ty::Projection, ref p) => format!("the associated type `{}`", p),
GenericKind::Alias(ty::Opaque, ref p) => {
format!(
"the opaque type `{}`",
self.tcx.def_path_str_with_substs(p.def_id, p.substs)
)
}
GenericKind::Alias(ref p) => match p.kind(self.tcx) {
ty::AliasKind::Projection => format!("the associated type `{}`", p),
ty::AliasKind::Opaque => format!("the opaque type `{}`", p),
},
};

if let Some(SubregionOrigin::CompareImplItemObligation {
Expand Down
23 changes: 6 additions & 17 deletions compiler/rustc_infer/src/infer/outlives/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub enum Component<'tcx> {
// is not in a position to judge which is the best technique, so
// we just product the projection as a component and leave it to
// the consumer to decide (but see `EscapingProjection` below).
Alias(ty::AliasKind, ty::AliasTy<'tcx>),
Alias(ty::AliasTy<'tcx>),

// In the case where a projection has escaping regions -- meaning
// regions bound within the type itself -- we always use
Expand All @@ -44,7 +44,7 @@ pub enum Component<'tcx> {
// projection, so that implied bounds code can avoid relying on
// them. This gives us room to improve the regionck reasoning in
// the future without breaking backwards compat.
EscapingProjection(Vec<Component<'tcx>>),
EscapingAlias(Vec<Component<'tcx>>),
}

/// Push onto `out` all the things that must outlive `'a` for the condition
Expand Down Expand Up @@ -120,17 +120,6 @@ fn compute_components<'tcx>(
out.push(Component::Param(p));
}

// Ignore lifetimes found in opaque types. Opaque types can
// have lifetimes in their substs which their hidden type doesn't
// actually use. If we inferred that an opaque type is outlived by
// its parameter lifetimes, then we could prove that any lifetime
// outlives any other lifetime, which is unsound.
// See https://github.com/rust-lang/rust/issues/84305 for
// more details.
ty::Alias(ty::Opaque, data) => {
out.push(Component::Alias(ty::Opaque, data));
},

// For projections, we prefer to generate an obligation like
// `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
// regionck more ways to prove that it holds. However,
Expand All @@ -139,23 +128,23 @@ fn compute_components<'tcx>(
// trait-ref. Therefore, if we see any higher-ranked regions,
// we simply fallback to the most restrictive rule, which
// requires that `Pi: 'a` for all `i`.
ty::Alias(ty::Projection, data) => {
if !data.has_escaping_bound_vars() {
ty::Alias(_, alias_ty) => {
if !alias_ty.has_escaping_bound_vars() {
// best case: no escaping regions, so push the
// projection and skip the subtree (thus generating no
// constraints for Pi). This defers the choice between
// the rules OutlivesProjectionEnv,
// OutlivesProjectionTraitDef, and
// OutlivesProjectionComponents to regionck.
out.push(Component::Alias(ty::Projection, data));
out.push(Component::Alias(alias_ty));
} else {
// fallback case: hard code
// OutlivesProjectionComponents. Continue walking
// through and constrain Pi.
let mut subcomponents = smallvec![];
let mut subvisited = SsoHashSet::new();
compute_components_recursive(tcx, ty.into(), &mut subcomponents, &mut subvisited);
out.push(Component::EscapingProjection(subcomponents.into_iter().collect()));
out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
}
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_infer/src/infer/outlives/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ impl<'tcx> OutlivesEnvironmentBuilder<'tcx> {
self.region_bound_pairs
.insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
}
OutlivesBound::RegionSubAlias(r_a, kind, projection_b) => {
OutlivesBound::RegionSubAlias(r_a, alias_b) => {
self.region_bound_pairs
.insert(ty::OutlivesPredicate(GenericKind::Alias(kind, projection_b), r_a));
.insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a));
}
OutlivesBound::RegionSubRegion(r_a, r_b) => {
if let (ReEarlyBound(_) | ReFree(_), ReVar(vid_b)) = (r_a.kind(), r_b.kind()) {
Expand Down
81 changes: 19 additions & 62 deletions compiler/rustc_infer/src/infer/outlives/obligations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ use crate::infer::{
};
use crate::traits::{ObligationCause, ObligationCauseCode};
use rustc_data_structures::undo_log::UndoLogs;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, Region, SubstsRef, Ty, TyCtxt, TypeVisitable};
Expand Down Expand Up @@ -266,10 +265,8 @@ where
Component::Param(param_ty) => {
self.param_ty_must_outlive(origin, region, *param_ty);
}
Component::Alias(kind, data) => {
self.alias_must_outlive(*kind, *data, origin, region)
}
Component::EscapingProjection(subcomponents) => {
Component::Alias(alias_ty) => self.alias_ty_must_outlive(origin, region, *alias_ty),
Component::EscapingAlias(subcomponents) => {
self.components_must_outlive(origin, &subcomponents, region, category);
}
Component::UnresolvedInferenceVariable(v) => {
Expand All @@ -285,61 +282,26 @@ where
}
}

#[instrument(level = "debug", skip(self))]
fn param_ty_must_outlive(
&mut self,
origin: infer::SubregionOrigin<'tcx>,
region: ty::Region<'tcx>,
param_ty: ty::ParamTy,
) {
debug!(
"param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})",
region, param_ty, origin
);

let generic = GenericKind::Param(param_ty);
let verify_bound = self.verify_bound.param_bound(param_ty);
self.delegate.push_verify(origin, generic, region, verify_bound);
self.delegate.push_verify(origin, GenericKind::Param(param_ty), region, verify_bound);
}

#[instrument(level = "debug", skip(self))]
fn alias_must_outlive(
fn alias_ty_must_outlive(
&mut self,
kind: ty::AliasKind,
data: ty::AliasTy<'tcx>,
origin: infer::SubregionOrigin<'tcx>,
region: ty::Region<'tcx>,
) {
self.generic_must_outlive(
origin,
region,
GenericKind::Alias(kind, data),
data.def_id,
data.substs,
kind == ty::Opaque,
|ty| match *ty.kind() {
ty::Alias(filter_kind, ty::AliasTy { def_id, substs, .. })
if kind == filter_kind =>
{
(def_id, substs)
}
_ => bug!("expected only projection types from env, not {:?}", ty),
},
);
}

#[instrument(level = "debug", skip(self, filter))]
fn generic_must_outlive(
&mut self,
origin: infer::SubregionOrigin<'tcx>,
region: ty::Region<'tcx>,
generic: GenericKind<'tcx>,
def_id: DefId,
substs: SubstsRef<'tcx>,
is_opaque: bool,
filter: impl Fn(Ty<'tcx>) -> (DefId, SubstsRef<'tcx>),
alias_ty: ty::AliasTy<'tcx>,
) {
// An optimization for a common case with opaque types.
if substs.is_empty() {
if alias_ty.substs.is_empty() {
return;
}

Expand All @@ -361,14 +323,14 @@ where
// These are guaranteed to apply, no matter the inference
// results.
let trait_bounds: Vec<_> =
self.verify_bound.declared_region_bounds(def_id, substs).collect();
self.verify_bound.declared_bounds_from_definition(alias_ty).collect();

debug!(?trait_bounds);

// Compute the bounds we can derive from the environment. This
// is an "approximate" match -- in some cases, these bounds
// may not apply.
let mut approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(generic);
let mut approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(alias_ty);
debug!(?approx_env_bounds);

// Remove outlives bounds that we get from the environment but
Expand All @@ -383,8 +345,8 @@ where
// If the declaration is `trait Trait<'b> { type Item: 'b; }`, then `projection_declared_bounds_from_trait`
// will be invoked with `['b => ^1]` and so we will get `^1` returned.
let bound = bound_outlives.skip_binder();
let (def_id, substs) = filter(bound.0);
self.verify_bound.declared_region_bounds(def_id, substs).all(|r| r != bound.1)
let ty::Alias(_, alias_ty) = bound.0.kind() else { bug!("expected AliasTy") };
self.verify_bound.declared_bounds_from_definition(*alias_ty).all(|r| r != bound.1)
});

// If declared bounds list is empty, the only applicable rule is
Expand All @@ -401,12 +363,12 @@ where
// the problem is to add `T: 'r`, which isn't true. So, if there are no
// inference variables, we use a verify constraint instead of adding
// edges, which winds up enforcing the same condition.
let needs_infer = substs.needs_infer();
if approx_env_bounds.is_empty() && trait_bounds.is_empty() && (needs_infer || is_opaque) {
if approx_env_bounds.is_empty()
&& trait_bounds.is_empty()
&& (alias_ty.needs_infer() || alias_ty.kind(self.tcx) == ty::Opaque)
{
debug!("no declared bounds");

self.substs_must_outlive(substs, origin, region);

self.substs_must_outlive(alias_ty.substs, origin, region);
return;
}

Expand Down Expand Up @@ -447,14 +409,9 @@ where
// projection outlive; in some cases, this may add insufficient
// edges into the inference graph, leading to inference failures
// even though a satisfactory solution exists.
let verify_bound = self.verify_bound.projection_opaque_bounds(
generic,
def_id,
substs,
&mut Default::default(),
);
debug!("projection_must_outlive: pushing {:?}", verify_bound);
self.delegate.push_verify(origin, generic, region, verify_bound);
let verify_bound = self.verify_bound.alias_bound(alias_ty, &mut Default::default());
debug!("alias_must_outlive: pushing {:?}", verify_bound);
self.delegate.push_verify(origin, GenericKind::Alias(alias_ty), region, verify_bound);
}

fn substs_must_outlive(
Expand Down
Loading

0 comments on commit 19423b5

Please sign in to comment.