Auto merge of #95474 - oli-obk:tait_ub, r=jackh726

Neither require nor imply lifetime bounds on opaque type for well formedness

The actual hidden type can live arbitrarily longer than any individual lifetime and arbitrarily shorter than all but one of the lifetimes.

fixes #86218
fixes #84305

This is a **breaking change** but it is a necessary soundness fix
This commit is contained in:
bors 2022-09-25 19:15:26 +00:00
commit f5193a9fcc
34 changed files with 696 additions and 153 deletions

View File

@ -362,6 +362,11 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
self.region_bound_pairs
.insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a));
}
OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => {
self.region_bound_pairs
.insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a));
}
}
}
}

View File

@ -2481,6 +2481,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
let labeled_user_string = match bound_kind {
GenericKind::Param(ref p) => format!("the parameter type `{}`", p),
GenericKind::Projection(ref p) => format!("the associated type `{}`", p),
GenericKind::Opaque(def_id, substs) => {
format!("the opaque type `{}`", self.tcx.def_path_str_with_substs(def_id, substs))
}
};
if let Some(SubregionOrigin::CompareImplItemObligation {

View File

@ -3,8 +3,9 @@
// RFC for reference.
use rustc_data_structures::sso::SsoHashSet;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
use rustc_middle::ty::{self, SubstsRef, Ty, TyCtxt, TypeVisitable};
use smallvec::{smallvec, SmallVec};
#[derive(Debug)]
@ -45,6 +46,8 @@ pub enum Component<'tcx> {
// them. This gives us room to improve the regionck reasoning in
// the future without breaking backwards compat.
EscapingProjection(Vec<Component<'tcx>>),
Opaque(DefId, SubstsRef<'tcx>),
}
/// Push onto `out` all the things that must outlive `'a` for the condition
@ -120,6 +123,17 @@ 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::Opaque(def_id, substs) => {
out.push(Component::Opaque(def_id, substs));
},
// 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,
@ -168,7 +182,6 @@ fn compute_components<'tcx>(
ty::Float(..) | // OutlivesScalar
ty::Never | // ...
ty::Adt(..) | // OutlivesNominalType
ty::Opaque(..) | // OutlivesNominalType (ish)
ty::Foreign(..) | // OutlivesNominalType
ty::Str | // OutlivesScalar (ish)
ty::Slice(..) | // ...

View File

@ -142,6 +142,10 @@ impl<'a, 'tcx> OutlivesEnvironmentBuilder<'tcx> {
self.region_bound_pairs
.insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a));
}
OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => {
self.region_bound_pairs
.insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a));
}
OutlivesBound::RegionSubRegion(r_a, r_b) => {
if let (ReEarlyBound(_) | ReFree(_), ReVar(vid_b)) = (r_a.kind(), r_b.kind()) {
infcx

View File

@ -68,10 +68,11 @@ use crate::infer::{
};
use crate::traits::{ObligationCause, ObligationCauseCode};
use rustc_data_structures::undo_log::UndoLogs;
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeVisitable};
use rustc_middle::ty::{self, Region, SubstsRef, Ty, TyCtxt, TypeVisitable};
use smallvec::smallvec;
impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
@ -283,6 +284,9 @@ where
Component::Param(param_ty) => {
self.param_ty_must_outlive(origin, region, *param_ty);
}
Component::Opaque(def_id, substs) => {
self.opaque_must_outlive(*def_id, substs, origin, region)
}
Component::Projection(projection_ty) => {
self.projection_must_outlive(origin, region, *projection_ty);
}
@ -314,10 +318,32 @@ where
);
let generic = GenericKind::Param(param_ty);
let verify_bound = self.verify_bound.generic_bound(generic);
let verify_bound = self.verify_bound.param_bound(param_ty);
self.delegate.push_verify(origin, generic, region, verify_bound);
}
#[instrument(level = "debug", skip(self))]
fn opaque_must_outlive(
&mut self,
def_id: DefId,
substs: SubstsRef<'tcx>,
origin: infer::SubregionOrigin<'tcx>,
region: ty::Region<'tcx>,
) {
self.generic_must_outlive(
origin,
region,
GenericKind::Opaque(def_id, substs),
def_id,
substs,
true,
|ty| match *ty.kind() {
ty::Opaque(def_id, substs) => (def_id, substs),
_ => bug!("expected only projection types from env, not {:?}", ty),
},
);
}
#[instrument(level = "debug", skip(self))]
fn projection_must_outlive(
&mut self,
@ -325,6 +351,36 @@ where
region: ty::Region<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
) {
self.generic_must_outlive(
origin,
region,
GenericKind::Projection(projection_ty),
projection_ty.item_def_id,
projection_ty.substs,
false,
|ty| match ty.kind() {
ty::Projection(projection_ty) => (projection_ty.item_def_id, projection_ty.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>),
) {
// An optimization for a common case with opaque types.
if substs.is_empty() {
return;
}
// This case is thorny for inference. The fundamental problem is
// that there are many cases where we have choice, and inference
// doesn't like choice (the current region inference in
@ -343,16 +399,15 @@ where
// These are guaranteed to apply, no matter the inference
// results.
let trait_bounds: Vec<_> =
self.verify_bound.projection_declared_bounds_from_trait(projection_ty).collect();
self.verify_bound.declared_region_bounds(def_id, substs).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.projection_approx_declared_bounds_from_env(projection_ty);
debug!("projection_must_outlive: approx_env_bounds={:?}", approx_env_bounds);
let mut approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(generic);
debug!(?approx_env_bounds);
// Remove outlives bounds that we get from the environment but
// which are also deducible from the trait. This arises (cc
@ -366,14 +421,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();
match *bound.0.kind() {
ty::Projection(projection_ty) => self
.verify_bound
.projection_declared_bounds_from_trait(projection_ty)
.all(|r| r != bound.1),
_ => panic!("expected only projection types from env, not {:?}", bound.0),
}
let (def_id, substs) = filter(bound.0);
self.verify_bound.declared_region_bounds(def_id, substs).all(|r| r != bound.1)
});
// If declared bounds list is empty, the only applicable rule is
@ -390,29 +439,11 @@ 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 = projection_ty.needs_infer();
if approx_env_bounds.is_empty() && trait_bounds.is_empty() && needs_infer {
debug!("projection_must_outlive: no declared bounds");
let needs_infer = substs.needs_infer();
if approx_env_bounds.is_empty() && trait_bounds.is_empty() && (needs_infer || is_opaque) {
debug!("no declared bounds");
let constraint = origin.to_constraint_category();
for k in projection_ty.substs {
match k.unpack() {
GenericArgKind::Lifetime(lt) => {
self.delegate.push_sub_region_constraint(
origin.clone(),
region,
lt,
constraint,
);
}
GenericArgKind::Type(ty) => {
self.type_must_outlive(origin.clone(), ty, region, constraint);
}
GenericArgKind::Const(_) => {
// Const parameters don't impose constraints.
}
}
}
self.substs_must_outlive(substs, origin, region);
return;
}
@ -442,8 +473,8 @@ where
.all(|b| b == Some(trait_bounds[0]))
{
let unique_bound = trait_bounds[0];
debug!("projection_must_outlive: unique trait bound = {:?}", unique_bound);
debug!("projection_must_outlive: unique declared bound appears in trait ref");
debug!(?unique_bound);
debug!("unique declared bound appears in trait ref");
let category = origin.to_constraint_category();
self.delegate.push_sub_region_constraint(origin, region, unique_bound, category);
return;
@ -454,11 +485,42 @@ 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 generic = GenericKind::Projection(projection_ty);
let verify_bound = self.verify_bound.generic_bound(generic);
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);
}
fn substs_must_outlive(
&mut self,
substs: SubstsRef<'tcx>,
origin: infer::SubregionOrigin<'tcx>,
region: ty::Region<'tcx>,
) {
let constraint = origin.to_constraint_category();
for k in substs {
match k.unpack() {
GenericArgKind::Lifetime(lt) => {
self.delegate.push_sub_region_constraint(
origin.clone(),
region,
lt,
constraint,
);
}
GenericArgKind::Type(ty) => {
self.type_must_outlive(origin.clone(), ty, region, constraint);
}
GenericArgKind::Const(_) => {
// Const parameters don't impose constraints.
}
}
}
}
}
impl<'cx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'tcx> {

View File

@ -2,11 +2,10 @@ use crate::infer::outlives::components::{compute_components_recursive, Component
use crate::infer::outlives::env::RegionBoundPairs;
use crate::infer::region_constraints::VerifyIfEq;
use crate::infer::{GenericKind, VerifyBound};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::sso::SsoHashSet;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::GenericArg;
use rustc_middle::ty::{self, EarlyBinder, OutlivesPredicate, Ty, TyCtxt};
use rustc_middle::ty::{self, EarlyBinder, OutlivesPredicate, SubstsRef, Ty, TyCtxt};
use smallvec::smallvec;
@ -38,20 +37,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
Self { tcx, region_bound_pairs, implicit_region_bound, param_env }
}
/// Returns a "verify bound" that encodes what we know about
/// `generic` and the regions it outlives.
pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> {
let mut visited = SsoHashSet::new();
match generic {
GenericKind::Param(param_ty) => self.param_bound(param_ty),
GenericKind::Projection(projection_ty) => {
self.projection_bound(projection_ty, &mut visited)
}
}
}
#[instrument(level = "debug", skip(self))]
fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
pub fn param_bound(&self, param_ty: ty::ParamTy) -> 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.
@ -105,41 +92,31 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
/// the clause from the environment only applies if `'0 = 'a`,
/// which we don't know yet. But we would still include `'b` in
/// this list.
pub fn projection_approx_declared_bounds_from_env(
pub fn approx_declared_bounds_from_env(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
generic: GenericKind<'tcx>,
) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> {
let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx);
let projection_ty = generic.to_ty(self.tcx);
let erased_projection_ty = self.tcx.erase_regions(projection_ty);
self.declared_generic_bounds_from_env_for_erased_ty(erased_projection_ty)
}
/// Searches the where-clauses in scope for regions that
/// `projection_ty` is known to outlive. Currently requires an
/// exact match.
pub fn projection_declared_bounds_from_trait(
#[instrument(level = "debug", skip(self, visited))]
pub fn projection_opaque_bounds(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'tcx> {
self.declared_projection_bounds_from_trait(projection_ty)
}
pub fn projection_bound(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
generic: GenericKind<'tcx>,
def_id: DefId,
substs: SubstsRef<'tcx>,
visited: &mut SsoHashSet<GenericArg<'tcx>>,
) -> VerifyBound<'tcx> {
debug!("projection_bound(projection_ty={:?})", projection_ty);
let projection_ty_as_ty =
self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
let generic_ty = generic.to_ty(self.tcx);
// Search the env for where clauses like `P: 'a`.
let env_bounds = self
.projection_approx_declared_bounds_from_env(projection_ty)
let projection_opaque_bounds = self
.approx_declared_bounds_from_env(generic)
.into_iter()
.map(|binder| {
if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == projection_ty_as_ty {
if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == generic_ty {
// Micro-optimize if this is an exact match (this
// occurs often when there are no region variables
// involved).
@ -149,21 +126,19 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
VerifyBound::IfEq(verify_if_eq_b)
}
});
// Extend with bounds that we can find from the trait.
let trait_bounds = self
.projection_declared_bounds_from_trait(projection_ty)
.map(|r| VerifyBound::OutlivedBy(r));
let trait_bounds =
self.declared_region_bounds(def_id, substs).map(|r| VerifyBound::OutlivedBy(r));
// see the extensive comment in projection_must_outlive
let recursive_bound = {
let mut components = smallvec![];
let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
compute_components_recursive(self.tcx, ty.into(), &mut components, visited);
compute_components_recursive(self.tcx, generic_ty.into(), &mut components, visited);
self.bound_from_components(&components, visited)
};
VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound)
VerifyBound::AnyBound(projection_opaque_bounds.chain(trait_bounds).collect())
.or(recursive_bound)
}
fn bound_from_components(
@ -195,7 +170,18 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
match *component {
Component::Region(lt) => VerifyBound::OutlivedBy(lt),
Component::Param(param_ty) => self.param_bound(param_ty),
Component::Projection(projection_ty) => self.projection_bound(projection_ty, visited),
Component::Opaque(did, substs) => self.projection_opaque_bounds(
GenericKind::Opaque(did, substs),
did,
substs,
visited,
),
Component::Projection(projection_ty) => self.projection_opaque_bounds(
GenericKind::Projection(projection_ty),
projection_ty.item_def_id,
projection_ty.substs,
visited,
),
Component::EscapingProjection(ref components) => {
self.bound_from_components(components, visited)
}
@ -293,30 +279,6 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
/// }
/// ```
///
/// then this function would return `'x`. This is subject to the
/// limitations around higher-ranked bounds described in
/// `region_bounds_declared_on_associated_item`.
fn declared_projection_bounds_from_trait(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'tcx> {
debug!("projection_bounds(projection_ty={:?})", projection_ty);
let tcx = self.tcx;
self.region_bounds_declared_on_associated_item(projection_ty.item_def_id)
.map(move |r| EarlyBinder(r).subst(tcx, projection_ty.substs))
}
/// Given the `DefId` of an associated item, returns any region
/// bounds attached to that associated item from the trait definition.
///
/// For example:
///
/// ```rust
/// trait Foo<'a> {
/// type Bar: 'a;
/// }
/// ```
///
/// If we were given the `DefId` of `Foo::Bar`, we would return
/// `'a`. You could then apply the substitutions from the
/// projection to convert this into your namespace. This also
@ -336,17 +298,20 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
///
/// This is for simplicity, and because we are not really smart
/// enough to cope with such bounds anywhere.
fn region_bounds_declared_on_associated_item(
pub fn declared_region_bounds(
&self,
assoc_item_def_id: DefId,
def_id: DefId,
substs: SubstsRef<'tcx>,
) -> impl Iterator<Item = ty::Region<'tcx>> {
let tcx = self.tcx;
let bounds = tcx.item_bounds(assoc_item_def_id);
let bounds = tcx.item_bounds(def_id);
trace!("{:#?}", bounds);
bounds
.into_iter()
.filter_map(|p| p.to_opt_type_outlives())
.filter_map(|p| p.no_bound_vars())
.map(|b| b.1)
.map(move |r| EarlyBinder(r).subst(tcx, substs))
}
/// Searches through a predicate list for a predicate `T: 'a`.

View File

@ -12,8 +12,10 @@ use rustc_data_structures::intern::Interned;
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::undo_log::UndoLogs;
use rustc_data_structures::unify as ut;
use rustc_hir::def_id::DefId;
use rustc_index::vec::IndexVec;
use rustc_middle::infer::unify_key::{RegionVidKey, UnifiedRegion};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::ReStatic;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{ReLateBound, ReVar};
@ -168,6 +170,7 @@ pub struct Verify<'tcx> {
pub enum GenericKind<'tcx> {
Param(ty::ParamTy),
Projection(ty::ProjectionTy<'tcx>),
Opaque(DefId, SubstsRef<'tcx>),
}
/// Describes the things that some `GenericKind` value `G` is known to
@ -747,6 +750,9 @@ impl<'tcx> fmt::Debug for GenericKind<'tcx> {
match *self {
GenericKind::Param(ref p) => write!(f, "{:?}", p),
GenericKind::Projection(ref p) => write!(f, "{:?}", p),
GenericKind::Opaque(def_id, substs) => ty::tls::with(|tcx| {
write!(f, "{}", tcx.def_path_str_with_substs(def_id, tcx.lift(substs).unwrap()))
}),
}
}
}
@ -756,6 +762,9 @@ impl<'tcx> fmt::Display for GenericKind<'tcx> {
match *self {
GenericKind::Param(ref p) => write!(f, "{}", p),
GenericKind::Projection(ref p) => write!(f, "{}", p),
GenericKind::Opaque(def_id, substs) => ty::tls::with(|tcx| {
write!(f, "{}", tcx.def_path_str_with_substs(def_id, tcx.lift(substs).unwrap()))
}),
}
}
}
@ -765,6 +774,7 @@ impl<'tcx> GenericKind<'tcx> {
match *self {
GenericKind::Param(ref p) => p.to_ty(tcx),
GenericKind::Projection(ref p) => tcx.mk_projection(p.item_def_id, p.substs),
GenericKind::Opaque(def_id, substs) => tcx.mk_opaque(def_id, substs),
}
}
}

View File

@ -246,6 +246,13 @@ impl<'tcx> Elaborator<'tcx> {
Component::UnresolvedInferenceVariable(_) => None,
Component::Opaque(def_id, substs) => {
let ty = tcx.mk_opaque(def_id, substs);
Some(ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(
ty, r_min,
)))
}
Component::Projection(projection) => {
// We might end up here if we have `Foo<<Bar as Baz>::Assoc>: 'a`.
// With this, we can deduce that `<Bar as Baz>::Assoc: 'a`.
@ -262,8 +269,9 @@ impl<'tcx> Elaborator<'tcx> {
None
}
})
.map(ty::Binder::dummy)
.map(|predicate_kind| predicate_kind.to_predicate(tcx))
.map(|predicate_kind| {
bound_predicate.rebind(predicate_kind).to_predicate(tcx)
})
.filter(|&predicate| visited.insert(predicate))
.map(|predicate| {
predicate_obligation(

View File

@ -8,8 +8,9 @@
use crate::error::DropCheckOverflow;
use crate::infer::canonical::{Canonical, QueryResponse};
use crate::ty::error::TypeError;
use crate::ty::subst::GenericArg;
use crate::ty::subst::{GenericArg, SubstsRef};
use crate::ty::{self, Ty, TyCtxt};
use rustc_hir::def_id::DefId;
use rustc_span::source_map::Span;
use std::iter::FromIterator;
@ -219,4 +220,5 @@ pub enum OutlivesBound<'tcx> {
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>),
RegionSubOpaque(ty::Region<'tcx>, DefId, SubstsRef<'tcx>),
}

View File

@ -156,6 +156,9 @@ fn implied_bounds_from_components<'tcx>(
Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)),
Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)),
Component::Projection(p) => Some(OutlivesBound::RegionSubProjection(sub_region, p)),
Component::Opaque(def_id, substs) => {
Some(OutlivesBound::RegionSubOpaque(sub_region, def_id, substs))
}
Component::EscapingProjection(_) =>
// If the projection has escaping regions, don't
// try to infer any implied bounds even for its

