Rollup merge of #118000 - compiler-errors:placeholder-ty-outlives, r=aliemjay
Make regionck care about placeholders in outlives components Currently, we don't consider a placeholder type `!T` to be a type component when it comes to processing type-outlives obligations. This means that they are essentially treated like unit values with no sub-components, and always outlive any region. This is problematic for `non_lifetime_binders`, and even more problematic for `with_negative_coherence`, since negative coherence uses placeholders as universals. This PR adds `Component::Placeholder` which acts much like `Component::Param`. This currently causes a regression in some non-lifetime-binders tests because `for<T> T: 'static` doesn't imply itself when processing outlives obligations, so code like this will fail: ``` fn foo() where for<T> T: 'static { foo() //~ fails } ``` Since the where clause doesn't imply itself. This requires making the `MatchAgainstHigherRankedOutlives` relation smarter when it comes to binders. r? types
This commit is contained in:
commit
6388c0ef04
@ -80,6 +80,10 @@ pub(crate) fn insert_outlives_predicate<'tcx>(
|
||||
.or_insert(span);
|
||||
}
|
||||
|
||||
Component::Placeholder(_) => {
|
||||
span_bug!(span, "Should not deduce placeholder outlives component");
|
||||
}
|
||||
|
||||
Component::Alias(alias_ty) => {
|
||||
// This would either arise from something like:
|
||||
//
|
||||
|
@ -2351,6 +2351,7 @@ pub fn construct_generic_bound_failure(
|
||||
|
||||
let labeled_user_string = match bound_kind {
|
||||
GenericKind::Param(ref p) => format!("the parameter type `{p}`"),
|
||||
GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"),
|
||||
GenericKind::Alias(ref p) => match p.kind(self.tcx) {
|
||||
ty::AliasKind::Projection | ty::AliasKind::Inherent => {
|
||||
format!("the associated type `{p}`")
|
||||
|
@ -11,6 +11,7 @@
|
||||
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
|
||||
@ -120,6 +121,10 @@ fn compute_components<'tcx>(
|
||||
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,
|
||||
@ -176,7 +181,6 @@ fn compute_components<'tcx>(
|
||||
ty::Tuple(..) | // ...
|
||||
ty::FnPtr(_) | // OutlivesFunction (*)
|
||||
ty::Dynamic(..) | // OutlivesObject, OutlivesFragment (*)
|
||||
ty::Placeholder(..) |
|
||||
ty::Bound(..) |
|
||||
ty::Error(_) => {
|
||||
// (*) Function pointers and trait objects are both binders.
|
||||
|
@ -243,6 +243,9 @@ fn components_must_outlive(
|
||||
Component::Param(param_ty) => {
|
||||
self.param_ty_must_outlive(origin, region, *param_ty);
|
||||
}
|
||||
Component::Placeholder(placeholder_ty) => {
|
||||
self.placeholder_ty_must_outlive(origin, region, *placeholder_ty);
|
||||
}
|
||||
Component::Alias(alias_ty) => self.alias_ty_must_outlive(origin, region, *alias_ty),
|
||||
Component::EscapingAlias(subcomponents) => {
|
||||
self.components_must_outlive(origin, &subcomponents, region, category);
|
||||
@ -267,10 +270,28 @@ fn param_ty_must_outlive(
|
||||
region: ty::Region<'tcx>,
|
||||
param_ty: ty::ParamTy,
|
||||
) {
|
||||
let verify_bound = self.verify_bound.param_bound(param_ty);
|
||||
let verify_bound = self.verify_bound.param_or_placeholder_bound(param_ty.to_ty(self.tcx));
|
||||
self.delegate.push_verify(origin, GenericKind::Param(param_ty), region, verify_bound);
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn placeholder_ty_must_outlive(
|
||||
&mut self,
|
||||
origin: infer::SubregionOrigin<'tcx>,
|
||||
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));
|
||||
self.delegate.push_verify(
|
||||
origin,
|
||||
GenericKind::Placeholder(placeholder_ty),
|
||||
region,
|
||||
verify_bound,
|
||||
);
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn alias_ty_must_outlive(
|
||||
&mut self,
|
||||
|
@ -37,11 +37,11 @@ pub fn new(
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
|
||||
pub fn param_or_placeholder_bound(&self, ty: Ty<'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.
|
||||
let declared_bounds_from_env = self.declared_generic_bounds_from_env(param_ty);
|
||||
let declared_bounds_from_env = self.declared_generic_bounds_from_env(ty);
|
||||
debug!(?declared_bounds_from_env);
|
||||
let mut param_bounds = vec![];
|
||||
for declared_bound in declared_bounds_from_env {
|
||||
@ -51,7 +51,7 @@ pub fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
|
||||
param_bounds.push(VerifyBound::OutlivedBy(region));
|
||||
} else {
|
||||
// This is `for<'a> T: 'a`. This means that `T` outlives everything! All done here.
|
||||
debug!("found that {param_ty:?} outlives any lifetime, returning empty vector");
|
||||
debug!("found that {ty:?} outlives any lifetime, returning empty vector");
|
||||
return VerifyBound::AllBounds(vec![]);
|
||||
}
|
||||
}
|
||||
@ -168,7 +168,10 @@ fn bound_from_single_component(
|
||||
) -> VerifyBound<'tcx> {
|
||||
match *component {
|
||||
Component::Region(lt) => VerifyBound::OutlivedBy(lt),
|
||||
Component::Param(param_ty) => self.param_bound(param_ty),
|
||||
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::Alias(alias_ty) => self.alias_bound(alias_ty, visited),
|
||||
Component::EscapingAlias(ref components) => {
|
||||
self.bound_from_components(components, visited)
|
||||
@ -195,9 +198,9 @@ fn bound_from_single_component(
|
||||
/// bounds, but all the bounds it returns can be relied upon.
|
||||
fn declared_generic_bounds_from_env(
|
||||
&self,
|
||||
param_ty: ty::ParamTy,
|
||||
generic_ty: Ty<'tcx>,
|
||||
) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> {
|
||||
let generic_ty = param_ty.to_ty(self.tcx);
|
||||
assert!(matches!(generic_ty.kind(), ty::Param(_) | ty::Placeholder(_)));
|
||||
self.declared_generic_bounds_from_env_for_erased_ty(generic_ty)
|
||||
}
|
||||
|
||||
|
@ -147,6 +147,7 @@ pub struct Verify<'tcx> {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)]
|
||||
pub enum GenericKind<'tcx> {
|
||||
Param(ty::ParamTy),
|
||||
Placeholder(ty::PlaceholderType),
|
||||
Alias(ty::AliasTy<'tcx>),
|
||||
}
|
||||
|
||||
@ -707,6 +708,7 @@ impl<'tcx> fmt::Debug for GenericKind<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
GenericKind::Param(ref p) => write!(f, "{p:?}"),
|
||||
GenericKind::Placeholder(ref p) => write!(f, "{p:?}"),
|
||||
GenericKind::Alias(ref p) => write!(f, "{p:?}"),
|
||||
}
|
||||
}
|
||||
@ -716,6 +718,7 @@ impl<'tcx> fmt::Display for GenericKind<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
GenericKind::Param(ref p) => write!(f, "{p}"),
|
||||
GenericKind::Placeholder(ref p) => write!(f, "{p:?}"),
|
||||
GenericKind::Alias(ref p) => write!(f, "{p}"),
|
||||
}
|
||||
}
|
||||
@ -725,6 +728,7 @@ impl<'tcx> GenericKind<'tcx> {
|
||||
pub fn to_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
|
||||
match *self {
|
||||
GenericKind::Param(ref p) => p.to_ty(tcx),
|
||||
GenericKind::Placeholder(ref p) => Ty::new_placeholder(tcx, *p),
|
||||
GenericKind::Alias(ref p) => p.to_ty(tcx),
|
||||
}
|
||||
}
|
||||
|
@ -365,6 +365,11 @@ fn elaborate(&mut self, elaboratable: &O) {
|
||||
Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min)))
|
||||
}
|
||||
|
||||
Component::Placeholder(p) => {
|
||||
let ty = Ty::new_placeholder(tcx, p);
|
||||
Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min)))
|
||||
}
|
||||
|
||||
Component::UnresolvedInferenceVariable(_) => None,
|
||||
|
||||
Component::Alias(alias_ty) => {
|
||||
|
@ -398,7 +398,8 @@ fn impl_intersection_has_negative_obligation(
|
||||
debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
|
||||
|
||||
let ref infcx = tcx.infer_ctxt().intercrate(true).with_next_trait_solver(true).build();
|
||||
let universe = infcx.universe();
|
||||
let root_universe = infcx.universe();
|
||||
assert_eq!(root_universe, ty::UniverseIndex::ROOT);
|
||||
|
||||
let impl1_header = fresh_impl_header(infcx, impl1_def_id);
|
||||
let param_env =
|
||||
@ -414,7 +415,12 @@ fn impl_intersection_has_negative_obligation(
|
||||
return false;
|
||||
};
|
||||
|
||||
plug_infer_with_placeholders(infcx, universe, (impl1_header.impl_args, impl2_header.impl_args));
|
||||
plug_infer_with_placeholders(
|
||||
infcx,
|
||||
root_universe,
|
||||
(impl1_header.impl_args, impl2_header.impl_args),
|
||||
);
|
||||
let param_env = infcx.resolve_vars_if_possible(param_env);
|
||||
|
||||
// FIXME(with_negative_coherence): the infcx has constraints from equating
|
||||
// the impl headers. We should use these constraints as assumptions, not as
|
||||
|
@ -208,6 +208,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::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)),
|
||||
Component::Placeholder(_) => {
|
||||
unimplemented!("Shouldn't expect a placeholder type in implied bounds (yet)")
|
||||
}
|
||||
Component::EscapingAlias(_) =>
|
||||
// If the projection has escaping regions, don't
|
||||
// try to infer any implied bounds even for its
|
||||
|
@ -0,0 +1,14 @@
|
||||
#![feature(negative_impls)]
|
||||
#![feature(with_negative_coherence)]
|
||||
|
||||
struct Wrap<T>(T);
|
||||
|
||||
trait Foo {}
|
||||
impl<T: 'static> !Foo for Box<T> {}
|
||||
|
||||
trait Bar {}
|
||||
impl<T> Bar for T where T: Foo {}
|
||||
impl<T> Bar for Box<T> {}
|
||||
//~^ ERROR conflicting implementations of trait `Bar` for type `Box<_>`
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,11 @@
|
||||
error[E0119]: conflicting implementations of trait `Bar` for type `Box<_>`
|
||||
--> $DIR/negative-coherence-check-placeholder-outlives.rs:11:1
|
||||
|
|
||||
LL | impl<T> Bar for T where T: Foo {}
|
||||
| ------------------------------ first implementation here
|
||||
LL | impl<T> Bar for Box<T> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0119`.
|
@ -0,0 +1,26 @@
|
||||
warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/placeholders-dont-outlive-static.rs:6:12
|
||||
|
|
||||
LL | #![feature(non_lifetime_binders)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
error[E0310]: the placeholder type `!1_"T"` may not live long enough
|
||||
--> $DIR/placeholders-dont-outlive-static.rs:13:5
|
||||
|
|
||||
LL | foo();
|
||||
| ^^^^^
|
||||
| |
|
||||
| the placeholder type `!1_"T"` must be valid for the static lifetime...
|
||||
| ...so that the type `T` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound
|
||||
|
|
||||
LL | fn bad() where !1_"T": 'static {
|
||||
| +++++++++++++++++++++
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0310`.
|
@ -0,0 +1,26 @@
|
||||
warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/placeholders-dont-outlive-static.rs:6:12
|
||||
|
|
||||
LL | #![feature(non_lifetime_binders)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
error[E0310]: the placeholder type `!1_"T"` may not live long enough
|
||||
--> $DIR/placeholders-dont-outlive-static.rs:19:5
|
||||
|
|
||||
LL | foo();
|
||||
| ^^^^^
|
||||
| |
|
||||
| the placeholder type `!1_"T"` must be valid for the static lifetime...
|
||||
| ...so that the type `T` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound
|
||||
|
|
||||
LL | fn good() where for<T> T: 'static, !1_"T": 'static {
|
||||
| +++++++++++++++++
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0310`.
|
@ -0,0 +1,22 @@
|
||||
// revisions: good bad
|
||||
|
||||
//[good] known-bug: unknown
|
||||
// `for<T> T: 'static` doesn't imply itself when processing outlives obligations
|
||||
|
||||
#![feature(non_lifetime_binders)]
|
||||
//[bad]~^ WARN the feature `non_lifetime_binders` is incomplete
|
||||
|
||||
fn foo() where for<T> T: 'static {}
|
||||
|
||||
#[cfg(bad)]
|
||||
fn bad() {
|
||||
foo();
|
||||
//[bad]~^ ERROR the placeholder type `!1_"T"` may not live long enough
|
||||
}
|
||||
|
||||
#[cfg(good)]
|
||||
fn good() where for<T> T: 'static {
|
||||
foo();
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,10 +1,9 @@
|
||||
// edition:2021
|
||||
// check-pass
|
||||
// known-bug: unknown
|
||||
|
||||
// Checks that test_type_match code doesn't ICE when predicates have late-bound types
|
||||
|
||||
#![feature(non_lifetime_binders)]
|
||||
//~^ WARN is incomplete and may not be safe to use
|
||||
|
||||
async fn walk2<'a, T: 'a>(_: T)
|
||||
where
|
||||
|
@ -7,5 +7,20 @@ LL | #![feature(non_lifetime_binders)]
|
||||
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
error[E0309]: the placeholder type `!1_"F"` may not live long enough
|
||||
--> $DIR/type-match-with-late-bound.rs:11:1
|
||||
|
|
||||
LL | async fn walk2<'a, T: 'a>(_: T)
|
||||
| -- the placeholder type `!1_"F"` must be valid for the lifetime `'a` as defined here...
|
||||
...
|
||||
LL | {}
|
||||
| ^^ ...so that the type `F` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound
|
||||
|
|
||||
LL | for<F> F: 'a, !1_"F": 'a
|
||||
| ~~~~~~~~~~~~
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0309`.
|
||||
|
Loading…
Reference in New Issue
Block a user