Skip to content

Commit dc442c5

Browse files
Make fast-path for implied wf lint better
1 parent 70d081e commit dc442c5

File tree

1 file changed

+92
-71
lines changed

1 file changed

+92
-71
lines changed

compiler/rustc_hir_analysis/src/check/compare_method.rs

+92-71
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,14 @@ pub(crate) fn compare_impl_method<'tcx>(
6767
return;
6868
}
6969

70-
if let Err(_) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref)
71-
{
70+
if let Err(_) = compare_predicate_entailment(
71+
tcx,
72+
impl_m,
73+
impl_m_span,
74+
trait_m,
75+
impl_trait_ref,
76+
CheckImpliedWfMode::Check,
77+
) {
7278
return;
7379
}
7480
}
@@ -146,6 +152,7 @@ fn compare_predicate_entailment<'tcx>(
146152
impl_m_span: Span,
147153
trait_m: &ty::AssocItem,
148154
impl_trait_ref: ty::TraitRef<'tcx>,
155+
check_implied_wf: CheckImpliedWfMode,
149156
) -> Result<(), ErrorGuaranteed> {
150157
let trait_to_impl_substs = impl_trait_ref.substs;
151158

@@ -300,92 +307,106 @@ fn compare_predicate_entailment<'tcx>(
300307
return Err(emitted);
301308
}
302309

