Check projections are well-formed when using projection candidates
This commit is contained in:
parent
87f2f42dc2
commit
b3057f4d5f
|
@ -323,9 +323,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = self
|
let result = self.infcx.probe(|_| {
|
||||||
.infcx
|
self.match_projection_obligation_against_definition_bounds(obligation).is_some()
|
||||||
.probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
|
});
|
||||||
|
|
||||||
if result {
|
if result {
|
||||||
candidates.vec.push(ProjectionCandidate);
|
candidates.vec.push(ProjectionCandidate);
|
||||||
|
|
|
@ -69,8 +69,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectionCandidate => {
|
ProjectionCandidate => {
|
||||||
self.confirm_projection_candidate(obligation);
|
let obligations = self.confirm_projection_candidate(obligation);
|
||||||
Ok(ImplSource::Param(Vec::new()))
|
Ok(ImplSource::Param(obligations))
|
||||||
}
|
}
|
||||||
|
|
||||||
ClosureCandidate => {
|
ClosureCandidate => {
|
||||||
|
@ -116,10 +116,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) {
|
fn confirm_projection_candidate(
|
||||||
|
&mut self,
|
||||||
|
obligation: &TraitObligation<'tcx>,
|
||||||
|
) -> Vec<PredicateObligation<'tcx>> {
|
||||||
self.infcx.commit_unconditionally(|_| {
|
self.infcx.commit_unconditionally(|_| {
|
||||||
let result = self.match_projection_obligation_against_definition_bounds(obligation);
|
let candidate = self
|
||||||
assert!(result);
|
.match_projection_obligation_against_definition_bounds(obligation)
|
||||||
|
.unwrap_or_else(|| bug!("Can't find selected projection candidate"));
|
||||||
|
let mut obligations = self
|
||||||
|
.infcx
|
||||||
|
.at(&obligation.cause, obligation.param_env)
|
||||||
|
.sup(obligation.predicate.to_poly_trait_ref(), candidate)
|
||||||
|
.map(|InferOk { obligations, .. }| obligations)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
bug!(
|
||||||
|
"Projection bound `{:?}` was applicable to `{:?}` but now is not",
|
||||||
|
candidate,
|
||||||
|
obligation
|
||||||
|
);
|
||||||
|
});
|
||||||
|
// Require that the projection is well-formed.
|
||||||
|
let self_ty = obligation.predicate.skip_binder().self_ty();
|
||||||
|
obligations.push(Obligation::new(
|
||||||
|
obligation.cause.clone(),
|
||||||
|
obligation.param_env,
|
||||||
|
ty::PredicateKind::WellFormed(self_ty.into()).to_predicate(self.tcx()),
|
||||||
|
));
|
||||||
|
obligations
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1159,7 +1159,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
fn match_projection_obligation_against_definition_bounds(
|
fn match_projection_obligation_against_definition_bounds(
|
||||||
&mut self,
|
&mut self,
|
||||||
obligation: &TraitObligation<'tcx>,
|
obligation: &TraitObligation<'tcx>,
|
||||||
) -> bool {
|
) -> Option<ty::PolyTraitRef<'tcx>> {
|
||||||
let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate);
|
let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate);
|
||||||
let (placeholder_trait_predicate, _) =
|
let (placeholder_trait_predicate, _) =
|
||||||
self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate);
|
self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate);
|
||||||
|
@ -1170,9 +1170,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
);
|
);
|
||||||
|
|
||||||
let tcx = self.infcx.tcx;
|
let tcx = self.infcx.tcx;
|
||||||
let predicates = match *placeholder_trait_predicate.trait_ref.self_ty().kind() {
|
let (def_id, substs) = match *placeholder_trait_predicate.trait_ref.self_ty().kind() {
|
||||||
ty::Projection(ref data) => tcx.item_bounds(data.item_def_id).subst(tcx, data.substs),
|
ty::Projection(ref data) => (data.item_def_id, data.substs),
|
||||||
ty::Opaque(def_id, substs) => tcx.item_bounds(def_id).subst(tcx, substs),
|
ty::Opaque(def_id, substs) => (def_id, substs),
|
||||||
_ => {
|
_ => {
|
||||||
span_bug!(
|
span_bug!(
|
||||||
obligation.cause.span,
|
obligation.cause.span,
|
||||||
|
@ -1182,12 +1182,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let bounds = tcx.item_bounds(def_id).subst(tcx, substs);
|
||||||
|
|
||||||
let matching_bound = predicates.iter().find_map(|bound| {
|
let matching_bound = bounds.iter().find_map(|bound| {
|
||||||
if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() {
|
if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() {
|
||||||
let bound = ty::Binder::bind(pred.trait_ref);
|
let bound = ty::Binder::bind(pred.trait_ref);
|
||||||
if self.infcx.probe(|_| {
|
if self.infcx.probe(|_| {
|
||||||
self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref)
|
self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref)
|
||||||
|
.is_ok()
|
||||||
}) {
|
}) {
|
||||||
return Some(bound);
|
return Some(bound);
|
||||||
}
|
}
|
||||||
|
@ -1200,17 +1202,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
matching_bound={:?}",
|
matching_bound={:?}",
|
||||||
matching_bound
|
matching_bound
|
||||||
);
|
);
|
||||||
match matching_bound {
|
matching_bound
|
||||||
None => false,
|
|
||||||
Some(bound) => {
|
|
||||||
// Repeat the successful match, if any, this time outside of a probe.
|
|
||||||
let result =
|
|
||||||
self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref);
|
|
||||||
|
|
||||||
assert!(result);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_projection(
|
fn match_projection(
|
||||||
|
@ -1218,12 +1210,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
obligation: &TraitObligation<'tcx>,
|
obligation: &TraitObligation<'tcx>,
|
||||||
trait_bound: ty::PolyTraitRef<'tcx>,
|
trait_bound: ty::PolyTraitRef<'tcx>,
|
||||||
placeholder_trait_ref: ty::TraitRef<'tcx>,
|
placeholder_trait_ref: ty::TraitRef<'tcx>,
|
||||||
) -> bool {
|
) -> Result<Vec<PredicateObligation<'tcx>>, ()> {
|
||||||
debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars());
|
debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars());
|
||||||
self.infcx
|
self.infcx
|
||||||
.at(&obligation.cause, obligation.param_env)
|
.at(&obligation.cause, obligation.param_env)
|
||||||
.sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound)
|
.sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound)
|
||||||
.is_ok()
|
.map(|InferOk { obligations, .. }| obligations)
|
||||||
|
.map_err(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate_where_clause<'o>(
|
fn evaluate_where_clause<'o>(
|
||||||
|
|
|
@ -1269,7 +1269,11 @@ pub fn check_type_bounds<'tcx>(
|
||||||
// Finally, resolve all regions. This catches wily misuses of
|
// Finally, resolve all regions. This catches wily misuses of
|
||||||
// lifetime parameters.
|
// lifetime parameters.
|
||||||
let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id);
|
let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id);
|
||||||
fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &[]);
|
let implied_bounds = match impl_ty.container {
|
||||||
|
ty::TraitContainer(_) => vec![],
|
||||||
|
ty::ImplContainer(def_id) => fcx.impl_implied_bounds(def_id, impl_ty_span),
|
||||||
|
};
|
||||||
|
fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &implied_bounds);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|
|
@ -1425,7 +1425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec<Ty<'tcx>> {
|
pub(super) fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec<Ty<'tcx>> {
|
||||||
match self.tcx.impl_trait_ref(impl_def_id) {
|
match self.tcx.impl_trait_ref(impl_def_id) {
|
||||||
Some(ref trait_ref) => {
|
Some(ref trait_ref) => {
|
||||||
// Trait impl: take implied bounds from all types that
|
// Trait impl: take implied bounds from all types that
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Like `projection-bound-cycle.rs` but this avoids using
|
||||||
|
// `feature(trivial_bounds)`.
|
||||||
|
|
||||||
|
#![feature(generic_associated_types)]
|
||||||
|
//~^ WARNING the feature `generic_associated_types` is incomplete
|
||||||
|
|
||||||
|
trait Print {
|
||||||
|
fn print();
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Foo {
|
||||||
|
type Item: Sized where <Self as Foo>::Item: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Number<T> { t: T }
|
||||||
|
|
||||||
|
impl<T> Foo for Number<T> {
|
||||||
|
// Well-formedness checks require that the following
|
||||||
|
// goal is true:
|
||||||
|
// ```
|
||||||
|
// if ([T]: Sized) { # if the where clauses hold
|
||||||
|
// [T]: Sized # then the bound on the associated type hold
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
// which it is :)
|
||||||
|
type Item where [T]: Sized = [T];
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OnlySized<T> where T: Sized { f: T }
|
||||||
|
impl<T> Print for OnlySized<T> {
|
||||||
|
fn print() {
|
||||||
|
println!("{}", std::mem::size_of::<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Bar {
|
||||||
|
type Assoc: Print;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Bar for T where T: Foo {
|
||||||
|
// This is not ok, we need to prove `wf(<T as Foo>::Item)`, which requires
|
||||||
|
// knowing that `<T as Foo>::Item: Sized` to satisfy the where clause. We
|
||||||
|
// can use the bound on `Foo::Item` for this, but that requires
|
||||||
|
// `wf(<T as Foo>::Item)`, which is an invalid cycle.
|
||||||
|
type Assoc = OnlySized<<T as Foo>::Item>;
|
||||||
|
//~^ ERROR overflow evaluating the requirement `<T as Foo>::Item: std::marker::Sized`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo<T: Print>() {
|
||||||
|
T::print() // oops, in fact `T = OnlySized<str>` which is ill-formed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar<T: Bar>() {
|
||||||
|
// we have `FromEnv(T: Bar)` hence
|
||||||
|
// `<T as Bar>::Assoc` is well-formed and
|
||||||
|
// `Implemented(<T as Bar>::Assoc: Print)` hold
|
||||||
|
foo::<<T as Bar>::Assoc>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
bar::<Number<u8>>()
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/projection-bound-cycle-generic.rs:4:12
|
||||||
|
|
|
||||||
|
LL | #![feature(generic_associated_types)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
|
||||||
|
|
||||||
|
error[E0275]: overflow evaluating the requirement `<T as Foo>::Item: std::marker::Sized`
|
||||||
|
--> $DIR/projection-bound-cycle-generic.rs:45:5
|
||||||
|
|
|
||||||
|
LL | struct OnlySized<T> where T: Sized { f: T }
|
||||||
|
| - required by this bound in `OnlySized`
|
||||||
|
...
|
||||||
|
LL | type Assoc = OnlySized<<T as Foo>::Item>;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error; 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0275`.
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Test case from Chalk.
|
||||||
|
// Make sure that we make sure that we don't allow arbitrary bounds to be
|
||||||
|
// proven when a bound and a where clause of an associated type are the same.
|
||||||
|
|
||||||
|
#![feature(generic_associated_types)]
|
||||||
|
//~^ WARNING the feature `generic_associated_types` is incomplete
|
||||||
|
#![feature(trivial_bounds)]
|
||||||
|
|
||||||
|
trait Print {
|
||||||
|
fn print();
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Foo {
|
||||||
|
type Item: Sized where <Self as Foo>::Item: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Number { }
|
||||||
|
|
||||||
|
impl Foo for Number {
|
||||||
|
// Well-formedness checks require that the following
|
||||||
|
// goal is true:
|
||||||
|
// ```
|
||||||
|
// if (str: Sized) { # if the where clauses hold
|
||||||
|
// str: Sized # then the bound on the associated type hold
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
// which it is :)
|
||||||
|
type Item where str: Sized = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OnlySized<T> where T: Sized { f: T }
|
||||||
|
impl<T> Print for OnlySized<T> {
|
||||||
|
fn print() {
|
||||||
|
println!("{}", std::mem::size_of::<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Bar {
|
||||||
|
type Assoc: Print;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Bar for T where T: Foo {
|
||||||
|
// This is not ok, we need to prove `wf(<T as Foo>::Item)`, which requires
|
||||||
|
// knowing that `<T as Foo>::Item: Sized` to satisfy the where clause. We
|
||||||
|
// can use the bound on `Foo::Item` for this, but that requires
|
||||||
|
// `wf(<T as Foo>::Item)`, which is an invalid cycle.
|
||||||
|
type Assoc = OnlySized<<T as Foo>::Item>;
|
||||||
|
//~^ ERROR overflow evaluating the requirement `<T as Foo>::Item: std::marker::Sized`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo<T: Print>() {
|
||||||
|
T::print() // oops, in fact `T = OnlySized<str>` which is ill-formed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar<T: Bar>() {
|
||||||
|
// we have `FromEnv(T: Bar)` hence
|
||||||
|
// `<T as Bar>::Assoc` is well-formed and
|
||||||
|
// `Implemented(<T as Bar>::Assoc: Print)` hold
|
||||||
|
foo::<<T as Bar>::Assoc>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
bar::<Number>()
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/projection-bound-cycle.rs:5:12
|
||||||
|
|
|
||||||
|
LL | #![feature(generic_associated_types)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
|
||||||
|
|
||||||
|
error[E0275]: overflow evaluating the requirement `<T as Foo>::Item: std::marker::Sized`
|
||||||
|
--> $DIR/projection-bound-cycle.rs:47:5
|
||||||
|
|
|
||||||
|
LL | struct OnlySized<T> where T: Sized { f: T }
|
||||||
|
| - required by this bound in `OnlySized`
|
||||||
|
...
|
||||||
|
LL | type Assoc = OnlySized<<T as Foo>::Item>;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error; 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0275`.
|
|
@ -0,0 +1,23 @@
|
||||||
|
#![feature(specialization)]
|
||||||
|
//~^ WARN the feature `specialization` is incomplete
|
||||||
|
|
||||||
|
trait Iterate<'a> {
|
||||||
|
type Ty: Valid;
|
||||||
|
fn iterate(self);
|
||||||
|
}
|
||||||
|
impl<'a, T> Iterate<'a> for T where T: Check {
|
||||||
|
default type Ty = ();
|
||||||
|
default fn iterate(self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Check {}
|
||||||
|
impl<'a, T> Check for T where <T as Iterate<'a>>::Ty: Valid {}
|
||||||
|
|
||||||
|
trait Valid {}
|
||||||
|
|
||||||
|
impl Valid for () {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Iterate::iterate(0);
|
||||||
|
//~^ ERROR overflow evaluating the requirement `{integer}: Check`
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/issue-38091-2.rs:1:12
|
||||||
|
|
|
||||||
|
LL | #![feature(specialization)]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
|
||||||
|
|
||||||
|
error[E0275]: overflow evaluating the requirement `{integer}: Check`
|
||||||
|
--> $DIR/issue-38091-2.rs:21:5
|
||||||
|
|
|
||||||
|
LL | fn iterate(self);
|
||||||
|
| ----------------- required by `Iterate::iterate`
|
||||||
|
...
|
||||||
|
LL | Iterate::iterate(0);
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: required because of the requirements on the impl of `Iterate<'_>` for `{integer}`
|
||||||
|
|
||||||
|
error: aborting due to previous error; 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0275`.
|
|
@ -18,4 +18,5 @@ trait Valid {}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
Iterate::iterate(0);
|
Iterate::iterate(0);
|
||||||
|
//~^ ERROR overflow evaluating the requirement `{integer}: Check`
|
||||||
}
|
}
|
|
@ -16,6 +16,18 @@ LL | type Ty: Valid;
|
||||||
LL | default type Ty = ();
|
LL | default type Ty = ();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^ the trait `Valid` is not implemented for `()`
|
| ^^^^^^^^^^^^^^^^^^^^^ the trait `Valid` is not implemented for `()`
|
||||||
|
|
||||||
error: aborting due to previous error; 1 warning emitted
|
error[E0275]: overflow evaluating the requirement `{integer}: Check`
|
||||||
|
--> $DIR/issue-38091.rs:20:5
|
||||||
|
|
|
||||||
|
LL | fn iterate(self);
|
||||||
|
| ----------------- required by `Iterate::iterate`
|
||||||
|
...
|
||||||
|
LL | Iterate::iterate(0);
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: required because of the requirements on the impl of `Iterate<'_>` for `{integer}`
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0277`.
|
error: aborting due to 2 previous errors; 1 warning emitted
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0275, E0277.
|
||||||
|
For more information about an error, try `rustc --explain E0275`.
|
Loading…
Reference in New Issue