Skip to content

Commit 8f267e2

Browse files
Make regionck care about placeholders in outlives components
1 parent 290fc68 commit 8f267e2

16 files changed

+177
-13
lines changed

compiler/rustc_hir_analysis/src/outlives/utils.rs

+4
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ pub(crate) fn insert_outlives_predicate<'tcx>(
8080
.or_insert(span);
8181
}
8282

83+
Component::Placeholder(_) => {
84+
span_bug!(span, "Should not deduce placeholder outlives component");
85+
}
86+
8387
Component::Alias(alias_ty) => {
8488
// This would either arise from something like:
8589
//

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2351,6 +2351,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
23512351

23522352
let labeled_user_string = match bound_kind {
23532353
GenericKind::Param(ref p) => format!("the parameter type `{p}`"),
2354+
GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"),
23542355
GenericKind::Alias(ref p) => match p.kind(self.tcx) {
23552356
ty::AliasKind::Projection | ty::AliasKind::Inherent => {
23562357
format!("the associated type `{p}`")

compiler/rustc_infer/src/infer/outlives/components.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use smallvec::{smallvec, SmallVec};
1111
pub enum Component<'tcx> {
1212
Region(ty::Region<'tcx>),
1313
Param(ty::ParamTy),
14+
Placeholder(ty::PlaceholderType),
1415
UnresolvedInferenceVariable(ty::InferTy),
1516

1617
// Projections like `T::Foo` are tricky because a constraint like
@@ -120,6 +121,10 @@ fn compute_components<'tcx>(
120121
out.push(Component::Param(p));
121122
}
122123

124+
ty::Placeholder(p) => {
125+
out.push(Component::Placeholder(p));
126+
}
127+
123128
// For projections, we prefer to generate an obligation like
124129
// `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
125130
// regionck more ways to prove that it holds. However,
@@ -176,7 +181,6 @@ fn compute_components<'tcx>(
176181
ty::Tuple(..) | // ...
177182
ty::FnPtr(_) | // OutlivesFunction (*)
178183
ty::Dynamic(..) | // OutlivesObject, OutlivesFragment (*)
179-
ty::Placeholder(..) |
180184
ty::Bound(..) |
181185
ty::Error(_) => {
182186
// (*) Function pointers and trait objects are both binders.

compiler/rustc_infer/src/infer/outlives/obligations.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,9 @@ where
243243
Component::Param(param_ty) => {
244244
self.param_ty_must_outlive(origin, region, *param_ty);
245245
}
246+
Component::Placeholder(placeholder_ty) => {
247+
self.placeholder_ty_must_outlive(origin, region, *placeholder_ty);
248+
}
246249
Component::Alias(alias_ty) => self.alias_ty_must_outlive(origin, region, *alias_ty),
247250
Component::EscapingAlias(subcomponents) => {
248251
self.components_must_outlive(origin, &subcomponents, region, category);
@@ -267,10 +270,28 @@ where
267270
region: ty::Region<'tcx>,
268271
param_ty: ty::ParamTy,
269272
) {
270-
let verify_bound = self.verify_bound.param_bound(param_ty);
273+
let verify_bound = self.verify_bound.param_or_placeholder_bound(param_ty.to_ty(self.tcx));
271274
self.delegate.push_verify(origin, GenericKind::Param(param_ty), region, verify_bound);
272275
}
273276

277+
#[instrument(level = "debug", skip(self))]
278+
fn placeholder_ty_must_outlive(
279+
&mut self,
280+
origin: infer::SubregionOrigin<'tcx>,
281+
region: ty::Region<'tcx>,
282+
placeholder_ty: ty::PlaceholderType,
283+
) {
284+
let verify_bound = self
285+
.verify_bound
286+
.param_or_placeholder_bound(Ty::new_placeholder(self.tcx, placeholder_ty));
287+
self.delegate.push_verify(
288+
origin,
289+
GenericKind::Placeholder(placeholder_ty),
290+
region,
291+
verify_bound,
292+
);
293+
}
294+
274295
#[instrument(level = "debug", skip(self))]
275296
fn alias_ty_must_outlive(
276297
&mut self,

compiler/rustc_infer/src/infer/outlives/verify.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
3737
}
3838

3939
#[instrument(level = "debug", skip(self))]
40-
pub fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
40+
pub fn param_or_placeholder_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
4141
// Start with anything like `T: 'a` we can scrape from the
4242
// environment. If the environment contains something like
4343
// `for<'a> T: 'a`, then we know that `T` outlives everything.
44-
let declared_bounds_from_env = self.declared_generic_bounds_from_env(param_ty);
44+
let declared_bounds_from_env = self.declared_generic_bounds_from_env(ty);
4545
debug!(?declared_bounds_from_env);
4646
let mut param_bounds = vec![];
4747
for declared_bound in declared_bounds_from_env {
@@ -51,7 +51,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
5151
param_bounds.push(VerifyBound::OutlivedBy(region));
5252
} else {
5353
// This is `for<'a> T: 'a`. This means that `T` outlives everything! All done here.
54-
debug!("found that {param_ty:?} outlives any lifetime, returning empty vector");
54+
debug!("found that {ty:?} outlives any lifetime, returning empty vector");
5555
return VerifyBound::AllBounds(vec![]);
5656
}
5757
}
@@ -168,7 +168,10 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
168168
) -> VerifyBound<'tcx> {
169169
match *component {
170170
Component::Region(lt) => VerifyBound::OutlivedBy(lt),
171-
Component::Param(param_ty) => self.param_bound(param_ty),
171+
Component::Param(param_ty) => self.param_or_placeholder_bound(param_ty.to_ty(self.tcx)),
172+
Component::Placeholder(placeholder_ty) => {
173+
self.param_or_placeholder_bound(Ty::new_placeholder(self.tcx, placeholder_ty))
174+
}
172175
Component::Alias(alias_ty) => self.alias_bound(alias_ty, visited),
173176
Component::EscapingAlias(ref components) => {
174177
self.bound_from_components(components, visited)
@@ -195,9 +198,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
195198
/// bounds, but all the bounds it returns can be relied upon.
196199
fn declared_generic_bounds_from_env(
197200
&self,
198-
param_ty: ty::ParamTy,
201+
generic_ty: Ty<'tcx>,
199202
) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> {
200-
let generic_ty = param_ty.to_ty(self.tcx);
203+
assert!(matches!(generic_ty.kind(), ty::Param(_) | ty::Placeholder(_)));
201204
self.declared_generic_bounds_from_env_for_erased_ty(generic_ty)
202205
}
203206

compiler/rustc_infer/src/infer/region_constraints/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ pub struct Verify<'tcx> {
147147
#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)]
148148
pub enum GenericKind<'tcx> {
149149
Param(ty::ParamTy),
150+
Placeholder(ty::PlaceholderType),
150151
Alias(ty::AliasTy<'tcx>),
151152
}
152153

@@ -707,6 +708,7 @@ impl<'tcx> fmt::Debug for GenericKind<'tcx> {
707708
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
708709
match *self {
709710
GenericKind::Param(ref p) => write!(f, "{p:?}"),
711+
GenericKind::Placeholder(ref p) => write!(f, "{p:?}"),
710712
GenericKind::Alias(ref p) => write!(f, "{p:?}"),
711713
}
712714
}
@@ -716,6 +718,7 @@ impl<'tcx> fmt::Display for GenericKind<'tcx> {
716718
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
717719
match *self {
718720
GenericKind::Param(ref p) => write!(f, "{p}"),
721+
GenericKind::Placeholder(ref p) => write!(f, "{p:?}"),
719722
GenericKind::Alias(ref p) => write!(f, "{p}"),
720723
}
721724
}
@@ -725,6 +728,7 @@ impl<'tcx> GenericKind<'tcx> {
725728
pub fn to_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
726729
match *self {
727730
GenericKind::Param(ref p) => p.to_ty(tcx),
731+
GenericKind::Placeholder(ref p) => Ty::new_placeholder(tcx, *p),
728732
GenericKind::Alias(ref p) => p.to_ty(tcx),
729733
}
730734
}

compiler/rustc_infer/src/traits/util.rs

+5
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,11 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
365365
Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min)))
366366
}
367367

368+
Component::Placeholder(p) => {
369+
let ty = Ty::new_placeholder(tcx, p);
370+
Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min)))
371+
}
372+
368373
Component::UnresolvedInferenceVariable(_) => None,
369374

