Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve region bounds from components of type projection #121602

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
47 changes: 42 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,30 @@ 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 Some(ty::ProjectionPredicate { term, projection_ty }) = predicate.no_bound_vars()
else {
continue;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... so if we are here, we have, for now, T == for<'a, 'b, ...> <... as ...>::....

I guess it is safe to say that T: 'static?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it is safe to say that T: 'static?

Why is that true?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I retract. It is not necessarily.

};
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 +199,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
25 changes: 25 additions & 0 deletions tests/ui/nll/ty-outlives/projection-aliasing-issue-121601.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//@ check-pass

pub trait Trait1 {
type Output1;
fn call<'z>(&'z self) -> &'z Self::Output1;
}

pub trait Trait2<T> {
type Output2;
fn call2<'x>(_: &'x T) -> &'x Self::Output2;
}

impl<A, B, T: Trait1<Output1 = A>> Trait2<T> for B
// Mind this `A` here
where
B: Trait2<A>,
{
type Output2 = <B as Trait2<A>>::Output2;
fn call2<'y>(source: &'y T) -> &'y Self::Output2 {
let t = source.call();
B::call2(t)
}
}

fn main() {}