mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Add IMPLIED_BOUNDS_ENTAILMENT lint
This commit is contained in:
parent
4653c93e44
commit
96154d7fa7
@ -255,15 +255,15 @@ fn compare_predicate_entailment<'tcx>(
|
|||||||
|
|
||||||
let mut wf_tys = FxIndexSet::default();
|
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,
|
impl_m_span,
|
||||||
infer::HigherRankedType,
|
infer::HigherRankedType,
|
||||||
tcx.fn_sig(impl_m.def_id),
|
tcx.fn_sig(impl_m.def_id),
|
||||||
);
|
);
|
||||||
|
let unnormalized_impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(unnormalized_impl_sig));
|
||||||
|
|
||||||
let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_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_fty = ocx.normalize(&norm_cause, param_env, unnormalized_impl_fty);
|
||||||
let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig));
|
|
||||||
debug!("compare_impl_method: impl_fty={:?}", 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);
|
let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
|
||||||
@ -312,21 +312,86 @@ fn compare_predicate_entailment<'tcx>(
|
|||||||
return Err(reported);
|
return Err(reported);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME(compiler-errors): This can be removed when IMPLIED_BOUNDS_ENTAILMENT
|
||||||
|
// becomes a hard error.
|
||||||
|
let lint_infcx = infcx.fork();
|
||||||
|
|
||||||
// Finally, resolve all regions. This catches wily misuses of
|
// Finally, resolve all regions. This catches wily misuses of
|
||||||
// lifetime parameters.
|
// lifetime parameters.
|
||||||
let outlives_environment = OutlivesEnvironment::with_bounds(
|
let outlives_environment = OutlivesEnvironment::with_bounds(
|
||||||
param_env,
|
param_env,
|
||||||
Some(infcx),
|
Some(infcx),
|
||||||
infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys),
|
infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys.clone()),
|
||||||
);
|
);
|
||||||
infcx.check_region_obligations_and_report_errors(
|
if let Some(guar) = infcx.check_region_obligations_and_report_errors(
|
||||||
impl_m.def_id.expect_local(),
|
impl_m.def_id.expect_local(),
|
||||||
&outlives_environment,
|
&outlives_environment,
|
||||||
|
) {
|
||||||
|
return Err(guar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(compiler-errors): This can be simplified when IMPLIED_BOUNDS_ENTAILMENT
|
||||||
|
// becomes a hard error (i.e. ideally we'd just register a WF obligation above...)
|
||||||
|
lint_implied_wf_entailment(
|
||||||
|
impl_m.def_id.expect_local(),
|
||||||
|
lint_infcx,
|
||||||
|
param_env,
|
||||||
|
unnormalized_impl_fty,
|
||||||
|
wf_tys,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lint_implied_wf_entailment<'tcx>(
|
||||||
|
impl_m_def_id: LocalDefId,
|
||||||
|
infcx: InferCtxt<'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
unnormalized_impl_fty: Ty<'tcx>,
|
||||||
|
wf_tys: FxIndexSet<Ty<'tcx>>,
|
||||||
|
) {
|
||||||
|
let ocx = ObligationCtxt::new(&infcx);
|
||||||
|
|
||||||
|
// 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(
|
||||||
|
infcx.tcx,
|
||||||
|
ObligationCause::dummy(),
|
||||||
|
param_env,
|
||||||
|
ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_impl_fty.into())),
|
||||||
|
));
|
||||||
|
|
||||||
|
let hir_id = infcx.tcx.hir().local_def_id_to_hir_id(impl_m_def_id);
|
||||||
|
let lint = || {
|
||||||
|
infcx.tcx.struct_span_lint_hir(
|
||||||
|
rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT,
|
||||||
|
hir_id,
|
||||||
|
infcx.tcx.def_span(impl_m_def_id),
|
||||||
|
"impl method assumes more implied bounds than the corresponding trait method",
|
||||||
|
|lint| lint,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let errors = ocx.select_all_or_error();
|
||||||
|
if !errors.is_empty() {
|
||||||
|
lint();
|
||||||
|
}
|
||||||
|
|
||||||
|
let outlives_environment = OutlivesEnvironment::with_bounds(
|
||||||
|
param_env,
|
||||||
|
Some(&infcx),
|
||||||
|
infcx.implied_bounds_tys(param_env, hir_id, wf_tys.clone()),
|
||||||
|
);
|
||||||
|
infcx.process_registered_region_obligations(
|
||||||
|
outlives_environment.region_bound_pairs(),
|
||||||
|
param_env,
|
||||||
|
);
|
||||||
|
|
||||||
|
if !infcx.resolve_regions(&outlives_environment).is_empty() {
|
||||||
|
lint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn compare_asyncness<'tcx>(
|
fn compare_asyncness<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
impl_m: &ty::AssocItem,
|
impl_m: &ty::AssocItem,
|
||||||
|
@ -1693,7 +1693,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||||||
&self,
|
&self,
|
||||||
generic_param_scope: LocalDefId,
|
generic_param_scope: LocalDefId,
|
||||||
outlives_env: &OutlivesEnvironment<'tcx>,
|
outlives_env: &OutlivesEnvironment<'tcx>,
|
||||||
) {
|
) -> Option<ErrorGuaranteed> {
|
||||||
let errors = self.resolve_regions(outlives_env);
|
let errors = self.resolve_regions(outlives_env);
|
||||||
|
|
||||||
if let None = self.tainted_by_errors() {
|
if let None = self.tainted_by_errors() {
|
||||||
@ -1704,6 +1704,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||||||
// errors from silly ones.
|
// errors from silly ones.
|
||||||
self.report_region_errors(generic_param_scope, &errors);
|
self.report_region_errors(generic_param_scope, &errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(!errors.is_empty()).then(|| {
|
||||||
|
self.tcx.sess.delay_span_bug(rustc_span::DUMMY_SP, "error should have been emitted")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// [Note-Type-error-reporting]
|
// [Note-Type-error-reporting]
|
||||||
|
@ -68,6 +68,7 @@ use crate::infer::{
|
|||||||
};
|
};
|
||||||
use crate::traits::{ObligationCause, ObligationCauseCode};
|
use crate::traits::{ObligationCause, ObligationCauseCode};
|
||||||
use rustc_data_structures::undo_log::UndoLogs;
|
use rustc_data_structures::undo_log::UndoLogs;
|
||||||
|
use rustc_errors::ErrorGuaranteed;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
use rustc_middle::mir::ConstraintCategory;
|
use rustc_middle::mir::ConstraintCategory;
|
||||||
@ -177,7 +178,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||||||
&self,
|
&self,
|
||||||
generic_param_scope: LocalDefId,
|
generic_param_scope: LocalDefId,
|
||||||
outlives_env: &OutlivesEnvironment<'tcx>,
|
outlives_env: &OutlivesEnvironment<'tcx>,
|
||||||
) {
|
) -> Option<ErrorGuaranteed> {
|
||||||
self.process_registered_region_obligations(
|
self.process_registered_region_obligations(
|
||||||
outlives_env.region_bound_pairs(),
|
outlives_env.region_bound_pairs(),
|
||||||
outlives_env.param_env,
|
outlives_env.param_env,
|
||||||
|
@ -3998,3 +3998,44 @@ declare_lint! {
|
|||||||
Warn,
|
Warn,
|
||||||
"named arguments in format used positionally"
|
"named arguments in format used positionally"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
/// The `implied_bounds_entailment` lint detects cases where the arguments of an impl method
|
||||||
|
/// have stronger implied bounds than those from the trait method it's implementing.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// ```rust,compile_fail
|
||||||
|
/// #![deny(implied_bounds_entailment)]
|
||||||
|
///
|
||||||
|
/// trait Trait {
|
||||||
|
/// fn get<'s>(s: &'s str, _: &'static &'static ()) -> &'static str;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl Trait for () {
|
||||||
|
/// fn get<'s>(s: &'s str, _: &'static &'s ()) -> &'static str {
|
||||||
|
/// s
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let val = <() as Trait>::get(&String::from("blah blah blah"), &&());
|
||||||
|
/// println!("{}", val);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// {{produces}}
|
||||||
|
///
|
||||||
|
/// ### Explanation
|
||||||
|
///
|
||||||
|
/// Neither the trait method, which provides no implied bounds about `'s`, nor the impl,
|
||||||
|
/// which can't name `'s`, requires the main function to prove that 's: 'static, but the
|
||||||
|
/// impl method is able to assume that 's: 'static within its own body.
|
||||||
|
///
|
||||||
|
/// This can be used to implement an unsound API if used incorrectly.
|
||||||
|
pub IMPLIED_BOUNDS_ENTAILMENT,
|
||||||
|
Deny,
|
||||||
|
"impl method assumes more implied bounds than its corresponding trait method",
|
||||||
|
@future_incompatible = FutureIncompatibleInfo {
|
||||||
|
reference: "issue #105572 <https://github.com/rust-lang/rust/issues/105572>",
|
||||||
|
reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
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 impl method assumes more implied bounds than the corresponding trait method
|
||||||
|
//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let val = <() as Trait>::get(&String::from("blah blah blah"), ());
|
||||||
|
println!("{}", val);
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
error: impl method assumes more implied bounds than the corresponding trait method
|
||||||
|
--> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:5
|
||||||
|
|
|
||||||
|
LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
|
||||||
|
= note: `#[deny(implied_bounds_entailment)]` on by default
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
Future incompatibility report: Future breakage diagnostic:
|
||||||
|
error: impl method assumes more implied bounds than the corresponding trait method
|
||||||
|
--> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:5
|
||||||
|
|
|
||||||
|
LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
|
||||||
|
= note: `#[deny(implied_bounds_entailment)]` on by default
|
||||||
|
|
@ -0,0 +1,19 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
pub struct MessageListeners<'a> {
|
||||||
|
listeners: RefCell<Vec<Box<dyn FnMut(()) + 'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 impl method assumes more implied bounds than the corresponding trait method
|
||||||
|
//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,23 @@
|
|||||||
|
error: impl method assumes more implied bounds than the corresponding trait method
|
||||||
|
--> $DIR/impl-implied-bounds-compatibility.rs:12:5
|
||||||
|
|
|
||||||
|
LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
|
||||||
|
= note: `#[deny(implied_bounds_entailment)]` on by default
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
Future incompatibility report: Future breakage diagnostic:
|
||||||
|
error: impl method assumes more implied bounds than the corresponding trait method
|
||||||
|
--> $DIR/impl-implied-bounds-compatibility.rs:12:5
|
||||||
|
|
|
||||||
|
LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
|
||||||
|
= note: `#[deny(implied_bounds_entailment)]` on by default
|
||||||
|
|
Loading…
Reference in New Issue
Block a user