Neither require nor imply lifetime bounds on opaque type for well formedness
This commit is contained in:
parent
c8ecf09a25
commit
37928f5986
30 changed files with 497 additions and 42 deletions
|
@ -2374,6 +2374,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 {
|
||||
|
|
|
@ -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(..) | // ...
|
||||
|
|
|
@ -140,6 +140,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
|
||||
|
|
|
@ -284,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);
|
||||
}
|
||||
|
@ -319,6 +322,27 @@ where
|
|||
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,
|
||||
|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,
|
||||
|
|
|
@ -47,6 +47,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
GenericKind::Projection(projection_ty) => {
|
||||
self.projection_bound(projection_ty, &mut visited)
|
||||
}
|
||||
GenericKind::Opaque(def_id, substs) => self.opaque_bound(def_id, substs),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,6 +156,20 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound)
|
||||
}
|
||||
|
||||
fn opaque_bound(&self, def_id: DefId, substs: SubstsRef<'tcx>) -> VerifyBound<'tcx> {
|
||||
let bounds: Vec<_> =
|
||||
self.bounds(def_id, substs).map(|r| VerifyBound::OutlivedBy(r)).collect();
|
||||
trace!("{:#?}", bounds);
|
||||
if bounds.is_empty() {
|
||||
// No bounds means the value must not have any lifetimes.
|
||||
// FIXME: should we implicitly add 'static to `tcx.item_bounds` for opaque types, just
|
||||
// like we add `Sized`?
|
||||
VerifyBound::OutlivedBy(self.tcx.lifetimes.re_static)
|
||||
} else {
|
||||
VerifyBound::AnyBound(bounds)
|
||||
}
|
||||
}
|
||||
|
||||
fn bound_from_components(
|
||||
&self,
|
||||
components: &[Component<'tcx>],
|
||||
|
@ -184,6 +199,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
|||
match *component {
|
||||
Component::Region(lt) => VerifyBound::OutlivedBy(lt),
|
||||
Component::Param(param_ty) => self.param_bound(param_ty),
|
||||
Component::Opaque(did, substs) => self.opaque_bound(did, substs),
|
||||
Component::Projection(projection_ty) => self.projection_bound(projection_ty, visited),
|
||||
Component::EscapingProjection(ref components) => {
|
||||
self.bound_from_components(components, visited)
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue