1
Fork 0

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

This commit is contained in:
Oli Scherer 2022-05-18 16:01:10 +00:00
parent c8ecf09a25
commit 37928f5986
30 changed files with 497 additions and 42 deletions

View file

@ -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 {

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

@ -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

View file

@ -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,

View file

@ -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)

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(