View File

@ -265,6 +265,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment)
}
#[instrument(level = "debug", skip(self, previous))]
fn cat_expr_adjusted_with<F>(
&self,
expr: &hir::Expr<'_>,
@ -274,7 +275,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
where
F: FnOnce() -> McResult<PlaceWithHirId<'tcx>>,
{
debug!("cat_expr_adjusted_with({:?}): {:?}", adjustment, expr);
let target = self.resolve_vars_if_possible(adjustment.target);
match adjustment.kind {
adjustment::Adjust::Deref(overloaded) => {
@ -299,6 +299,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
}
}
#[instrument(level = "debug", skip(self))]
pub(crate) fn cat_expr_unadjusted(
&self,
expr: &hir::Expr<'_>,
@ -387,6 +388,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
}
}
#[instrument(level = "debug", skip(self, span))]
pub(crate) fn cat_res(
&self,
hir_id: hir::HirId,
@ -394,8 +396,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
expr_ty: Ty<'tcx>,
res: Res,
) -> McResult<PlaceWithHirId<'tcx>> {
debug!("cat_res: id={:?} expr={:?} def={:?}", hir_id, expr_ty, res);
match res {
Res::Def(
DefKind::Ctor(..)
@ -475,13 +475,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
ret
}
#[instrument(level = "debug", skip(self))]
fn cat_overloaded_place(
&self,
expr: &hir::Expr<'_>,
base: &hir::Expr<'_>,
) -> McResult<PlaceWithHirId<'tcx>> {
debug!("cat_overloaded_place(expr={:?}, base={:?})", expr, base);
// Reconstruct the output assuming it's a reference with the
// same region and mutability as the receiver. This holds for
// `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
@ -497,13 +496,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
self.cat_deref(expr, base)
}
#[instrument(level = "debug", skip(self, node))]
fn cat_deref(
&self,
node: &impl HirNode,
base_place: PlaceWithHirId<'tcx>,
) -> McResult<PlaceWithHirId<'tcx>> {
debug!("cat_deref: base_place={:?}", base_place);
let base_curr_ty = base_place.place.ty();
let deref_ty = match base_curr_ty.builtin_deref(true) {
Some(mt) => mt.ty,

View File

@ -96,6 +96,23 @@ pub(crate) fn insert_outlives_predicate<'tcx>(
.or_insert(span);
}
Component::Opaque(def_id, substs) => {
// This would arise from something like:
//
// ```rust
// type Opaque<T> = impl Sized;
// fn defining<T>() -> Opaque<T> {}
// struct Ss<'a, T>(&'a Opaque<T>);
// ```
//
// Here we want to have an implied bound `Opaque<T>: 'a`
let ty = tcx.mk_opaque(def_id, substs);
required_predicates
.entry(ty::OutlivesPredicate(ty.into(), outlived_region))
.or_insert(span);
}
Component::EscapingProjection(_) => {
// As above, but the projection involves
// late-bound regions. Therefore, the WF

View File

@ -1,23 +0,0 @@
error[E0477]: the type `<() as Yay<&'a ()>>::InnerStream<'s>` does not fulfill the required lifetime
--> $DIR/issue-86218.rs:22:28
|
LL | type InnerStream<'s> = impl Stream<Item = i32> + 's;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: type must outlive the lifetime `'s` as defined here as required by this binding
--> $DIR/issue-86218.rs:22:22
|
LL | type InnerStream<'s> = impl Stream<Item = i32> + 's;
| ^^
error: unconstrained opaque type
--> $DIR/issue-86218.rs:22:28
|
LL | type InnerStream<'s> = impl Stream<Item = i32> + 's;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `InnerStream` must be used in combination with a concrete type within the same module
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0477`.

View File

@ -0,0 +1,23 @@
// check-pass
#![feature(type_alias_impl_trait)]
pub trait Stream {
type Item;
}
impl Stream for () {
type Item = i32;
}
trait Yay<AdditionalValue> {
type InnerStream<'s>: Stream<Item = i32> + 's;
fn foo<'s>() -> Self::InnerStream<'s>;
}
impl<T> Yay<T> for () {
type InnerStream<'s> = impl Stream<Item = i32> + 's;
fn foo<'s>() -> Self::InnerStream<'s> { () }
}
fn main() {}

View File

@ -1,7 +1,4 @@
// check-fail
// known-bug: #86218
// This should pass, but seems to run into a TAIT issue.
// check-pass
#![feature(type_alias_impl_trait)]
@ -20,7 +17,8 @@ trait Yay<AdditionalValue> {
impl<'a> Yay<&'a ()> for () {
type InnerStream<'s> = impl Stream<Item = i32> + 's;
fn foo<'s>() -> Self::InnerStream<'s> { todo!() }
//^ ERROR does not fulfill the required lifetime
fn foo<'s>() -> Self::InnerStream<'s> { () }
}
fn main() {}

View File

@ -0,0 +1,25 @@
// run-rustfix
pub trait Trait {}
pub struct Foo;
impl Trait for Foo {}
fn foo<'x, P>(
_post: P,
x: &'x Foo,
) -> &'x impl Trait {
x
}
pub fn bar<'t, T: 't>(
//~^ HELP: consider adding an explicit lifetime bound...
post: T,
x: &'t Foo,
) -> &'t impl Trait {
foo(post, x)
//~^ ERROR: the parameter type `T` may not live long enough
}
fn main() {}

View File

@ -0,0 +1,25 @@
// run-rustfix
pub trait Trait {}
pub struct Foo;
impl Trait for Foo {}
fn foo<'x, P>(
_post: P,
x: &'x Foo,
) -> &'x impl Trait {
x
}
pub fn bar<'t, T>(
//~^ HELP: consider adding an explicit lifetime bound...
post: T,
x: &'t Foo,
) -> &'t impl Trait {
foo(post, x)
//~^ ERROR: the parameter type `T` may not live long enough
}
fn main() {}

View File

@ -0,0 +1,14 @@
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/unactionable_diagnostic.rs:21:5
|
LL | foo(post, x)
| ^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound...
|
LL | pub fn bar<'t, T: 't>(
| ++++
error: aborting due to previous error
For more information about this error, try `rustc --explain E0309`.

View File

@ -0,0 +1,51 @@
#![feature(type_alias_impl_trait)]
type WithLifetime<'a> = impl Equals<SelfType = ()>;
fn _defining_use<'a>() -> WithLifetime<'a> {}
trait Convert<'a> {
type Witness;
fn convert<'b, T: ?Sized>(_proof: &'b Self::Witness, x: &'a T) -> &'b T;
}
impl<'a> Convert<'a> for () {
type Witness = WithLifetime<'a>;
fn convert<'b, T: ?Sized>(_proof: &'b WithLifetime<'a>, x: &'a T) -> &'b T {
// compiler used to think it gets to assume 'a: 'b here because
// of the `&'b WithLifetime<'a>` argument
x
//~^ ERROR lifetime may not live long enough
}
}
fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
WithLifetime::<'a>::convert_helper::<(), T>(&(), x)
}
trait Equals {
type SelfType;
fn convert_helper<'a, 'b, W: Convert<'a, Witness = Self>, T: ?Sized>(
proof: &'b Self::SelfType,
x: &'a T,
) -> &'b T;
}
impl<S> Equals for S {
type SelfType = Self;
fn convert_helper<'a, 'b, W: Convert<'a, Witness = Self>, T: ?Sized>(
proof: &'b Self,
x: &'a T,
) -> &'b T {
W::convert(proof, x)
}
}
fn main() {
let r;
{
let x = String::from("Hello World?");
r = extend_lifetime(&x);
}
println!("{}", r);
}

View File

@ -0,0 +1,16 @@
error: lifetime may not live long enough
--> $DIR/implied_bounds.rs:17:9
|
LL | impl<'a> Convert<'a> for () {
| -- lifetime `'a` defined here
...
LL | fn convert<'b, T: ?Sized>(_proof: &'b WithLifetime<'a>, x: &'a T) -> &'b T {
| -- lifetime `'b` defined here
...
LL | x
| ^ associated function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`
error: aborting due to previous error

View File

@ -0,0 +1,10 @@
// check-pass
#![feature(type_alias_impl_trait)]
type Ty<'a, A> = impl Sized + 'a;
fn defining<'a, A>() -> Ty<'a, A> {}
fn assert_static<T: 'static>() {}
fn test<'a, A>() where Ty<'a, A>: 'static, { assert_static::<Ty<'a, A>>() }
fn main() {}

View File

@ -0,0 +1,18 @@
// check-pass
fn foo<F>(_: F)
where
F: 'static,
{
}
fn from<F: Send>(f: F) -> impl Send {
f
}
fn bar<T>() {
foo(from(|| ()))
}
fn main() {
}

View File

@ -0,0 +1,31 @@
trait StaticDefaultRef: 'static {
fn default_ref() -> &'static Self;
}
impl StaticDefaultRef for str {
fn default_ref() -> &'static str {
""
}
}
fn into_impl(x: &str) -> &(impl ?Sized + AsRef<str> + StaticDefaultRef + '_) {
x
}
fn extend_lifetime<'a>(x: &'a str) -> &'static str {
let t = into_impl(x);
helper(|_| t) //~ ERROR lifetime may not live long enough
}
fn helper<T: ?Sized + AsRef<str> + StaticDefaultRef>(f: impl FnOnce(&T) -> &T) -> &'static str {
f(T::default_ref()).as_ref()
}
fn main() {
let r;
{
let x = String::from("Hello World?");
r = extend_lifetime(&x);
}
println!("{}", r);
}

View File

@ -0,0 +1,11 @@
error: lifetime may not live long enough
--> $DIR/implied_bounds_closure.rs:17:16
|
LL | fn extend_lifetime<'a>(x: &'a str) -> &'static str {
| -- lifetime `'a` defined here
LL | let t = into_impl(x);
LL | helper(|_| t)
| ^ returning this value requires that `'a` must outlive `'static`
error: aborting due to previous error

View File

@ -0,0 +1,51 @@
#![feature(type_alias_impl_trait)]
type WithLifetime<T> = impl Equals<SelfType = ()>;
fn _defining_use<T>() -> WithLifetime<T> {}
trait Convert<'a> {
type Witness;
fn convert<'b, T: ?Sized>(_proof: &'b Self::Witness, x: &'a T) -> &'b T;
}
impl<'a> Convert<'a> for () {
type Witness = WithLifetime<&'a ()>;
fn convert<'b, T: ?Sized>(_proof: &'b WithLifetime<&'a ()>, x: &'a T) -> &'b T {
// compiler used to think it gets to assume 'a: 'b here because
// of the `&'b WithLifetime<&'a ()>` argument
x
//~^ ERROR lifetime may not live long enough
}
}
fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
WithLifetime::<&'a ()>::convert_helper::<(), T>(&(), x)
}
trait Equals {
type SelfType;
fn convert_helper<'a, 'b, W: Convert<'a, Witness = Self>, T: ?Sized>(
proof: &'b Self::SelfType,
x: &'a T,
) -> &'b T;
}
impl<S> Equals for S {
type SelfType = Self;
fn convert_helper<'a, 'b, W: Convert<'a, Witness = Self>, T: ?Sized>(
proof: &'b Self,
x: &'a T,
) -> &'b T {
W::convert(proof, x)
}
}
fn main() {
let r;
{
let x = String::from("Hello World?");
r = extend_lifetime(&x);
}
println!("{}", r);
}