370375
Component::Alias(alias_ty) => {

compiler/rustc_trait_selection/src/traits/coherence.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,8 @@ fn impl_intersection_has_negative_obligation(
398398
debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
399399

400400
let ref infcx = tcx.infer_ctxt().intercrate(true).with_next_trait_solver(true).build();
401-
let universe = infcx.universe();
401+
let root_universe = infcx.universe();
402+
assert_eq!(root_universe, ty::UniverseIndex::ROOT);
402403

403404
let impl1_header = fresh_impl_header(infcx, impl1_def_id);
404405
let param_env =
@@ -414,7 +415,12 @@ fn impl_intersection_has_negative_obligation(
414415
return false;
415416
};
416417

417-
plug_infer_with_placeholders(infcx, universe, (impl1_header.impl_args, impl2_header.impl_args));
418+
plug_infer_with_placeholders(
419+
infcx,
420+
root_universe,
421+
(impl1_header.impl_args, impl2_header.impl_args),
422+
);
423+
let param_env = infcx.resolve_vars_if_possible(param_env);
418424

419425
util::elaborate(tcx, tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args))
420426
.any(|(clause, _)| try_prove_negated_where_clause(infcx, clause, param_env))

compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs

+3
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ fn implied_bounds_from_components<'tcx>(
208208
Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)),
209209
Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)),
210210
Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)),
211+
Component::Placeholder(_) => {
212+
unimplemented!("Shouldn't expect a placeholder type in implied bounds (yet)")
213+
}
211214
Component::EscapingAlias(_) =>
212215
// If the projection has escaping regions, don't
213216
// try to infer any implied bounds even for its
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![feature(negative_impls)]
2+
#![feature(with_negative_coherence)]
3+
4+
struct Wrap<T>(T);
5+
6+
trait Foo {}
7+
impl<T: 'static> !Foo for Box<T> {}
8+
9+
trait Bar {}
10+
impl<T> Bar for T where T: Foo {}
11+
impl<T> Bar for Box<T> {}
12+
//~^ ERROR conflicting implementations of trait `Bar` for type `Box<_>`
13+
14+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0119]: conflicting implementations of trait `Bar` for type `Box<_>`
2+
--> $DIR/negative-coherence-check-placeholder-outlives.rs:11:1
3+
|
4+
LL | impl<T> Bar for T where T: Foo {}
5+
| ------------------------------ first implementation here
6+
LL | impl<T> Bar for Box<T> {}
7+
| ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0119`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/placeholders-dont-outlive-static.rs:6:12
3+
|
4+
LL | #![feature(non_lifetime_binders)]
5+
| ^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
8+
= note: `#[warn(incomplete_features)]` on by default
9+
10+
error[E0310]: the placeholder type `!1_"T"` may not live long enough
11+
--> $DIR/placeholders-dont-outlive-static.rs:13:5
12+
|
13+
LL | foo();
14+
| ^^^^^
15+
| |
16+
| the placeholder type `!1_"T"` must be valid for the static lifetime...
17+
| ...so that the type `T` will meet its required lifetime bounds
18+
|
19+
help: consider adding an explicit lifetime bound
20+
|
21+
LL | fn bad() where !1_"T": 'static {
22+
| +++++++++++++++++++++
23+
24+
error: aborting due to previous error; 1 warning emitted
25+
26+
For more information about this error, try `rustc --explain E0310`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/placeholders-dont-outlive-static.rs:6:12
3+
|
4+
LL | #![feature(non_lifetime_binders)]
5+
| ^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
8+
= note: `#[warn(incomplete_features)]` on by default
9+
10+
error[E0310]: the placeholder type `!1_"T"` may not live long enough
11+
--> $DIR/placeholders-dont-outlive-static.rs:19:5
12+
|
13+
LL | foo();
14+
| ^^^^^
15+
| |
16+
| the placeholder type `!1_"T"` must be valid for the static lifetime...
17+
| ...so that the type `T` will meet its required lifetime bounds
18+
|
19+
help: consider adding an explicit lifetime bound
20+
|
21+
LL | fn good() where for<T> T: 'static, !1_"T": 'static {
22+
| +++++++++++++++++
23+
24+
error: aborting due to previous error; 1 warning emitted
25+
26+
For more information about this error, try `rustc --explain E0310`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// revisions: good bad
2+
3+
//[good] known-bug: unknown
4+
// `for<T> T: 'static` doesn't imply itself when processing outlives obligations
5+
6+
#![feature(non_lifetime_binders)]
7+
//[bad]~^ WARN the feature `non_lifetime_binders` is incomplete
8+
9+
fn foo() where for<T> T: 'static {}
10+
11+
#[cfg(bad)]
12+
fn bad() {
13+
foo();
14+
//[bad]~^ ERROR the placeholder type `!1_"T"` may not live long enough
15+
}
16+
17+
#[cfg(good)]
18+
fn good() where for<T> T: 'static {
19+
foo();
20+
}
21+
22+
fn main() {}

tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
// edition:2021
2-
// check-pass
2+
// known-bug: unknown
33

44
// Checks that test_type_match code doesn't ICE when predicates have late-bound types
55

66
#![feature(non_lifetime_binders)]
7-
//~^ WARN is incomplete and may not be safe to use
87

98
async fn walk2<'a, T: 'a>(_: T)
109
where

tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr

+16-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,20 @@ LL | #![feature(non_lifetime_binders)]
77
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
88
= note: `#[warn(incomplete_features)]` on by default
99

10-
warning: 1 warning emitted
10+
error[E0309]: the placeholder type `!1_"F"` may not live long enough
11+
--> $DIR/type-match-with-late-bound.rs:11:1
12+
|
13+
LL | async fn walk2<'a, T: 'a>(_: T)
14+
| -- the placeholder type `!1_"F"` must be valid for the lifetime `'a` as defined here...
15+
...
16+
LL | {}
17+
| ^^ ...so that the type `F` will meet its required lifetime bounds
18+
|
19+
help: consider adding an explicit lifetime bound
20+
|
21+
LL | for<F> F: 'a, !1_"F": 'a
22+
| ~~~~~~~~~~~~
23+
24+
error: aborting due to previous error; 1 warning emitted
1125

26+
For more information about this error, try `rustc --explain E0309`.

0 commit comments

Comments
 (0)