1
Fork 0

Make fast-path for implied wf lint better

This commit is contained in:
Michael Goulet 2022-12-19 19:04:55 +00:00
parent c40ededa10
commit 8c86773fd3

View file

@ -71,8 +71,14 @@ pub(crate) fn compare_impl_method<'tcx>(
return; return;
} }
if let Err(_) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref) if let Err(_) = compare_predicate_entailment(
{ tcx,
impl_m,
impl_m_span,
trait_m,
impl_trait_ref,
CheckImpliedWfMode::Check,
) {
return; return;
} }
} }
@ -150,6 +156,7 @@ fn compare_predicate_entailment<'tcx>(
impl_m_span: Span, impl_m_span: Span,
trait_m: &ty::AssocItem, trait_m: &ty::AssocItem,
impl_trait_ref: ty::TraitRef<'tcx>, impl_trait_ref: ty::TraitRef<'tcx>,
check_implied_wf: CheckImpliedWfMode,
) -> Result<(), ErrorGuaranteed> { ) -> Result<(), ErrorGuaranteed> {
let trait_to_impl_substs = impl_trait_ref.substs; let trait_to_impl_substs = impl_trait_ref.substs;
@ -304,54 +311,7 @@ fn compare_predicate_entailment<'tcx>(
return Err(emitted); return Err(emitted);
} }
// Check that all obligations are satisfied by the implementation's if check_implied_wf == CheckImpliedWfMode::Check {
// version.
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
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
// lifetime parameters.
let outlives_environment = OutlivesEnvironment::with_bounds(
param_env,
Some(infcx),
infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys.clone()),
);
if let Some(guar) = infcx.check_region_obligations_and_report_errors(
impl_m.def_id.expect_local(),
&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(())
}
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 // We need to check that the impl's args are well-formed given
// the hybrid param-env (impl + trait method where-clauses). // the hybrid param-env (impl + trait method where-clauses).
ocx.register_obligation(traits::Obligation::new( ocx.register_obligation(traits::Obligation::new(
@ -360,36 +320,97 @@ fn lint_implied_wf_entailment<'tcx>(
param_env, param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_impl_fty.into())), 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 emit_implied_wf_lint = || {
let lint = || {
infcx.tcx.struct_span_lint_hir( infcx.tcx.struct_span_lint_hir(
rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT, rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT,
hir_id, impl_m_hir_id,
infcx.tcx.def_span(impl_m_def_id), infcx.tcx.def_span(impl_m.def_id),
"impl method assumes more implied bounds than the corresponding trait method", "impl method assumes more implied bounds than the corresponding trait method",
|lint| lint, |lint| lint,
); );
}; };
// Check that all obligations are satisfied by the implementation's
// version.
let errors = ocx.select_all_or_error(); let errors = ocx.select_all_or_error();
if !errors.is_empty() { if !errors.is_empty() {
lint(); match check_implied_wf {
CheckImpliedWfMode::Check => {
return compare_predicate_entailment(
tcx,
impl_m,
impl_m_span,
trait_m,
impl_trait_ref,
CheckImpliedWfMode::Skip,
)
.map(|()| {
// If the skip-mode was successful, emit a lint.
emit_implied_wf_lint();
});
}
CheckImpliedWfMode::Skip => {
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
return Err(reported);
}
}
} }
let outlives_environment = OutlivesEnvironment::with_bounds( // Finally, resolve all regions. This catches wily misuses of
// lifetime parameters.
let outlives_env = OutlivesEnvironment::with_bounds(
param_env, param_env,
Some(&infcx), Some(infcx),
infcx.implied_bounds_tys(param_env, hir_id, wf_tys.clone()), infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys.clone()),
); );
infcx.process_registered_region_obligations( infcx.process_registered_region_obligations(
outlives_environment.region_bound_pairs(), outlives_env.region_bound_pairs(),
param_env, outlives_env.param_env,
); );
let errors = infcx.resolve_regions(&outlives_env);
if !infcx.resolve_regions(&outlives_environment).is_empty() { if !errors.is_empty() {
lint(); // FIXME(compiler-errors): This can be simplified when IMPLIED_BOUNDS_ENTAILMENT
// becomes a hard error (i.e. ideally we'd just call `resolve_regions_and_report_errors`
match check_implied_wf {
CheckImpliedWfMode::Check => {
return compare_predicate_entailment(
tcx,
impl_m,
impl_m_span,
trait_m,
impl_trait_ref,
CheckImpliedWfMode::Skip,
)
.map(|()| {
// If the skip-mode was successful, emit a lint.
emit_implied_wf_lint();
});
} }
CheckImpliedWfMode::Skip => {
if infcx.tainted_by_errors().is_none() {
infcx.err_ctxt().report_region_errors(impl_m.def_id.expect_local(), &errors);
}
return Err(tcx
.sess
.delay_span_bug(rustc_span::DUMMY_SP, "error should have been emitted"));
}
}
}
Ok(())
}
#[derive(Debug, PartialEq, Eq)]
enum CheckImpliedWfMode {
/// Checks implied well-formedness of the impl method. If it fails, we will
/// re-check with `Skip`, and emit a lint if it succeeds.
Check,
/// Skips checking implied well-formedness of the impl method, but will emit
/// a lint if the `compare_predicate_entailment` succeeded. This means that
/// the reason that we had failed earlier during `Check` was due to the impl
/// having stronger requirements than the trait.
Skip,
} }
fn compare_asyncness<'tcx>( fn compare_asyncness<'tcx>(