View File

@ -0,0 +1,16 @@
error: lifetime may not live long enough
--> $DIR/implied_bounds_from_types.rs:17:9
|
LL | impl<'a> Convert<'a> for () {
| -- lifetime `'a` defined here
...
LL | fn convert<'b, T: ?Sized>(_proof: &'b WithLifetime<&'a ()>, x: &'a T) -> &'b T {
| -- lifetime `'b` defined here
...
LL | x
| ^ associated function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`
error: aborting due to previous error

View File

@ -0,0 +1,27 @@
#![feature(type_alias_impl_trait)]
// known-bug: #99840
// this should not compile
// check-pass
type Alias = impl Sized;
fn constrain() -> Alias {
1i32
}
trait HideIt {
type Assoc;
}
impl HideIt for () {
type Assoc = Alias;
}
pub trait Yay {}
impl Yay for <() as HideIt>::Assoc {}
// impl Yay for i32 {} // this already errors
// impl Yay for u32 {} // this also already errors
fn main() {}

View File

@ -0,0 +1,43 @@
#![feature(type_alias_impl_trait)]
mod test_lifetime_param {
type Ty<'a> = impl Sized;
fn defining(a: &str) -> Ty<'_> { a }
fn assert_static<'a: 'static>() {}
//~^ WARN: unnecessary lifetime parameter `'a`
fn test<'a>() where Ty<'a>: 'static { assert_static::<'a>() }
//~^ ERROR: lifetime may not live long enough
}
mod test_higher_kinded_lifetime_param {
type Ty<'a> = impl Sized;
fn defining(a: &str) -> Ty<'_> { a }
fn assert_static<'a: 'static>() {}
//~^ WARN: unnecessary lifetime parameter `'a`
fn test<'a>() where for<'b> Ty<'b>: 'a { assert_static::<'a>() }
//~^ ERROR: lifetime may not live long enough
}
mod test_higher_kinded_lifetime_param2 {
fn assert_static<'a: 'static>() {}
//~^ WARN: unnecessary lifetime parameter `'a`
fn test<'a>() { assert_static::<'a>() }
//~^ ERROR: lifetime may not live long enough
}
mod test_type_param {
type Ty<A> = impl Sized;
fn defining<A>(s: A) -> Ty<A> { s }
fn assert_static<A: 'static>() {}
fn test<A>() where Ty<A>: 'static { assert_static::<A>() }
//~^ ERROR: parameter type `A` may not live long enough
}
mod test_implied_from_fn_sig {
type Opaque<T: 'static> = impl Sized;
fn defining<T: 'static>() -> Opaque<T> {}
fn assert_static<T: 'static>() {}
fn test<T>(_: Opaque<T>) { assert_static::<T>(); }
}
fn main() {}

View File

@ -0,0 +1,58 @@
warning: unnecessary lifetime parameter `'a`
--> $DIR/implied_lifetime_wf_check3.rs:6:22
|
LL | fn assert_static<'a: 'static>() {}
| ^^
|
= help: you can use the `'static` lifetime directly, in place of `'a`
warning: unnecessary lifetime parameter `'a`
--> $DIR/implied_lifetime_wf_check3.rs:15:22
|
LL | fn assert_static<'a: 'static>() {}
| ^^
|
= help: you can use the `'static` lifetime directly, in place of `'a`
warning: unnecessary lifetime parameter `'a`
--> $DIR/implied_lifetime_wf_check3.rs:22:22
|
LL | fn assert_static<'a: 'static>() {}
| ^^
|
= help: you can use the `'static` lifetime directly, in place of `'a`
error: lifetime may not live long enough
--> $DIR/implied_lifetime_wf_check3.rs:8:43
|
LL | fn test<'a>() where Ty<'a>: 'static { assert_static::<'a>() }
| -- lifetime `'a` defined here ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
error: lifetime may not live long enough
--> $DIR/implied_lifetime_wf_check3.rs:17:46
|
LL | fn test<'a>() where for<'b> Ty<'b>: 'a { assert_static::<'a>() }
| -- lifetime `'a` defined here ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
error: lifetime may not live long enough
--> $DIR/implied_lifetime_wf_check3.rs:24:21
|
LL | fn test<'a>() { assert_static::<'a>() }
| -- ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
| |
| lifetime `'a` defined here
error[E0310]: the parameter type `A` may not live long enough
--> $DIR/implied_lifetime_wf_check3.rs:32:41
|
LL | fn test<A>() where Ty<A>: 'static { assert_static::<A>() }
| ^^^^^^^^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound...
|
LL | fn test<A: 'static>() where Ty<A>: 'static { assert_static::<A>() }
| +++++++++
error: aborting due to 4 previous errors; 3 warnings emitted
For more information about this error, try `rustc --explain E0310`.

