Uplift push_outlives_components
This commit is contained in:
parent
3bec61736a
commit
23c6f23b21
12 changed files with 386 additions and 300 deletions
|
@ -1,266 +0,0 @@
|
||||||
// The outlines relation `T: 'a` or `'a: 'b`. This code frequently
|
|
||||||
// refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that
|
|
||||||
// RFC for reference.
|
|
||||||
|
|
||||||
use rustc_data_structures::sso::SsoHashSet;
|
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
|
|
||||||
use rustc_middle::ty::{GenericArg, GenericArgKind};
|
|
||||||
use smallvec::{smallvec, SmallVec};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Component<'tcx> {
|
|
||||||
Region(ty::Region<'tcx>),
|
|
||||||
Param(ty::ParamTy),
|
|
||||||
Placeholder(ty::PlaceholderType),
|
|
||||||
UnresolvedInferenceVariable(ty::InferTy),
|
|
||||||
|
|
||||||
// Projections like `T::Foo` are tricky because a constraint like
|
|
||||||
// `T::Foo: 'a` can be satisfied in so many ways. There may be a
|
|
||||||
// where-clause that says `T::Foo: 'a`, or the defining trait may
|
|
||||||
// include a bound like `type Foo: 'static`, or -- in the most
|
|
||||||
// conservative way -- we can prove that `T: 'a` (more generally,
|
|
||||||
// that all components in the projection outlive `'a`). This code
|
|
||||||
// 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::AliasTy<'tcx>),
|
|
||||||
|
|
||||||
// In the case where a projection has escaping regions -- meaning
|
|
||||||
// regions bound within the type itself -- we always use
|
|
||||||
// the most conservative rule, which requires that all components
|
|
||||||
// outlive the bound. So for example if we had a type like this:
|
|
||||||
//
|
|
||||||
// for<'a> Trait1< <T as Trait2<'a,'b>>::Foo >
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
//
|
|
||||||
// then the inner projection (underlined) has an escaping region
|
|
||||||
// `'a`. We consider that outer trait `'c` to meet a bound if `'b`
|
|
||||||
// outlives `'b: 'c`, and we don't consider whether the trait
|
|
||||||
// declares that `Foo: 'static` etc. Therefore, we just return the
|
|
||||||
// free components of such a projection (in this case, `'b`).
|
|
||||||
//
|
|
||||||
// However, in the future, we may want to get smarter, and
|
|
||||||
// actually return a "higher-ranked projection" here. Therefore,
|
|
||||||
// we mark that these components are part of an escaping
|
|
||||||
// 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.
|
|
||||||
EscapingAlias(Vec<Component<'tcx>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Push onto `out` all the things that must outlive `'a` for the condition
|
|
||||||
/// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
|
|
||||||
pub fn push_outlives_components<'tcx>(
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
ty0: Ty<'tcx>,
|
|
||||||
out: &mut SmallVec<[Component<'tcx>; 4]>,
|
|
||||||
) {
|
|
||||||
let mut visited = SsoHashSet::new();
|
|
||||||
compute_components(tcx, ty0, out, &mut visited);
|
|
||||||
debug!("components({:?}) = {:?}", ty0, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_components<'tcx>(
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
ty: Ty<'tcx>,
|
|
||||||
out: &mut SmallVec<[Component<'tcx>; 4]>,
|
|
||||||
visited: &mut SsoHashSet<GenericArg<'tcx>>,
|
|
||||||
) {
|
|
||||||
// Descend through the types, looking for the various "base"
|
|
||||||
// components and collecting them into `out`. This is not written
|
|
||||||
// with `collect()` because of the need to sometimes skip subtrees
|
|
||||||
// in the `subtys` iterator (e.g., when encountering a
|
|
||||||
// projection).
|
|
||||||
match *ty.kind() {
|
|
||||||
ty::FnDef(_, args) => {
|
|
||||||
// HACK(eddyb) ignore lifetimes found shallowly in `args`.
|
|
||||||
// This is inconsistent with `ty::Adt` (including all args)
|
|
||||||
// and with `ty::Closure` (ignoring all args other than
|
|
||||||
// upvars, of which a `ty::FnDef` doesn't have any), but
|
|
||||||
// consistent with previous (accidental) behavior.
|
|
||||||
// See https://github.com/rust-lang/rust/issues/70917
|
|
||||||
// for further background and discussion.
|
|
||||||
for child in args {
|
|
||||||
match child.unpack() {
|
|
||||||
GenericArgKind::Type(ty) => {
|
|
||||||
compute_components(tcx, ty, out, visited);
|
|
||||||
}
|
|
||||||
GenericArgKind::Lifetime(_) => {}
|
|
||||||
GenericArgKind::Const(_) => {
|
|
||||||
compute_components_recursive(tcx, child, out, visited);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Pat(element, _) |
|
|
||||||
ty::Array(element, _) => {
|
|
||||||
// Don't look into the len const as it doesn't affect regions
|
|
||||||
compute_components(tcx, element, out, visited);
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Closure(_, args) => {
|
|
||||||
let tupled_ty = args.as_closure().tupled_upvars_ty();
|
|
||||||
compute_components(tcx, tupled_ty, out, visited);
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::CoroutineClosure(_, args) => {
|
|
||||||
let tupled_ty = args.as_coroutine_closure().tupled_upvars_ty();
|
|
||||||
compute_components(tcx, tupled_ty, out, visited);
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Coroutine(_, args) => {
|
|
||||||
// Same as the closure case
|
|
||||||
let tupled_ty = args.as_coroutine().tupled_upvars_ty();
|
|
||||||
compute_components(tcx, tupled_ty, out, visited);
|
|
||||||
|
|
||||||
// We ignore regions in the coroutine interior as we don't
|
|
||||||
// want these to affect region inference
|
|
||||||
}
|
|
||||||
|
|
||||||
// All regions are bound inside a witness
|
|
||||||
ty::CoroutineWitness(..) => (),
|
|
||||||
|
|
||||||
// OutlivesTypeParameterEnv -- the actual checking that `X:'a`
|
|
||||||
// is implied by the environment is done in regionck.
|
|
||||||
ty::Param(p) => {
|
|
||||||
out.push(Component::Param(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Placeholder(p) => {
|
|
||||||
out.push(Component::Placeholder(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
// regionck is not (at least currently) prepared to deal with
|
|
||||||
// higher-ranked regions that may appear in the
|
|
||||||
// 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(_, 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(alias_ty));
|
|
||||||
} else {
|
|
||||||
// fallback case: hard code
|
|
||||||
// OutlivesProjectionComponents. Continue walking
|
|
||||||
// through and constrain Pi.
|
|
||||||
let mut subcomponents = smallvec![];
|
|
||||||
let mut subvisited = SsoHashSet::new();
|
|
||||||
compute_alias_components_recursive(tcx, ty, &mut subcomponents, &mut subvisited);
|
|
||||||
out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We assume that inference variables are fully resolved.
|
|
||||||
// So, if we encounter an inference variable, just record
|
|
||||||
// the unresolved variable as a component.
|
|
||||||
ty::Infer(infer_ty) => {
|
|
||||||
out.push(Component::UnresolvedInferenceVariable(infer_ty));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Most types do not introduce any region binders, nor
|
|
||||||
// involve any other subtle cases, and so the WF relation
|
|
||||||
// simply constraints any regions referenced directly by
|
|
||||||
// the type and then visits the types that are lexically
|
|
||||||
// contained within. (The comments refer to relevant rules
|
|
||||||
// from RFC1214.)
|
|
||||||
ty::Bool | // OutlivesScalar
|
|
||||||
ty::Char | // OutlivesScalar
|
|
||||||
ty::Int(..) | // OutlivesScalar
|
|
||||||
ty::Uint(..) | // OutlivesScalar
|
|
||||||
ty::Float(..) | // OutlivesScalar
|
|
||||||
ty::Never | // ...
|
|
||||||
ty::Adt(..) | // OutlivesNominalType
|
|
||||||
ty::Foreign(..) | // OutlivesNominalType
|
|
||||||
ty::Str | // OutlivesScalar (ish)
|
|
||||||
ty::Slice(..) | // ...
|
|
||||||
ty::RawPtr(..) | // ...
|
|
||||||
ty::Ref(..) | // OutlivesReference
|
|
||||||
ty::Tuple(..) | // ...
|
|
||||||
ty::FnPtr(_) | // OutlivesFunction (*)
|
|
||||||
ty::Dynamic(..) | // OutlivesObject, OutlivesFragment (*)
|
|
||||||
ty::Bound(..) |
|
|
||||||
ty::Error(_) => {
|
|
||||||
// (*) Function pointers and trait objects are both binders.
|
|
||||||
// In the RFC, this means we would add the bound regions to
|
|
||||||
// the "bound regions list". In our representation, no such
|
|
||||||
// list is maintained explicitly, because bound regions
|
|
||||||
// themselves can be readily identified.
|
|
||||||
compute_components_recursive(tcx, ty.into(), out, visited);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collect [Component]s for *all* the args of `parent`.
|
|
||||||
///
|
|
||||||
/// This should not be used to get the components of `parent` itself.
|
|
||||||
/// Use [push_outlives_components] instead.
|
|
||||||
pub(super) fn compute_alias_components_recursive<'tcx>(
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
alias_ty: Ty<'tcx>,
|
|
||||||
out: &mut SmallVec<[Component<'tcx>; 4]>,
|
|
||||||
visited: &mut SsoHashSet<GenericArg<'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 { &[] };
|
|
||||||
for (index, child) in alias_ty.args.iter().enumerate() {
|
|
||||||
if opt_variances.get(index) == Some(&ty::Bivariant) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if !visited.insert(child) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
match child.unpack() {
|
|
||||||
GenericArgKind::Type(ty) => {
|
|
||||||
compute_components(tcx, ty, out, visited);
|
|
||||||
}
|
|
||||||
GenericArgKind::Lifetime(lt) => {
|
|
||||||
// Ignore higher ranked regions.
|
|
||||||
if !lt.is_bound() {
|
|
||||||
out.push(Component::Region(lt));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GenericArgKind::Const(_) => {
|
|
||||||
compute_components_recursive(tcx, child, out, visited);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collect [Component]s for *all* the args of `parent`.
|
|
||||||
///
|
|
||||||
/// This should not be used to get the components of `parent` itself.
|
|
||||||
/// Use [push_outlives_components] instead.
|
|
||||||
fn compute_components_recursive<'tcx>(
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
parent: GenericArg<'tcx>,
|
|
||||||
out: &mut SmallVec<[Component<'tcx>; 4]>,
|
|
||||||
visited: &mut SsoHashSet<GenericArg<'tcx>>,
|
|
||||||
) {
|
|
||||||
for child in parent.walk_shallow(visited) {
|
|
||||||
match child.unpack() {
|
|
||||||
GenericArgKind::Type(ty) => {
|
|
||||||
compute_components(tcx, ty, out, visited);
|
|
||||||
}
|
|
||||||
GenericArgKind::Lifetime(lt) => {
|
|
||||||
// Ignore higher ranked regions.
|
|
||||||
if !lt.is_bound() {
|
|
||||||
out.push(Component::Region(lt));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GenericArgKind::Const(_) => {
|
|
||||||
compute_components_recursive(tcx, child, out, visited);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,8 +7,9 @@ use crate::infer::free_regions::RegionRelations;
|
||||||
use crate::infer::lexical_region_resolve;
|
use crate::infer::lexical_region_resolve;
|
||||||
use rustc_middle::traits::query::{NoSolution, OutlivesBound};
|
use rustc_middle::traits::query::{NoSolution, OutlivesBound};
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
|
// TODO: Remove me
|
||||||
|
pub use rustc_type_ir::outlives as components;
|
||||||
|
|
||||||
pub mod components;
|
|
||||||
pub mod env;
|
pub mod env;
|
||||||
pub mod for_liveness;
|
pub mod for_liveness;
|
||||||
pub mod obligations;
|
pub mod obligations;
|
||||||
|
|
|
@ -291,7 +291,7 @@ where
|
||||||
fn components_must_outlive(
|
fn components_must_outlive(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: infer::SubregionOrigin<'tcx>,
|
origin: infer::SubregionOrigin<'tcx>,
|
||||||
components: &[Component<'tcx>],
|
components: &[Component<TyCtxt<'tcx>>],
|
||||||
region: ty::Region<'tcx>,
|
region: ty::Region<'tcx>,
|
||||||
category: ConstraintCategory<'tcx>,
|
category: ConstraintCategory<'tcx>,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -139,7 +139,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
||||||
|
|
||||||
fn bound_from_components(
|
fn bound_from_components(
|
||||||
&self,
|
&self,
|
||||||
components: &[Component<'tcx>],
|
components: &[Component<TyCtxt<'tcx>>],
|
||||||
visited: &mut SsoHashSet<GenericArg<'tcx>>,
|
visited: &mut SsoHashSet<GenericArg<'tcx>>,
|
||||||
) -> VerifyBound<'tcx> {
|
) -> VerifyBound<'tcx> {
|
||||||
let mut bounds = components
|
let mut bounds = components
|
||||||
|
@ -158,7 +158,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
||||||
|
|
||||||
fn bound_from_single_component(
|
fn bound_from_single_component(
|
||||||
&self,
|
&self,
|
||||||
component: &Component<'tcx>,
|
component: &Component<TyCtxt<'tcx>>,
|
||||||
visited: &mut SsoHashSet<GenericArg<'tcx>>,
|
visited: &mut SsoHashSet<GenericArg<'tcx>>,
|
||||||
) -> VerifyBound<'tcx> {
|
) -> VerifyBound<'tcx> {
|
||||||
match *component {
|
match *component {
|
||||||
|
|
|
@ -54,6 +54,13 @@ pub struct Expr<'tcx> {
|
||||||
pub kind: ExprKind,
|
pub kind: ExprKind,
|
||||||
args: ty::GenericArgsRef<'tcx>,
|
args: ty::GenericArgsRef<'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'tcx> rustc_type_ir::inherent::ExprConst<TyCtxt<'tcx>> for Expr<'tcx> {
|
||||||
|
fn args(self) -> ty::GenericArgsRef<'tcx> {
|
||||||
|
self.args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> Expr<'tcx> {
|
impl<'tcx> Expr<'tcx> {
|
||||||
pub fn new_binop(
|
pub fn new_binop(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
|
|
|
@ -284,7 +284,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
|
||||||
/// those relationships.
|
/// those relationships.
|
||||||
fn implied_bounds_from_components<'tcx>(
|
fn implied_bounds_from_components<'tcx>(
|
||||||
sub_region: ty::Region<'tcx>,
|
sub_region: ty::Region<'tcx>,
|
||||||
sup_components: SmallVec<[Component<'tcx>; 4]>,
|
sup_components: SmallVec<[Component<TyCtxt<'tcx>>; 4]>,
|
||||||
) -> Vec<OutlivesBound<'tcx>> {
|
) -> Vec<OutlivesBound<'tcx>> {
|
||||||
sup_components
|
sup_components
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -232,6 +232,10 @@ pub trait Region<I: Interner<Region = Self>>:
|
||||||
fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
|
fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
|
||||||
|
|
||||||
fn new_static(interner: I) -> Self;
|
fn new_static(interner: I) -> Self;
|
||||||
|
|
||||||
|
fn is_bound(self) -> bool {
|
||||||
|
matches!(self.kind(), ty::ReBound(..))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Const<I: Interner<Const = Self>>:
|
pub trait Const<I: Interner<Const = Self>>:
|
||||||
|
@ -272,6 +276,10 @@ pub trait Const<I: Interner<Const = Self>>:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ExprConst<I: Interner<ExprConst = Self>>: Copy + Debug + Hash + Eq + Relate<I> {
|
||||||
|
fn args(self) -> I::GenericArgs;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait GenericsOf<I: Interner<GenericsOf = Self>> {
|
pub trait GenericsOf<I: Interner<GenericsOf = Self>> {
|
||||||
fn count(&self) -> usize;
|
fn count(&self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ pub trait Interner:
|
||||||
type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
|
type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
|
||||||
type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
|
type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
|
||||||
type ValueConst: Copy + Debug + Hash + Eq;
|
type ValueConst: Copy + Debug + Hash + Eq;
|
||||||
type ExprConst: Copy + Debug + Hash + Eq + Relate<Self>;
|
type ExprConst: ExprConst<Self>;
|
||||||
|
|
||||||
// Kinds of regions
|
// Kinds of regions
|
||||||
type Region: Region<Self>;
|
type Region: Region<Self>;
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub mod inherent;
|
||||||
pub mod ir_print;
|
pub mod ir_print;
|
||||||
pub mod lang_items;
|
pub mod lang_items;
|
||||||
pub mod lift;
|
pub mod lift;
|
||||||
|
pub mod outlives;
|
||||||
pub mod relate;
|
pub mod relate;
|
||||||
pub mod solve;
|
pub mod solve;
|
||||||
|
|
||||||
|
|
335
compiler/rustc_type_ir/src/outlives.rs
Normal file
335
compiler/rustc_type_ir/src/outlives.rs
Normal file
|
@ -0,0 +1,335 @@
|
||||||
|
//! The outlives relation `T: 'a` or `'a: 'b`. This code frequently
|
||||||
|
//! refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that
|
||||||
|
//! RFC for reference.
|
||||||
|
|
||||||
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
|
use crate::data_structures::SsoHashSet;
|
||||||
|
use crate::inherent::*;
|
||||||
|
use crate::visit::TypeVisitableExt as _;
|
||||||
|
use crate::{self as ty, Interner};
|
||||||
|
|
||||||
|
#[derive(derivative::Derivative)]
|
||||||
|
#[derivative(Debug(bound = ""))]
|
||||||
|
pub enum Component<I: Interner> {
|
||||||
|
Region(I::Region),
|
||||||
|
Param(I::ParamTy),
|
||||||
|
Placeholder(I::PlaceholderTy),
|
||||||
|
UnresolvedInferenceVariable(ty::InferTy),
|
||||||
|
|
||||||
|
// Projections like `T::Foo` are tricky because a constraint like
|
||||||
|
// `T::Foo: 'a` can be satisfied in so many ways. There may be a
|
||||||
|
// where-clause that says `T::Foo: 'a`, or the defining trait may
|
||||||
|
// include a bound like `type Foo: 'static`, or -- in the most
|
||||||
|
// conservative way -- we can prove that `T: 'a` (more generally,
|
||||||
|
// that all components in the projection outlive `'a`). This code
|
||||||
|
// 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::AliasTy<I>),
|
||||||
|
|
||||||
|
// In the case where a projection has escaping regions -- meaning
|
||||||
|
// regions bound within the type itself -- we always use
|
||||||
|
// the most conservative rule, which requires that all components
|
||||||
|
// outlive the bound. So for example if we had a type like this:
|
||||||
|
//
|
||||||
|
// for<'a> Trait1< <T as Trait2<'a,'b>>::Foo >
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
//
|
||||||
|
// then the inner projection (underlined) has an escaping region
|
||||||
|
// `'a`. We consider that outer trait `'c` to meet a bound if `'b`
|
||||||
|
// outlives `'b: 'c`, and we don't consider whether the trait
|
||||||
|
// declares that `Foo: 'static` etc. Therefore, we just return the
|
||||||
|
// free components of such a projection (in this case, `'b`).
|
||||||
|
//
|
||||||
|
// However, in the future, we may want to get smarter, and
|
||||||
|
// actually return a "higher-ranked projection" here. Therefore,
|
||||||
|
// we mark that these components are part of an escaping
|
||||||
|
// 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.
|
||||||
|
EscapingAlias(Vec<Component<I>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push onto `out` all the things that must outlive `'a` for the condition
|
||||||
|
/// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
|
||||||
|
pub fn push_outlives_components<I: Interner>(
|
||||||
|
tcx: I,
|
||||||
|
ty0: I::Ty,
|
||||||
|
out: &mut SmallVec<[Component<I>; 4]>,
|
||||||
|
) {
|
||||||
|
let mut visited = SsoHashSet::new();
|
||||||
|
compute_components_for_ty(tcx, ty0, out, &mut visited);
|
||||||
|
debug!("components({:?}) = {:?}", ty0, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_components_for_arg<I: Interner>(
|
||||||
|
tcx: I,
|
||||||
|
arg: I::GenericArg,
|
||||||
|
out: &mut SmallVec<[Component<I>; 4]>,
|
||||||
|
visited: &mut SsoHashSet<I::GenericArg>,
|
||||||
|
) {
|
||||||
|
match arg.kind() {
|
||||||
|
ty::GenericArgKind::Type(ty) => {
|
||||||
|
compute_components_for_ty(tcx, ty, out, visited);
|
||||||
|
}
|
||||||
|
ty::GenericArgKind::Lifetime(lt) => {
|
||||||
|
compute_components_for_lt(lt, out);
|
||||||
|
}
|
||||||
|
ty::GenericArgKind::Const(ct) => {
|
||||||
|
compute_components_for_const(tcx, ct, out, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_components_for_ty<I: Interner>(
|
||||||
|
tcx: I,
|
||||||
|
ty: I::Ty,
|
||||||
|
out: &mut SmallVec<[Component<I>; 4]>,
|
||||||
|
visited: &mut SsoHashSet<I::GenericArg>,
|
||||||
|
) {
|
||||||
|
if !visited.insert(ty.into()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Descend through the types, looking for the various "base"
|
||||||
|
// components and collecting them into `out`. This is not written
|
||||||
|
// with `collect()` because of the need to sometimes skip subtrees
|
||||||
|
// in the `subtys` iterator (e.g., when encountering a
|
||||||
|
// projection).
|
||||||
|
match ty.kind() {
|
||||||
|
ty::FnDef(_, args) => {
|
||||||
|
// HACK(eddyb) ignore lifetimes found shallowly in `args`.
|
||||||
|
// This is inconsistent with `ty::Adt` (including all args)
|
||||||
|
// and with `ty::Closure` (ignoring all args other than
|
||||||
|
// upvars, of which a `ty::FnDef` doesn't have any), but
|
||||||
|
// consistent with previous (accidental) behavior.
|
||||||
|
// See https://github.com/rust-lang/rust/issues/70917
|
||||||
|
// for further background and discussion.
|
||||||
|
for child in args.iter() {
|
||||||
|
match child.kind() {
|
||||||
|
ty::GenericArgKind::Type(ty) => {
|
||||||
|
compute_components_for_ty(tcx, ty, out, visited);
|
||||||
|
}
|
||||||
|
ty::GenericArgKind::Lifetime(_) => {}
|
||||||
|
ty::GenericArgKind::Const(ct) => {
|
||||||
|
compute_components_for_const(tcx, ct, out, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Pat(element, _) | ty::Array(element, _) => {
|
||||||
|
compute_components_for_ty(tcx, element, out, visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Closure(_, args) => {
|
||||||
|
let tupled_ty = args.as_closure().tupled_upvars_ty();
|
||||||
|
compute_components_for_ty(tcx, tupled_ty, out, visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::CoroutineClosure(_, args) => {
|
||||||
|
let tupled_ty = args.as_coroutine_closure().tupled_upvars_ty();
|
||||||
|
compute_components_for_ty(tcx, tupled_ty, out, visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Coroutine(_, args) => {
|
||||||
|
// Same as the closure case
|
||||||
|
let tupled_ty = args.as_coroutine().tupled_upvars_ty();
|
||||||
|
compute_components_for_ty(tcx, tupled_ty, out, visited);
|
||||||
|
|
||||||
|
// We ignore regions in the coroutine interior as we don't
|
||||||
|
// want these to affect region inference
|
||||||
|
}
|
||||||
|
|
||||||
|
// All regions are bound inside a witness, and we don't emit
|
||||||
|
// higher-ranked outlives components currently.
|
||||||
|
ty::CoroutineWitness(..) => {},
|
||||||
|
|
||||||
|
// OutlivesTypeParameterEnv -- the actual checking that `X:'a`
|
||||||
|
// is implied by the environment is done in regionck.
|
||||||
|
ty::Param(p) => {
|
||||||
|
out.push(Component::Param(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Placeholder(p) => {
|
||||||
|
out.push(Component::Placeholder(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
// regionck is not (at least currently) prepared to deal with
|
||||||
|
// higher-ranked regions that may appear in the
|
||||||
|
// 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(_, 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(alias_ty));
|
||||||
|
} else {
|
||||||
|
// fallback case: hard code
|
||||||
|
// OutlivesProjectionComponents. Continue walking
|
||||||
|
// through and constrain Pi.
|
||||||
|
let mut subcomponents = smallvec![];
|
||||||
|
let mut subvisited = SsoHashSet::new();
|
||||||
|
compute_alias_components_recursive(tcx, ty, &mut subcomponents, &mut subvisited);
|
||||||
|
out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We assume that inference variables are fully resolved.
|
||||||
|
// So, if we encounter an inference variable, just record
|
||||||
|
// the unresolved variable as a component.
|
||||||
|
ty::Infer(infer_ty) => {
|
||||||
|
out.push(Component::UnresolvedInferenceVariable(infer_ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Most types do not introduce any region binders, nor
|
||||||
|
// involve any other subtle cases, and so the WF relation
|
||||||
|
// simply constraints any regions referenced directly by
|
||||||
|
// the type and then visits the types that are lexically
|
||||||
|
// contained within. (The comments refer to relevant rules
|
||||||
|
// from RFC1214.)
|
||||||
|
|
||||||
|
ty::Bool | // OutlivesScalar
|
||||||
|
ty::Char | // OutlivesScalar
|
||||||
|
ty::Int(..) | // OutlivesScalar
|
||||||
|
ty::Uint(..) | // OutlivesScalar
|
||||||
|
ty::Float(..) | // OutlivesScalar
|
||||||
|
ty::Never | // OutlivesScalar
|
||||||
|
ty::Foreign(..) | // OutlivesNominalType
|
||||||
|
ty::Str | // OutlivesScalar (ish)
|
||||||
|
ty::Bound(..) |
|
||||||
|
ty::Error(_) => {
|
||||||
|
// Trivial.
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutlivesNominalType
|
||||||
|
ty::Adt(_, args) => {
|
||||||
|
for arg in args.iter() {
|
||||||
|
compute_components_for_arg(tcx, arg, out, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutlivesNominalType
|
||||||
|
ty::Slice(ty) |
|
||||||
|
ty::RawPtr(ty, _) => {
|
||||||
|
compute_components_for_ty(tcx, ty, out, visited);
|
||||||
|
}
|
||||||
|
ty::Tuple(tys) => {
|
||||||
|
for ty in tys.iter() {
|
||||||
|
compute_components_for_ty(tcx, ty, out, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutlivesReference
|
||||||
|
ty::Ref(lt, ty, _) => {
|
||||||
|
compute_components_for_lt(lt, out);
|
||||||
|
compute_components_for_ty(tcx, ty, out, visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Dynamic(preds, lt, _) => {
|
||||||
|
compute_components_for_lt(lt, out);
|
||||||
|
for pred in preds.iter() {
|
||||||
|
match pred.skip_binder() {
|
||||||
|
ty::ExistentialPredicate::Trait(tr) => {
|
||||||
|
for arg in tr.args.iter() {
|
||||||
|
compute_components_for_arg(tcx, arg, out, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::ExistentialPredicate::Projection(proj) => {
|
||||||
|
for arg in proj.args.iter() {
|
||||||
|
compute_components_for_arg(tcx, arg, out, visited);
|
||||||
|
}
|
||||||
|
match proj.term.kind() {
|
||||||
|
ty::TermKind::Ty(ty) => {
|
||||||
|
compute_components_for_ty(tcx, ty, out, visited)
|
||||||
|
}
|
||||||
|
ty::TermKind::Const(ct) => {
|
||||||
|
compute_components_for_const(tcx, ct, out, visited)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::ExistentialPredicate::AutoTrait(..) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::FnPtr(sig) => {
|
||||||
|
for ty in sig.skip_binder().inputs_and_output.iter() {
|
||||||
|
compute_components_for_ty(tcx, ty, out, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collect [Component]s for *all* the args of `parent`.
|
||||||
|
///
|
||||||
|
/// This should not be used to get the components of `parent` itself.
|
||||||
|
/// Use [push_outlives_components] instead.
|
||||||
|
pub fn compute_alias_components_recursive<I: Interner>(
|
||||||
|
tcx: I,
|
||||||
|
alias_ty: I::Ty,
|
||||||
|
out: &mut SmallVec<[Component<I>; 4]>,
|
||||||
|
visited: &mut SsoHashSet<I::GenericArg>,
|
||||||
|
) {
|
||||||
|
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 { Some(tcx.variances_of(alias_ty.def_id)) } else { None };
|
||||||
|
|
||||||
|
for (index, child) in alias_ty.args.iter().enumerate() {
|
||||||
|
if opt_variances.and_then(|variances| variances.get(index)) == Some(ty::Bivariant) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
compute_components_for_arg(tcx, child, out, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_components_for_lt<I: Interner>(lt: I::Region, out: &mut SmallVec<[Component<I>; 4]>) {
|
||||||
|
if !lt.is_bound() {
|
||||||
|
out.push(Component::Region(lt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_components_for_const<I: Interner>(
|
||||||
|
tcx: I,
|
||||||
|
ct: I::Const,
|
||||||
|
out: &mut SmallVec<[Component<I>; 4]>,
|
||||||
|
visited: &mut SsoHashSet<I::GenericArg>,
|
||||||
|
) {
|
||||||
|
if !visited.insert(ct.into()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
match ct.kind() {
|
||||||
|
ty::ConstKind::Param(_)
|
||||||
|
| ty::ConstKind::Bound(_, _)
|
||||||
|
| ty::ConstKind::Infer(_)
|
||||||
|
| ty::ConstKind::Placeholder(_)
|
||||||
|
| ty::ConstKind::Error(_) => {
|
||||||
|
// Trivial
|
||||||
|
}
|
||||||
|
ty::ConstKind::Expr(e) => {
|
||||||
|
for arg in e.args().iter() {
|
||||||
|
compute_components_for_arg(tcx, arg, out, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::ConstKind::Value(ty, _) => {
|
||||||
|
compute_components_for_ty(tcx, ty, out, visited);
|
||||||
|
}
|
||||||
|
ty::ConstKind::Unevaluated(uv) => {
|
||||||
|
for arg in uv.args.iter() {
|
||||||
|
compute_components_for_arg(tcx, arg, out, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,3 @@
|
||||||
error[E0311]: the parameter type `U` may not live long enough
|
|
||||||
--> $DIR/async-generics-and-bounds.rs:9:5
|
|
||||||
|
|
|
||||||
LL | async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
|
|
||||||
| ^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
| | |
|
|
||||||
| | the parameter type `U` must be valid for the anonymous lifetime as defined here...
|
|
||||||
| ...so that the reference type `&(T, U)` does not outlive the data it points at
|
|
||||||
|
|
|
||||||
help: consider adding an explicit lifetime bound
|
|
||||||
|
|
|
||||||
LL | async fn foo<'a>(&'a self) -> &'a (T, U) where T: Debug + Sized, U: Hash, U: 'a;
|
|
||||||
| ++++ ++ ++ +++++++
|
|
||||||
|
|
||||||
error[E0311]: the parameter type `T` may not live long enough
|
error[E0311]: the parameter type `T` may not live long enough
|
||||||
--> $DIR/async-generics-and-bounds.rs:9:5
|
--> $DIR/async-generics-and-bounds.rs:9:5
|
||||||
|
|
|
|
||||||
|
@ -26,6 +12,20 @@ help: consider adding an explicit lifetime bound
|
||||||
LL | async fn foo<'a>(&'a self) -> &'a (T, U) where T: Debug + Sized, U: Hash, T: 'a;
|
LL | async fn foo<'a>(&'a self) -> &'a (T, U) where T: Debug + Sized, U: Hash, T: 'a;
|
||||||
| ++++ ++ ++ +++++++
|
| ++++ ++ ++ +++++++
|
||||||
|
|
||||||
|
error[E0311]: the parameter type `U` may not live long enough
|
||||||
|
--> $DIR/async-generics-and-bounds.rs:9:5
|
||||||
|
|
|
||||||
|
LL | async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
|
||||||
|
| ^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
| | |
|
||||||
|
| | the parameter type `U` must be valid for the anonymous lifetime as defined here...
|
||||||
|
| ...so that the reference type `&(T, U)` does not outlive the data it points at
|
||||||
|
|
|
||||||
|
help: consider adding an explicit lifetime bound
|
||||||
|
|
|
||||||
|
LL | async fn foo<'a>(&'a self) -> &'a (T, U) where T: Debug + Sized, U: Hash, U: 'a;
|
||||||
|
| ++++ ++ ++ +++++++
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0311`.
|
For more information about this error, try `rustc --explain E0311`.
|
||||||
|
|
|
@ -1,17 +1,3 @@
|
||||||
error[E0311]: the parameter type `U` may not live long enough
|
|
||||||
--> $DIR/async-generics.rs:6:5
|
|
||||||
|
|
|
||||||
LL | async fn foo(&self) -> &(T, U);
|
|
||||||
| ^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^
|
|
||||||
| | |
|
|
||||||
| | the parameter type `U` must be valid for the anonymous lifetime as defined here...
|
|
||||||
| ...so that the reference type `&(T, U)` does not outlive the data it points at
|
|
||||||
|
|
|
||||||
help: consider adding an explicit lifetime bound
|
|
||||||
|
|
|
||||||
LL | async fn foo<'a>(&'a self) -> &'a (T, U) where U: 'a;
|
|
||||||
| ++++ ++ ++ +++++++++++
|
|
||||||
|
|
||||||
error[E0311]: the parameter type `T` may not live long enough
|
error[E0311]: the parameter type `T` may not live long enough
|
||||||
--> $DIR/async-generics.rs:6:5
|
--> $DIR/async-generics.rs:6:5
|
||||||
|
|
|
|
||||||
|
@ -26,6 +12,20 @@ help: consider adding an explicit lifetime bound
|
||||||
LL | async fn foo<'a>(&'a self) -> &'a (T, U) where T: 'a;
|
LL | async fn foo<'a>(&'a self) -> &'a (T, U) where T: 'a;
|
||||||
| ++++ ++ ++ +++++++++++
|
| ++++ ++ ++ +++++++++++
|
||||||
|
|
||||||
|
error[E0311]: the parameter type `U` may not live long enough
|
||||||
|
--> $DIR/async-generics.rs:6:5
|
||||||
|
|
|
||||||
|
LL | async fn foo(&self) -> &(T, U);
|
||||||
|
| ^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^
|
||||||
|
| | |
|
||||||
|
| | the parameter type `U` must be valid for the anonymous lifetime as defined here...
|
||||||
|
| ...so that the reference type `&(T, U)` does not outlive the data it points at
|
||||||
|
|
|
||||||
|
help: consider adding an explicit lifetime bound
|
||||||
|
|
|
||||||
|
LL | async fn foo<'a>(&'a self) -> &'a (T, U) where U: 'a;
|
||||||
|
| ++++ ++ ++ +++++++++++
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0311`.
|
For more information about this error, try `rustc --explain E0311`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue