From d535e4790388e8c5012c7450655d7c0647cb390b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 9 Dec 2022 01:50:04 +0000 Subject: [PATCH 1/2] Check that impl method args are WF given implied-bounds assumptions from trait --- .../src/check/compare_method.rs | 23 +++++++++++-- .../impl-implied-bounds-compatibility.rs | 18 +++++++++++ .../impl-implied-bounds-compatibility.stderr | 32 +++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/implied-bounds/impl-implied-bounds-compatibility.rs create mode 100644 src/test/ui/implied-bounds/impl-implied-bounds-compatibility.stderr diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs index 1d6f9b2917651..eadb7ae85c707 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_method.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs @@ -251,14 +251,14 @@ fn compare_predicate_entailment<'tcx>( let mut wf_tys = FxIndexSet::default(); - let impl_sig = infcx.replace_bound_vars_with_fresh_vars( + let unnormalized_impl_sig = infcx.replace_bound_vars_with_fresh_vars( impl_m_span, infer::HigherRankedType, tcx.fn_sig(impl_m.def_id), ); let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id); - let impl_sig = ocx.normalize(&norm_cause, param_env, impl_sig); + let impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_impl_sig); let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)); debug!("compare_impl_method: impl_fty={:?}", impl_fty); @@ -300,6 +300,25 @@ fn compare_predicate_entailment<'tcx>( return Err(emitted); } + // Finally, we need to check that the impl's args are well-formed given + // the hybrid param-env (impl + trait method where-clauses). + for (unnormalized_arg, arg) in + std::iter::zip(unnormalized_impl_sig.inputs_and_output, impl_sig.inputs_and_output) + { + ocx.register_obligation(traits::Obligation::new( + tcx, + cause.clone(), + param_env, + ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_arg.into())), + )); + ocx.register_obligation(traits::Obligation::new( + tcx, + cause.clone(), + param_env, + ty::Binder::dummy(ty::PredicateKind::WellFormed(arg.into())), + )); + } + // Check that all obligations are satisfied by the implementation's // version. let errors = ocx.select_all_or_error(); diff --git a/src/test/ui/implied-bounds/impl-implied-bounds-compatibility.rs b/src/test/ui/implied-bounds/impl-implied-bounds-compatibility.rs new file mode 100644 index 0000000000000..442179c072f07 --- /dev/null +++ b/src/test/ui/implied-bounds/impl-implied-bounds-compatibility.rs @@ -0,0 +1,18 @@ +use std::cell::RefCell; + +pub struct MessageListeners<'a> { + listeners: RefCell>>, +} + +pub trait MessageListenersInterface { + fn listeners<'c>(&'c self) -> &'c MessageListeners<'c>; +} + +impl<'a> MessageListenersInterface for MessageListeners<'a> { + fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { + //~^ ERROR cannot infer an appropriate lifetime for lifetime parameter 'b in generic type due to conflicting requirement + self + } +} + +fn main() {} diff --git a/src/test/ui/implied-bounds/impl-implied-bounds-compatibility.stderr b/src/test/ui/implied-bounds/impl-implied-bounds-compatibility.stderr new file mode 100644 index 0000000000000..cca31ea4fcc16 --- /dev/null +++ b/src/test/ui/implied-bounds/impl-implied-bounds-compatibility.stderr @@ -0,0 +1,32 @@ +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 'b in generic type due to conflicting requirements + --> $DIR/impl-implied-bounds-compatibility.rs:12:5 + | +LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first, the lifetime cannot outlive the lifetime `'c` as defined here... + --> $DIR/impl-implied-bounds-compatibility.rs:12:5 + | +LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...so that the method type is compatible with trait + --> $DIR/impl-implied-bounds-compatibility.rs:12:5 + | +LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected `fn(&'c MessageListeners<'a>) -> &'c MessageListeners<'c>` + found `fn(&MessageListeners<'a>) -> &'a MessageListeners<'_>` +note: but, the lifetime must be valid for the lifetime `'a` as defined here... + --> $DIR/impl-implied-bounds-compatibility.rs:11:6 + | +LL | impl<'a> MessageListenersInterface for MessageListeners<'a> { + | ^^ +note: ...so that the reference type `&'a MessageListeners<'_>` does not outlive the data it points at + --> $DIR/impl-implied-bounds-compatibility.rs:12:5 + | +LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0495`. From 45d1e921b2012e9cdb89d68ddfbb8afb1c942dd3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 11 Dec 2022 19:28:26 +0000 Subject: [PATCH 2/2] Check that unnormalized sig is WF, add test --- .../src/check/compare_method.rs | 34 +++++++------------ ...plied-bounds-compatibility-unnormalized.rs | 19 +++++++++++ ...d-bounds-compatibility-unnormalized.stderr | 28 +++++++++++++++ 3 files changed, 60 insertions(+), 21 deletions(-) create mode 100644 src/test/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.rs create mode 100644 src/test/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs index eadb7ae85c707..38a53a7da67fc 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_method.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs @@ -256,10 +256,21 @@ fn compare_predicate_entailment<'tcx>( infer::HigherRankedType, tcx.fn_sig(impl_m.def_id), ); + let unnormalized_impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(unnormalized_impl_sig)); + + // We need to check that the impl's args are well-formed given + // the hybrid param-env (impl + trait method where-clauses). + ocx.register_obligation(traits::Obligation::new( + tcx, + cause.clone(), + param_env, + ty::Binder::dummy(ty::PredicateKind::WellFormed( + tcx.mk_fn_ptr(ty::Binder::dummy(unnormalized_impl_sig)).into(), + )), + )); let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id); - let impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_impl_sig); - let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)); + let impl_fty = ocx.normalize(&norm_cause, param_env, unnormalized_impl_fty); debug!("compare_impl_method: impl_fty={:?}", impl_fty); let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs); @@ -300,25 +311,6 @@ fn compare_predicate_entailment<'tcx>( return Err(emitted); } - // Finally, we need to check that the impl's args are well-formed given - // the hybrid param-env (impl + trait method where-clauses). - for (unnormalized_arg, arg) in - std::iter::zip(unnormalized_impl_sig.inputs_and_output, impl_sig.inputs_and_output) - { - ocx.register_obligation(traits::Obligation::new( - tcx, - cause.clone(), - param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_arg.into())), - )); - ocx.register_obligation(traits::Obligation::new( - tcx, - cause.clone(), - param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(arg.into())), - )); - } - // Check that all obligations are satisfied by the implementation's // version. let errors = ocx.select_all_or_error(); diff --git a/src/test/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.rs b/src/test/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.rs new file mode 100644 index 0000000000000..280856805e551 --- /dev/null +++ b/src/test/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.rs @@ -0,0 +1,19 @@ +trait Project { + type Ty; +} +impl Project for &'_ &'_ () { + type Ty = (); +} +trait Trait { + fn get<'s>(s: &'s str, _: ()) -> &'static str; +} +impl Trait for () { + fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str { + //~^ ERROR cannot infer an appropriate lifetime for lifetime parameter 's in generic type due to conflicting requirements + s + } +} +fn main() { + let val = <() as Trait>::get(&String::from("blah blah blah"), ()); + println!("{}", val); +} diff --git a/src/test/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr b/src/test/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr new file mode 100644 index 0000000000000..4e4d9102db338 --- /dev/null +++ b/src/test/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr @@ -0,0 +1,28 @@ +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 's in generic type due to conflicting requirements + --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:5 + | +LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first, the lifetime cannot outlive the lifetime `'s` as defined here... + --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:12 + | +LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str { + | ^^ +note: ...so that the method type is compatible with trait + --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:5 + | +LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected `fn(&'s str, ()) -> &'static str` + found `fn(&str, ()) -> &'static str` + = note: but, the lifetime must be valid for the static lifetime... +note: ...so that the reference type `&'static &()` does not outlive the data it points at + --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:5 + | +LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0495`.