View File

@ -0,0 +1,11 @@
#![feature(type_alias_impl_trait)]
mod test_type_param_static {
type Ty<A> = impl Sized + 'static;
//~^ ERROR: the parameter type `A` may not live long enough
fn defining<A: 'static>(s: A) -> Ty<A> { s }
fn assert_static<A: 'static>() {}
fn test<A>() where Ty<A>: 'static { assert_static::<A>() }
}
fn main() {}

View File

@ -0,0 +1,14 @@
error[E0310]: the parameter type `A` may not live long enough
--> $DIR/implied_lifetime_wf_check4_static.rs:4:18
|
LL | type Ty<A> = impl Sized + 'static;
| ^^^^^^^^^^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound...
|
LL | type Ty<A: 'static> = impl Sized + 'static;
| +++++++++
error: aborting due to previous error
For more information about this error, try `rustc --explain E0310`.

View File

@ -16,7 +16,7 @@ fn rand_generator<'a>(rng: &'a ()) -> RandGenerator<'a> {
}
}
pub type RandGeneratorWithIndirection<'a> = impl Generator<Return = (), Yield = u64> + 'a;
pub type RandGeneratorWithIndirection<'c> = impl Generator<Return = (), Yield = u64> + 'c;
pub fn rand_generator_with_indirection<'a>(rng: &'a ()) -> RandGeneratorWithIndirection<'a> {
fn helper<'b>(rng: &'b ()) -> impl 'b + Generator<Return = (), Yield = u64> {
move || {

View File

@ -0,0 +1,20 @@
// check-pass
#![feature(generators, generator_trait)]
#![feature(type_alias_impl_trait)]
trait Trait {}
impl<T> Trait for T {}
type Foo<'c> = impl Trait + 'c;
fn foo<'a>(rng: &'a ()) -> Foo<'a> {
fn helper<'b>(rng: &'b ()) -> impl 'b + Trait {
rng
}
helper(rng)
}
fn main() {
}

View File

@ -0,0 +1,14 @@
// check-pass
#![feature(type_alias_impl_trait)]
type Opaque<T> = impl Sized;
fn defining<T>() -> Opaque<T> {}
struct Ss<'a, T>(&'a Opaque<T>);
fn test<'a, T>(_: Ss<'a, T>) {
// test that we have an implied bound `Opaque<T>: 'a` from fn signature
None::<&'a Opaque<T>>;
}
fn main() {}