Make regionck care about placeholders in outlives components

This commit is contained in:
Michael Goulet 2023-11-17 03:00:38 +00:00
parent 290fc68f2d
commit 8f267e2b87
16 changed files with 177 additions and 13 deletions

View File

@ -80,6 +80,10 @@ pub(crate) fn insert_outlives_predicate<'tcx>(
.or_insert(span); .or_insert(span);
} }
Component::Placeholder(_) => {
span_bug!(span, "Should not deduce placeholder outlives component");
}
Component::Alias(alias_ty) => { Component::Alias(alias_ty) => {
// This would either arise from something like: // This would either arise from something like:
// //

View File

@ -2351,6 +2351,7 @@ pub fn construct_generic_bound_failure(
let labeled_user_string = match bound_kind { let labeled_user_string = match bound_kind {
GenericKind::Param(ref p) => format!("the parameter type `{p}`"), 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) { GenericKind::Alias(ref p) => match p.kind(self.tcx) {
ty::AliasKind::Projection | ty::AliasKind::Inherent => { ty::AliasKind::Projection | ty::AliasKind::Inherent => {
format!("the associated type `{p}`") format!("the associated type `{p}`")

View File

@ -11,6 +11,7 @@
pub enum Component<'tcx> { pub enum Component<'tcx> {
Region(ty::Region<'tcx>), Region(ty::Region<'tcx>),
Param(ty::ParamTy), Param(ty::ParamTy),
Placeholder(ty::PlaceholderType),
UnresolvedInferenceVariable(ty::InferTy), UnresolvedInferenceVariable(ty::InferTy),
// Projections like `T::Foo` are tricky because a constraint like // Projections like `T::Foo` are tricky because a constraint like
@ -120,6 +121,10 @@ fn compute_components<'tcx>(
out.push(Component::Param(p)); out.push(Component::Param(p));
} }
ty::Placeholder(p) => {
out.push(Component::Placeholder(p));
}
// For projections, we prefer to generate an obligation like // For projections, we prefer to generate an obligation like
// `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
// regionck more ways to prove that it holds. However, // regionck more ways to prove that it holds. However,
@ -176,7 +181,6 @@ fn compute_components<'tcx>(
ty::Tuple(..) | // ... ty::Tuple(..) | // ...
ty::FnPtr(_) | // OutlivesFunction (*) ty::FnPtr(_) | // OutlivesFunction (*)
ty::Dynamic(..) | // OutlivesObject, OutlivesFragment (*) ty::Dynamic(..) | // OutlivesObject, OutlivesFragment (*)
ty::Placeholder(..) |
ty::Bound(..) | ty::Bound(..) |
ty::Error(_) => { ty::Error(_) => {
// (*) Function pointers and trait objects are both binders. // (*) Function pointers and trait objects are both binders.

View File

@ -243,6 +243,9 @@ fn components_must_outlive(
Component::Param(param_ty) => { Component::Param(param_ty) => {
self.param_ty_must_outlive(origin, region, *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::Alias(alias_ty) => self.alias_ty_must_outlive(origin, region, *alias_ty),
Component::EscapingAlias(subcomponents) => { Component::EscapingAlias(subcomponents) => {
self.components_must_outlive(origin, &subcomponents, region, category); self.components_must_outlive(origin, &subcomponents, region, category);
@ -267,10 +270,28 @@ fn param_ty_must_outlive(
region: ty::Region<'tcx>, region: ty::Region<'tcx>,
param_ty: ty::ParamTy, 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); 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))] #[instrument(level = "debug", skip(self))]
fn alias_ty_must_outlive( fn alias_ty_must_outlive(
&mut self, &mut self,

View File

@ -37,11 +37,11 @@ pub fn new(
} }
#[instrument(level = "debug", skip(self))] #[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 // Start with anything like `T: 'a` we can scrape from the
// environment. If the environment contains something like // environment. If the environment contains something like
// `for<'a> T: 'a`, then we know that `T` outlives everything. // `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); debug!(?declared_bounds_from_env);
let mut param_bounds = vec![]; let mut param_bounds = vec![];
for declared_bound in declared_bounds_from_env { 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)); param_bounds.push(VerifyBound::OutlivedBy(region));
} else { } else {
// This is `for<'a> T: 'a`. This means that `T` outlives everything! All done here. // 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![]); return VerifyBound::AllBounds(vec![]);
} }
} }
@ -168,7 +168,10 @@ fn bound_from_single_component(
) -> VerifyBound<'tcx> { ) -> VerifyBound<'tcx> {
match *component { match *component {
Component::Region(lt) => VerifyBound::OutlivedBy(lt), 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::Alias(alias_ty) => self.alias_bound(alias_ty, visited),
Component::EscapingAlias(ref components) => { Component::EscapingAlias(ref components) => {
self.bound_from_components(components, visited) 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. /// bounds, but all the bounds it returns can be relied upon.
fn declared_generic_bounds_from_env( fn declared_generic_bounds_from_env(
&self, &self,
param_ty: ty::ParamTy, generic_ty: Ty<'tcx>,
) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'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) self.declared_generic_bounds_from_env_for_erased_ty(generic_ty)
} }

View File

@ -147,6 +147,7 @@ pub struct Verify<'tcx> {
#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)] #[derive(Copy, Clone, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)]
pub enum GenericKind<'tcx> { pub enum GenericKind<'tcx> {
Param(ty::ParamTy), Param(ty::ParamTy),
Placeholder(ty::PlaceholderType),
Alias(ty::AliasTy<'tcx>), Alias(ty::AliasTy<'tcx>),
} }
@ -707,6 +708,7 @@ impl<'tcx> fmt::Debug for GenericKind<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self { match *self {
GenericKind::Param(ref p) => write!(f, "{p:?}"), GenericKind::Param(ref p) => write!(f, "{p:?}"),
GenericKind::Placeholder(ref p) => write!(f, "{p:?}"),
GenericKind::Alias(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 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self { match *self {
GenericKind::Param(ref p) => write!(f, "{p}"), GenericKind::Param(ref p) => write!(f, "{p}"),
GenericKind::Placeholder(ref p) => write!(f, "{p:?}"),
GenericKind::Alias(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> { pub fn to_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match *self { match *self {
GenericKind::Param(ref p) => p.to_ty(tcx), 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), GenericKind::Alias(ref p) => p.to_ty(tcx),
} }
} }

View File

@ -365,6 +365,11 @@ fn elaborate(&mut self, elaboratable: &O) {
Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min))) 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::UnresolvedInferenceVariable(_) => None,
Component::Alias(alias_ty) => { Component::Alias(alias_ty) => {

View File

@ -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); 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 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 impl1_header = fresh_impl_header(infcx, impl1_def_id);
let param_env = let param_env =
@ -414,7 +415,12 @@ fn impl_intersection_has_negative_obligation(
return false; 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);
util::elaborate(tcx, tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args)) util::elaborate(tcx, tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args))
.any(|(clause, _)| try_prove_negated_where_clause(infcx, clause, param_env)) .any(|(clause, _)| try_prove_negated_where_clause(infcx, clause, param_env))

View File

@ -208,6 +208,9 @@ fn implied_bounds_from_components<'tcx>(
Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)), Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)),
Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)), Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)),
Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(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(_) => Component::EscapingAlias(_) =>
// If the projection has escaping regions, don't // If the projection has escaping regions, don't
// try to infer any implied bounds even for its // try to infer any implied bounds even for its

View File

@ -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() {}

View File

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

View File

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

View File

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

View File

@ -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() {}

View File

@ -1,10 +1,9 @@
// edition:2021 // edition:2021
// check-pass // known-bug: unknown
// Checks that test_type_match code doesn't ICE when predicates have late-bound types // Checks that test_type_match code doesn't ICE when predicates have late-bound types
#![feature(non_lifetime_binders)] #![feature(non_lifetime_binders)]
//~^ WARN is incomplete and may not be safe to use
async fn walk2<'a, T: 'a>(_: T) async fn walk2<'a, T: 'a>(_: T)
where where

View File

@ -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: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
= note: `#[warn(incomplete_features)]` on by default = 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`.