use rustc_infer::infer::{self, InferCtxt, SubregionOrigin};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::TypeVisitable;
+use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::{Span, DUMMY_SP};
self.add_outlives(r1_vid, r2_vid);
}
- GenericArgKind::Type(mut t1) => {
+ GenericArgKind::Type(t1) => {
// we don't actually use this for anything, but
// the `TypeOutlives` code needs an origin.
let origin = infer::RelateParamBound(DUMMY_SP, t1, None);
- // Placeholder regions need to be converted now because it may
- // create new region variables, which can't be done later when
- // verifying these bounds.
- if t1.has_placeholders() {
- t1 = tcx.fold_regions(t1, |r, _| match *r {
- ty::RePlaceholder(placeholder) => {
- self.constraints.placeholder_region(self.infcx, placeholder)
- }
- _ => r,
- });
- }
-
TypeOutlives::new(
&mut *self,
tcx,
}
}
+ /// Placeholder regions need to be converted eagerly because it may
+ /// create new region variables, which we must not do when verifying
+ /// our region bounds.
+ ///
+ /// FIXME: This should get removed once higher ranked region obligations
+ /// are dealt with during trait solving.
+ fn replace_placeholders_with_nll<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T {
+ if value.has_placeholders() {
+ self.tcx.fold_regions(value, |r, _| match *r {
+ ty::RePlaceholder(placeholder) => {
+ self.constraints.placeholder_region(self.infcx, placeholder)
+ }
+ _ => r,
+ })
+ } else {
+ value
+ }
+ }
+
fn verify_to_type_test(
&mut self,
generic_kind: GenericKind<'tcx>,
verify_bound: VerifyBound<'tcx>,
) -> TypeTest<'tcx> {
let lower_bound = self.to_region_vid(region);
-
TypeTest { generic_kind, lower_bound, locations: self.locations, verify_bound }
}
a: ty::Region<'tcx>,
bound: VerifyBound<'tcx>,
) {
+ let kind = self.replace_placeholders_with_nll(kind);
+ let bound = self.replace_placeholders_with_nll(bound);
let type_test = self.verify_to_type_test(kind, a, bound);
self.add_type_test(type_test);
}
let constraint_sets: Vec<_> = unnormalized_input_output_tys
.flat_map(|ty| {
debug!("build: input_or_output={:?}", ty);
- // We only add implied bounds for the normalized type as the unnormalized
- // type may not actually get checked by the caller.
- //
- // Can otherwise be unsound, see #91068.
+ // We add implied bounds from both the unnormalized and normalized ty.
+ // See issue #87748
+ let constraints_implied1 = self.add_implied_bounds(ty);
let TypeOpOutput { output: norm_ty, constraints: constraints1, .. } = self
.param_env
.and(type_op::normalize::Normalize::new(ty))
// }
// ```
// Both &Self::Bar and &() are WF
- let constraints_implied = self.add_implied_bounds(norm_ty);
+ let constraints_implied2 =
+ if ty != norm_ty { self.add_implied_bounds(norm_ty) } else { None };
normalized_inputs_and_output.push(norm_ty);
- constraints1.into_iter().chain(constraints_implied)
+ constraints1.into_iter().chain(constraints_implied1).chain(constraints_implied2)
})
.collect();
))
});
debug!(?sig);
- let sig = self.normalize(sig, term_location);
- self.check_call_dest(body, term, &sig, *destination, target, term_location);
-
+ // IMPORTANT: We have to prove well formed for the function signature before
+ // we normalize it, as otherwise types like `<&'a &'b () as Trait>::Assoc`
+ // get normalized away, causing us to ignore the `'b: 'a` bound used by the function.
+ //
+ // Normalization results in a well formed type if the input is well formed, so we
+ // don't have to check it twice.
+ //
+ // See #91068 for an example.
self.prove_predicates(
sig.inputs_and_output
.iter()
term_location.to_locations(),
ConstraintCategory::Boring,
);
+ let sig = self.normalize(sig, term_location);
+ self.check_call_dest(body, term, &sig, *destination, target, term_location);
// The ordinary liveness rules will ensure that all
// regions in the type of the callee are live here. We
/// }
/// ```
/// This is described with an `AnyRegion('a, 'b)` node.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, TypeFoldable, TypeVisitable)]
pub enum VerifyBound<'tcx> {
/// See [`VerifyIfEq`] docs
IfEq(ty::Binder<'tcx, VerifyIfEq<'tcx>>),
}
self
}
+
+ /// Whether this projection can be soundly normalized.
+ ///
+ /// Wf predicates must not be normalized, as normalization
+ /// can remove required bounds which would cause us to
+ /// unsoundly accept some programs. See #91068.
+ #[inline]
+ pub fn allow_normalization(self) -> bool {
+ match self.kind().skip_binder() {
+ PredicateKind::WellFormed(_) => false,
+ PredicateKind::Trait(_)
+ | PredicateKind::RegionOutlives(_)
+ | PredicateKind::TypeOutlives(_)
+ | PredicateKind::Projection(_)
+ | PredicateKind::ObjectSafe(_)
+ | PredicateKind::ClosureKind(_, _, _)
+ | PredicateKind::Subtype(_)
+ | PredicateKind::Coerce(_)
+ | PredicateKind::ConstEvaluatable(_)
+ | PredicateKind::ConstEquate(_, _)
+ | PredicateKind::TypeWellFormedFromEnv(_) => true,
+ }
+ }
}
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Predicate<'tcx> {
constant.eval(self.selcx.tcx(), self.param_env)
}
}
+
+ #[inline]
+ fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+ if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) {
+ p.super_fold_with(self)
+ } else {
+ p
+ }
+ }
}
pub struct BoundVarReplacer<'me, 'tcx> {
mir::ConstantKind::Val(_, _) => constant.try_super_fold_with(self)?,
})
}
+
+ #[inline]
+ fn try_fold_predicate(
+ &mut self,
+ p: ty::Predicate<'tcx>,
+ ) -> Result<ty::Predicate<'tcx>, Self::Error> {
+ if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) {
+ p.try_super_fold_with(self)
+ } else {
+ Ok(p)
+ }
+ }
}
let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
+ // Next, add all inputs and output as well-formed tys. Importantly,
+ // we have to do this before normalization, since the normalized ty may
+ // not contain the input parameters. See issue #87748.
+ wf_tys.extend(trait_sig.inputs_and_output.iter());
let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig);
- // Add the resulting inputs and output as well-formed.
+ // We also have to add the normalized trait signature
+ // as we don't normalize during implied bounds computation.
wf_tys.extend(trait_sig.inputs_and_output.iter());
let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig));
{
fn get_service(
//~^ ERROR the trait bound `Bug: Foo` is not satisfied
+ //~| ERROR the trait bound `Bug: Foo` is not satisfied
&self,
) -> Self::AssocType;
- //~^ the trait bound `Bug: Foo` is not satisfied
}
fn with_factory<H>(factory: dyn ThriftService<()>) {}
LL | |
LL | | Service<AssocType = <Bug as Foo>::OnlyFoo>
... |
-LL | |
+LL | | ) -> Self::AssocType;
LL | | }
| |_^ the trait `Foo` is not implemented for `Bug`
|
|
LL | / fn get_service(
LL | |
+LL | |
LL | | &self,
LL | | ) -> Self::AssocType;
| |_________________________^ the trait `Foo` is not implemented for `Bug`
| ^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()`
error[E0277]: the trait bound `Bug: Foo` is not satisfied
- --> $DIR/issue-59324.rs:19:10
+ --> $DIR/issue-59324.rs:16:8
|
-LL | ) -> Self::AssocType;
- | ^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Bug`
+LL | fn get_service(
+ | ^^^^^^^^^^^ the trait `Foo` is not implemented for `Bug`
|
help: consider further restricting this bound
|
-// check-pass
+// check-fail
trait Trait {
type Type;
fn g<'a, 'b>() {
f::<'a, 'b>(());
+ //~^ ERROR lifetime may not live long enough
}
fn main() {}
--- /dev/null
+error: lifetime may not live long enough
+ --> $DIR/implied-bounds-unnorm-associated-type-2.rs:19:5
+ |
+LL | fn g<'a, 'b>() {
+ | -- -- lifetime `'b` defined here
+ | |
+ | lifetime `'a` defined here
+LL | f::<'a, 'b>(());
+ | ^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
+ |
+ = help: consider adding the following bound: `'b: 'a`
+ = note: requirement occurs because of a function pointer to `f`
+ = note: the function `f` is invariant over the parameter `'a`
+ = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+error: aborting due to previous error
+
-// check-fail
-// See issue #91899. If we treat unnormalized args as WF, `Self` can also be a
-// source of unsoundness.
+// check-pass
pub trait Yokeable<'a>: 'static {
type Output: 'a;
impl<T> ZeroCopyFrom<[T]> for &'static [T] {
fn zero_copy_from<'b>(cart: &'b [T]) -> &'b [T] {
- //~^ the parameter
cart
}
}
+++ /dev/null
-error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/implied-bounds-unnorm-associated-type-3.rs:19:5
- |
-LL | fn zero_copy_from<'b>(cart: &'b [T]) -> &'b [T] {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `[T]` will meet its required lifetime bounds
- |
-help: consider adding an explicit lifetime bound...
- |
-LL | impl<T: 'static> ZeroCopyFrom<[T]> for &'static [T] {
- | +++++++++
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0310`.
--- /dev/null
+// A regression test for #98543
+
+trait Trait {
+ type Type;
+}
+
+impl<T> Trait for T {
+ type Type = ();
+}
+
+fn f<'a, 'b>(s: &'b str, _: <&'a &'b () as Trait>::Type) -> &'a str
+where
+ &'a &'b (): Trait, // <- adding this bound is the change from #91068
+{
+ s
+}
+
+fn main() {
+ let x = String::from("Hello World!");
+ let y = f(&x, ());
+ drop(x);
+ //~^ ERROR cannot move out of `x` because it is borrowed
+ println!("{}", y);
+}
--- /dev/null
+error[E0505]: cannot move out of `x` because it is borrowed
+ --> $DIR/implied-bounds-unnorm-associated-type-4.rs:21:10
+ |
+LL | let y = f(&x, ());
+ | -- borrow of `x` occurs here
+LL | drop(x);
+ | ^ move out of `x` occurs here
+LL |
+LL | println!("{}", y);
+ | - borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0505`.
--- /dev/null
+trait Trait<'a>: 'a {
+ type Type;
+}
+
+// if the `T: 'a` bound gets implied we would probably get ub here again
+impl<'a, T> Trait<'a> for T {
+ //~^ ERROR the parameter type `T` may not live long enough
+ type Type = ();
+}
+
+fn f<'a, 'b>(s: &'b str, _: <&'b () as Trait<'a>>::Type) -> &'a str
+where
+ &'b (): Trait<'a>,
+{
+ s
+}
+
+fn main() {
+ let x = String::from("Hello World!");
+ let y = f(&x, ());
+ drop(x);
+ println!("{}", y);
+}
--- /dev/null
+error[E0309]: the parameter type `T` may not live long enough
+ --> $DIR/implied-bounds-unnorm-associated-type-5.rs:6:13
+ |
+LL | impl<'a, T> Trait<'a> for T {
+ | ^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/implied-bounds-unnorm-associated-type-5.rs:1:18
+ |
+LL | trait Trait<'a>: 'a {
+ | ^^
+help: consider adding an explicit lifetime bound...
+ |
+LL | impl<'a, T: 'a> Trait<'a> for T {
+ | ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0309`.
// check-fail
-// See issue #91068. Types in the substs of an associated type can't be implied
-// to be WF, since they don't actually have to be constructed.
+// See issue #91068. We check that the unnormalized associated types in
+// function signatures are implied
trait Trait {
type Type;
fn f<'a, 'b>(s: &'b str, _: <&'a &'b () as Trait>::Type) -> &'a str {
s
- //~^ ERROR lifetime may not live long enough
}
fn main() {
let x = String::from("Hello World!");
let y = f(&x, ());
drop(x);
+ //~^ ERROR cannot move out of `x` because it is borrowed
println!("{}", y);
}
-error: lifetime may not live long enough
- --> $DIR/implied-bounds-unnorm-associated-type.rs:14:5
+error[E0505]: cannot move out of `x` because it is borrowed
+ --> $DIR/implied-bounds-unnorm-associated-type.rs:20:10
|
-LL | fn f<'a, 'b>(s: &'b str, _: <&'a &'b () as Trait>::Type) -> &'a str {
- | -- -- lifetime `'b` defined here
- | |
- | lifetime `'a` defined here
-LL | s
- | ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
- |
- = help: consider adding the following bound: `'b: 'a`
+LL | let y = f(&x, ());
+ | -- borrow of `x` occurs here
+LL | drop(x);
+ | ^ move out of `x` occurs here
+LL |
+LL | println!("{}", y);
+ | - borrow later used here
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0505`.
+++ /dev/null
-// check-fail
-// known-bug: #87748
-
-// This should pass, but unnormalized input args aren't treated as implied.
-
-#![feature(generic_associated_types)]
-
-trait MyTrait {
- type Assoc<'a, 'b> where 'b: 'a;
- fn do_sth(arg: Self::Assoc<'_, '_>);
-}
-
-struct Foo;
-
-impl MyTrait for Foo {
- type Assoc<'a, 'b> = u32 where 'b: 'a;
-
- fn do_sth(_: u32) {}
- // fn do_sth(_: Self::Assoc<'static, 'static>) {}
- // fn do_sth(_: Self::Assoc<'_, '_>) {}
-}
-
-fn main() {}
+++ /dev/null
-error[E0478]: lifetime bound not satisfied
- --> $DIR/issue-87748.rs:18:5
- |
-LL | fn do_sth(_: u32) {}
- | ^^^^^^^^^^^^^^^^^
- |
-note: lifetime parameter instantiated with the anonymous lifetime as defined here
- --> $DIR/issue-87748.rs:18:5
- |
-LL | fn do_sth(_: u32) {}
- | ^^^^^^^^^^^^^^^^^
-note: but lifetime parameter must outlive the anonymous lifetime as defined here
- --> $DIR/issue-87748.rs:18:5
- |
-LL | fn do_sth(_: u32) {}
- | ^^^^^^^^^^^^^^^^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0478`.
--- /dev/null
+// Checks that we properly add implied bounds from unnormalized projections in
+// inputs when typechecking functions.
+
+// check-pass
+
+#![feature(generic_associated_types)]
+
+trait MyTrait {
+ type Assoc<'a, 'b> where 'b: 'a;
+ fn do_sth(arg: Self::Assoc<'_, '_>);
+ fn do_sth2(arg: Self::Assoc<'_, '_>) {}
+}
+
+struct Foo;
+
+impl MyTrait for Foo {
+ type Assoc<'a, 'b> = u32 where 'b: 'a;
+
+ fn do_sth(_: u32) {}
+ fn do_sth2(_: Self::Assoc<'static, 'static>) {}
+}
+
+fn main() {}
fn give_me_ice<T>() {
callee::<fn(&()) -> <T as SomeTrait<'_>>::Associated>();
//~^ ERROR the trait bound `for<'r> T: SomeTrait<'r>` is not satisfied [E0277]
- //~| ERROR the trait bound `for<'r> T: SomeTrait<'r>` is not satisfied [E0277]
}
fn callee<T: Fn<(&'static (),)>>() {
LL | fn give_me_ice<T: for<'r> SomeTrait<'r>>() {
| +++++++++++++++++++++++
-error[E0277]: the trait bound `for<'r> T: SomeTrait<'r>` is not satisfied
- --> $DIR/issue-85455.rs:8:14
- |
-LL | callee::<fn(&()) -> <T as SomeTrait<'_>>::Associated>();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'r> SomeTrait<'r>` is not implemented for `T`
- |
-help: consider restricting type parameter `T`
- |
-LL | fn give_me_ice<T: for<'r> SomeTrait<'r>>() {
- | +++++++++++++++++++++++
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
let _x = <fn(&())>::make_f();
//~^ ERROR implementation of `Y` is not general enough
//~| ERROR implementation of `Y` is not general enough
+ //~| ERROR implementation of `Y` is not general enough
}
= note: `Y` would have to be implemented for the type `for<'r> fn(&'r ())`
= note: ...but `Y` is actually implemented for the type `fn(&'0 ())`, for some specific lifetime `'0`
-error: aborting due to 2 previous errors
+error: implementation of `Y` is not general enough
+ --> $DIR/impl-fn-ignore-binder-via-bottom.rs:30:14
+ |
+LL | let _x = <fn(&())>::make_f();
+ | ^^^^^^^^^^^^^^^^^^^ implementation of `Y` is not general enough
+ |
+ = note: `Y` would have to be implemented for the type `for<'r> fn(&'r ())`
+ = note: ...but `Y` is actually implemented for the type `fn(&'0 ())`, for some specific lifetime `'0`
+
+error: aborting due to 3 previous errors