Skip to content

Commit b3057f4

Browse files
committed
Check projections are well-formed when using projection candidates
1 parent 87f2f42 commit b3057f4

File tree

13 files changed

+278
-30
lines changed

13 files changed

+278
-30
lines changed

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,9 +323,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
323323
_ => return,
324324
}
325325

326-
let result = self
327-
.infcx
328-
.probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
326+
let result = self.infcx.probe(|_| {
327+
self.match_projection_obligation_against_definition_bounds(obligation).is_some()
328+
});
329329

330330
if result {
331331
candidates.vec.push(ProjectionCandidate);

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
6969
}
7070

7171
ProjectionCandidate => {
72-
self.confirm_projection_candidate(obligation);
73-
Ok(ImplSource::Param(Vec::new()))
72+
let obligations = self.confirm_projection_candidate(obligation);
73+
Ok(ImplSource::Param(obligations))
7474
}
7575

7676
ClosureCandidate => {
@@ -116,10 +116,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
116116
}
117117
}
118118

119-
fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) {
119+
fn confirm_projection_candidate(
120+
&mut self,
121+
obligation: &TraitObligation<'tcx>,
122+
) -> Vec<PredicateObligation<'tcx>> {
120123
self.infcx.commit_unconditionally(|_| {
121-
let result = self.match_projection_obligation_against_definition_bounds(obligation);
122-
assert!(result);
124+
let candidate = self
125+
.match_projection_obligation_against_definition_bounds(obligation)
126+
.unwrap_or_else(|| bug!("Can't find selected projection candidate"));
127+
let mut obligations = self
128+
.infcx
129+
.at(&obligation.cause, obligation.param_env)
130+
.sup(obligation.predicate.to_poly_trait_ref(), candidate)
131+
.map(|InferOk { obligations, .. }| obligations)
132+
.unwrap_or_else(|_| {
133+
bug!(
134+
"Projection bound `{:?}` was applicable to `{:?}` but now is not",
135+
candidate,
136+
obligation
137+
);
138+
});
139+
// Require that the projection is well-formed.
140+
let self_ty = obligation.predicate.skip_binder().self_ty();
141+
obligations.push(Obligation::new(
142+
obligation.cause.clone(),
143+
obligation.param_env,
144+
ty::PredicateKind::WellFormed(self_ty.into()).to_predicate(self.tcx()),
145+
));
146+
obligations
123147
})
124148
}
125149

compiler/rustc_trait_selection/src/traits/select/mod.rs

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,7 +1159,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11591159
fn match_projection_obligation_against_definition_bounds(
11601160
&mut self,
11611161
obligation: &TraitObligation<'tcx>,
1162-
) -> bool {
1162+
) -> Option<ty::PolyTraitRef<'tcx>> {
11631163
let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate);
11641164
let (placeholder_trait_predicate, _) =
11651165
self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate);
@@ -1170,9 +1170,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11701170
);
11711171

11721172
let tcx = self.infcx.tcx;
1173-
let predicates = match *placeholder_trait_predicate.trait_ref.self_ty().kind() {
1174-
ty::Projection(ref data) => tcx.item_bounds(data.item_def_id).subst(tcx, data.substs),
1175-
ty::Opaque(def_id, substs) => tcx.item_bounds(def_id).subst(tcx, substs),
1173+
let (def_id, substs) = match *placeholder_trait_predicate.trait_ref.self_ty().kind() {
1174+
ty::Projection(ref data) => (data.item_def_id, data.substs),
1175+
ty::Opaque(def_id, substs) => (def_id, substs),
11761176
_ => {
11771177
span_bug!(
11781178
obligation.cause.span,
@@ -1182,12 +1182,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11821182
);
11831183
}
11841184
};
1185+
let bounds = tcx.item_bounds(def_id).subst(tcx, substs);
11851186

1186-
let matching_bound = predicates.iter().find_map(|bound| {
1187+
let matching_bound = bounds.iter().find_map(|bound| {
11871188
if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() {
11881189
let bound = ty::Binder::bind(pred.trait_ref);
11891190
if self.infcx.probe(|_| {
11901191
self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref)
1192+
.is_ok()
11911193
}) {
11921194
return Some(bound);
11931195
}
@@ -1200,30 +1202,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12001202
matching_bound={:?}",
12011203
matching_bound
12021204
);
1203-
match matching_bound {
1204-
None => false,
1205-
Some(bound) => {
1206-
// Repeat the successful match, if any, this time outside of a probe.
1207-
let result =
1208-
self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref);
1209-
1210-
assert!(result);
1211-
true
1212-
}
1213-
}
1205+
matching_bound
12141206
}
12151207

12161208
fn match_projection(
12171209
&mut self,
12181210
obligation: &TraitObligation<'tcx>,
12191211
trait_bound: ty::PolyTraitRef<'tcx>,
12201212
placeholder_trait_ref: ty::TraitRef<'tcx>,
1221-
) -> bool {
1213+
) -> Result<Vec<PredicateObligation<'tcx>>, ()> {
12221214
debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars());
12231215
self.infcx
12241216
.at(&obligation.cause, obligation.param_env)
12251217
.sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound)
1226-
.is_ok()
1218+
.map(|InferOk { obligations, .. }| obligations)
1219+
.map_err(|_| ())
12271220
}
12281221