303-
// Check that all obligations are satisfied by the implementation's
304-
// version.
305-
let errors = ocx.select_all_or_error();
306-
if !errors.is_empty() {
307-
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
308-
return Err(reported);
310+
if check_implied_wf == CheckImpliedWfMode::Check {
311+
// We need to check that the impl's args are well-formed given
312+
// the hybrid param-env (impl + trait method where-clauses).
313+
ocx.register_obligation(traits::Obligation::new(
314+
infcx.tcx,
315+
ObligationCause::dummy(),
316+
param_env,
317+
ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_impl_fty.into())),
318+
));
309319
}
310-
311-
// FIXME(compiler-errors): This can be removed when IMPLIED_BOUNDS_ENTAILMENT
312-
// becomes a hard error.
313-
let lint_infcx = infcx.fork();
314-
315-
// Finally, resolve all regions. This catches wily misuses of
316-
// lifetime parameters.
317-
let outlives_environment = OutlivesEnvironment::with_bounds(
318-
param_env,
319-
Some(infcx),
320-
infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys.clone()),
321-
);
322-
if let Some(guar) = infcx.check_region_obligations_and_report_errors(
323-
impl_m.def_id.expect_local(),
324-
&outlives_environment,
325-
) {
326-
return Err(guar);
327-
}
328-
329-
// FIXME(compiler-errors): This can be simplified when IMPLIED_BOUNDS_ENTAILMENT
330-
// becomes a hard error (i.e. ideally we'd just register a WF obligation above...)
331-
lint_implied_wf_entailment(
332-
impl_m.def_id.expect_local(),
333-
lint_infcx,
334-
param_env,
335-
unnormalized_impl_fty,
336-
wf_tys,
337-
);
338-
339-
Ok(())
340-
}
341-
342-
fn lint_implied_wf_entailment<'tcx>(
343-
impl_m_def_id: LocalDefId,
344-
infcx: InferCtxt<'tcx>,
345-
param_env: ty::ParamEnv<'tcx>,
346-
unnormalized_impl_fty: Ty<'tcx>,
347-
wf_tys: FxIndexSet<Ty<'tcx>>,
348-
) {
349-
let ocx = ObligationCtxt::new(&infcx);
350-
351-
// We need to check that the impl's args are well-formed given
352-
// the hybrid param-env (impl + trait method where-clauses).
353-
ocx.register_obligation(traits::Obligation::new(
354-
infcx.tcx,
355-
ObligationCause::dummy(),
356-
param_env,
357-
ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_impl_fty.into())),
358-
));
359-
360-
let hir_id = infcx.tcx.hir().local_def_id_to_hir_id(impl_m_def_id);
361-
let lint = || {
320+
let emit_implied_wf_lint = || {
362321
infcx.tcx.struct_span_lint_hir(
363322
rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT,
364-
hir_id,
365-
infcx.tcx.def_span(impl_m_def_id),
323+
impl_m_hir_id,
324+
infcx.tcx.def_span(impl_m.def_id),
366325
"impl method assumes more implied bounds than the corresponding trait method",
367326
|lint| lint,
368327
);
369328
};
370329

330+
// Check that all obligations are satisfied by the implementation's
331+
// version.
371332
let errors = ocx.select_all_or_error();
372333
if !errors.is_empty() {
373-
lint();
334+
match check_implied_wf {
335+
CheckImpliedWfMode::Check => {
336+
return compare_predicate_entailment(
337+
tcx,
338+
impl_m,
339+
impl_m_span,
340+
trait_m,
341+
impl_trait_ref,
342+
CheckImpliedWfMode::Skip,
343+
)
344+
.map(|()| {
345+
// If the skip-mode was successful, emit a lint.
346+
emit_implied_wf_lint();
347+
});
348+
}
349+
CheckImpliedWfMode::Skip => {
350+
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
351+
return Err(reported);
352+
}
353+
}
374354
}
375355

376-
let outlives_environment = OutlivesEnvironment::with_bounds(
356+
// Finally, resolve all regions. This catches wily misuses of
357+
// lifetime parameters.
358+
let outlives_env = OutlivesEnvironment::with_bounds(
377359
param_env,
378-
Some(&infcx),
379-
infcx.implied_bounds_tys(param_env, hir_id, wf_tys.clone()),
360+
Some(infcx),
361+
infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys.clone()),
380362
);
381363
infcx.process_registered_region_obligations(
382-
outlives_environment.region_bound_pairs(),
383-
param_env,
364+
outlives_env.region_bound_pairs(),
365+
outlives_env.param_env,
384366
);
385-
386-
if !infcx.resolve_regions(&outlives_environment).is_empty() {
387-
lint();
367+
let errors = infcx.resolve_regions(&outlives_env);
368+
if !errors.is_empty() {
369+
// FIXME(compiler-errors): This can be simplified when IMPLIED_BOUNDS_ENTAILMENT
370+
// becomes a hard error (i.e. ideally we'd just call `resolve_regions_and_report_errors`
371+
match check_implied_wf {
372+
CheckImpliedWfMode::Check => {
373+
return compare_predicate_entailment(
374+
tcx,
375+
impl_m,
376+
impl_m_span,
377+
trait_m,
378+
impl_trait_ref,
379+
CheckImpliedWfMode::Skip,
380+
)
381+
.map(|()| {
382+
// If the skip-mode was successful, emit a lint.
383+
emit_implied_wf_lint();
384+
});
385+
}
386+
CheckImpliedWfMode::Skip => {
387+
if infcx.tainted_by_errors().is_none() {
388+
infcx.err_ctxt().report_region_errors(impl_m.def_id.expect_local(), &errors);
389+
}
390+
return Err(tcx
391+
.sess
392+
.delay_span_bug(rustc_span::DUMMY_SP, "error should have been emitted"));
393+
}
394+
}
388395
}
396+
397+
Ok(())
398+
}
399+
400+
#[derive(Debug, PartialEq, Eq)]
401+
enum CheckImpliedWfMode {
402+
/// Checks implied well-formedness of the impl method. If it fails, we will
403+
/// re-check with `Skip`, and emit a lint if it succeeds.
404+
Check,
405+
/// Skips checking implied well-formedness of the impl method, but will emit
406+
/// a lint if the `compare_predicate_entailment` succeeded. This means that
407+
/// the reason that we had failed earlier during `Check` was due to the impl
408+
/// having stronger requirements than the trait.
409+
Skip,
389410
}
390411

391412
#[instrument(skip(tcx), level = "debug", ret)]

0 commit comments

Comments
 (0)