12291222
fn evaluate_where_clause<'o>(

compiler/rustc_typeck/src/check/compare_method.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1269,7 +1269,11 @@ pub fn check_type_bounds<'tcx>(
12691269
// Finally, resolve all regions. This catches wily misuses of
12701270
// lifetime parameters.
12711271
let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id);
1272-
fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &[]);
1272+
let implied_bounds = match impl_ty.container {
1273+
ty::TraitContainer(_) => vec![],
1274+
ty::ImplContainer(def_id) => fcx.impl_implied_bounds(def_id, impl_ty_span),
1275+
};
1276+
fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &implied_bounds);
12731277

12741278
Ok(())
12751279
})

compiler/rustc_typeck/src/check/wfcheck.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1425,7 +1425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14251425
.collect()
14261426
}
14271427

1428-
fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec<Ty<'tcx>> {
1428+
pub(super) fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec<Ty<'tcx>> {
14291429
match self.tcx.impl_trait_ref(impl_def_id) {
14301430
Some(ref trait_ref) => {
14311431
// Trait impl: take implied bounds from all types that
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Like `projection-bound-cycle.rs` but this avoids using
2+
// `feature(trivial_bounds)`.
3+
4+
#![feature(generic_associated_types)]
5+
//~^ WARNING the feature `generic_associated_types` is incomplete
6+
7+
trait Print {
8+
fn print();
9+
}
10+
11+
trait Foo {
12+
type Item: Sized where <Self as Foo>::Item: Sized;
13+
}
14+
15+
struct Number<T> { t: T }
16+
17+
impl<T> Foo for Number<T> {
18+
// Well-formedness checks require that the following
19+
// goal is true:
20+
// ```
21+
// if ([T]: Sized) { # if the where clauses hold
22+
// [T]: Sized # then the bound on the associated type hold
23+
// }
24+
// ```
25+
// which it is :)
26+
type Item where [T]: Sized = [T];
27+
}
28+
29+
struct OnlySized<T> where T: Sized { f: T }
30+
impl<T> Print for OnlySized<T> {
31+
fn print() {
32+
println!("{}", std::mem::size_of::<T>());
33+
}
34+
}
35+
36+
trait Bar {
37+
type Assoc: Print;
38+
}
39+
40+
impl<T> Bar for T where T: Foo {
41+
// This is not ok, we need to prove `wf(<T as Foo>::Item)`, which requires
42+
// knowing that `<T as Foo>::Item: Sized` to satisfy the where clause. We
43+
// can use the bound on `Foo::Item` for this, but that requires
44+
// `wf(<T as Foo>::Item)`, which is an invalid cycle.
45+
type Assoc = OnlySized<<T as Foo>::Item>;
46+
//~^ ERROR overflow evaluating the requirement `<T as Foo>::Item: std::marker::Sized`
47+
}
48+
49+
fn foo<T: Print>() {
50+
T::print() // oops, in fact `T = OnlySized<str>` which is ill-formed
51+
}
52+
53+
fn bar<T: Bar>() {
54+
// we have `FromEnv(T: Bar)` hence
55+
// `<T as Bar>::Assoc` is well-formed and
56+
// `Implemented(<T as Bar>::Assoc: Print)` hold
57+
foo::<<T as Bar>::Assoc>()
58+
}
59+
60+
fn main() {
61+
bar::<Number<u8>>()
62+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/projection-bound-cycle-generic.rs:4:12
3+
|
4+
LL | #![feature(generic_associated_types)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
9+
10+
error[E0275]: overflow evaluating the requirement `<T as Foo>::Item: std::marker::Sized`
11+
--> $DIR/projection-bound-cycle-generic.rs:45:5
12+
|
13+
LL | struct OnlySized<T> where T: Sized { f: T }
14+
| - required by this bound in `OnlySized`
15+
...
16+
LL | type Assoc = OnlySized<<T as Foo>::Item>;
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
19+
error: aborting due to previous error; 1 warning emitted
20+
21+
For more information about this error, try `rustc --explain E0275`.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Test case from Chalk.
2+
// Make sure that we make sure that we don't allow arbitrary bounds to be
3+
// proven when a bound and a where clause of an associated type are the same.
4+
5+
#![feature(generic_associated_types)]
6+
//~^ WARNING the feature `generic_associated_types` is incomplete
7+
#![feature(trivial_bounds)]
8+
9+
trait Print {
10+
fn print();
11+
}
12+
13+
trait Foo {
14+
type Item: Sized where <Self as Foo>::Item: Sized;
15+
}
16+
17+
struct Number { }
18+
19+
impl Foo for Number {
20+
// Well-formedness checks require that the following
21+
// goal is true:
22+
// ```
23+
// if (str: Sized) { # if the where clauses hold
24+
// str: Sized # then the bound on the associated type hold
25+
// }
26+
// ```
27+
// which it is :)
28+
type Item where str: Sized = str;
29+
}
30+
31+
struct OnlySized<T> where T: Sized { f: T }
32+
impl<T> Print for OnlySized<T> {
33+
fn print() {
34+
println!("{}", std::mem::size_of::<T>());
35+
}
36+
}
37+
38+
trait Bar {
39+
type Assoc: Print;
40+
}
41+
42+
impl<T> Bar for T where T: Foo {
43+
// This is not ok, we need to prove `wf(<T as Foo>::Item)`, which requires
44+
// knowing that `<T as Foo>::Item: Sized` to satisfy the where clause. We
45+
// can use the bound on `Foo::Item` for this, but that requires
46+
// `wf(<T as Foo>::Item)`, which is an invalid cycle.
47+
type Assoc = OnlySized<<T as Foo>::Item>;
48+
//~^ ERROR overflow evaluating the requirement `<T as Foo>::Item: std::marker::Sized`
49+
}
50+
51+
fn foo<T: Print>() {
52+
T::print() // oops, in fact `T = OnlySized<str>` which is ill-formed
53+
}
54+
55+
fn bar<T: Bar>() {
56+
// we have `FromEnv(T: Bar)` hence
57+
// `<T as Bar>::Assoc` is well-formed and
58+
// `Implemented(<T as Bar>::Assoc: Print)` hold
59+
foo::<<T as Bar>::Assoc>()
60+
}
61+
62+
fn main() {
63+
bar::<Number>()
64+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/projection-bound-cycle.rs:5:12
3+
|
4+
LL | #![feature(generic_associated_types)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
9+
10+
error[E0275]: overflow evaluating the requirement `<T as Foo>::Item: std::marker::Sized`
11+
--> $DIR/projection-bound-cycle.rs:47:5
12+
|
13+
LL | struct OnlySized<T> where T: Sized { f: T }
14+
| - required by this bound in `OnlySized`
15+
...
16+
LL | type Assoc = OnlySized<<T as Foo>::Item>;
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
19+
error: aborting due to previous error; 1 warning emitted
20+
21+
For more information about this error, try `rustc --explain E0275`.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![feature(specialization)]
2+
//~^ WARN the feature `specialization` is incomplete
3+
4+
trait Iterate<'a> {
5+
type Ty: Valid;
6+
fn iterate(self);
7+
}
8+
impl<'a, T> Iterate<'a> for T where T: Check {
9+
default type Ty = ();
10+
default fn iterate(self) {}
11+
}
12+
13+
trait Check {}
14+
impl<'a, T> Check for T where <T as Iterate<'a>>::Ty: Valid {}
15+
16+
trait Valid {}
17+
18+
impl Valid for () {}
19+
20+
fn main() {
21+
Iterate::iterate(0);
22+
//~^ ERROR overflow evaluating the requirement `{integer}: Check`
23+
}

0 commit comments

Comments
 (0)