Skip to content

Commit 1dc43b2

Browse files
authored
Rollup merge of #106465 - compiler-errors:bump-IMPLIED_BOUNDS_ENTAILMENT, r=lcnr
Bump `IMPLIED_BOUNDS_ENTAILMENT` to Deny + ReportNow #105575 (comment) > and then later in the same cycle increase the lint to `deny` and change it to `FutureCompatReportNow` in this nightly cycle. r? ```@lcnr``` when they're back from holiday 😄
2 parents 96bb02f + eaa7cc8 commit 1dc43b2

File tree

4 files changed

+184
-18
lines changed

4 files changed

+184
-18
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+148-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use super::potentially_plural_count;
22
use crate::errors::LifetimesOrBoundsMismatchOnTrait;
33
use hir::def_id::{DefId, LocalDefId};
44
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
5-
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed};
5+
use rustc_errors::{
6+
pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed, MultiSpan,
7+
};
68
use rustc_hir as hir;
79
use rustc_hir::def::{DefKind, Res};
810
use rustc_hir::intravisit;
@@ -320,15 +322,6 @@ fn compare_method_predicate_entailment<'tcx>(
320322
ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_impl_fty.into())),
321323
));
322324
}
323-
let emit_implied_wf_lint = || {
324-
infcx.tcx.struct_span_lint_hir(
325-
rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT,
326-
impl_m_hir_id,
327-
infcx.tcx.def_span(impl_m.def_id),
328-
"impl method assumes more implied bounds than the corresponding trait method",
329-
|lint| lint,
330-
);
331-
};
332325

333326
// Check that all obligations are satisfied by the implementation's
334327
// version.
@@ -346,7 +339,7 @@ fn compare_method_predicate_entailment<'tcx>(
346339
)
347340
.map(|()| {
348341
// If the skip-mode was successful, emit a lint.
349-
emit_implied_wf_lint();
342+
emit_implied_wf_lint(infcx.tcx, impl_m, impl_m_hir_id, vec![]);
350343
});
351344
}
352345
CheckImpliedWfMode::Skip => {
@@ -382,8 +375,16 @@ fn compare_method_predicate_entailment<'tcx>(
382375
CheckImpliedWfMode::Skip,
383376
)
384377
.map(|()| {
378+
let bad_args = extract_bad_args_for_implies_lint(
379+
tcx,
380+
&errors,
381+
(trait_m, trait_sig),
382+
// Unnormalized impl sig corresponds to the HIR types written
383+
(impl_m, unnormalized_impl_sig),
384+
impl_m_hir_id,
385+
);
385386
// If the skip-mode was successful, emit a lint.
386-
emit_implied_wf_lint();
387+
emit_implied_wf_lint(tcx, impl_m, impl_m_hir_id, bad_args);
387388
});
388389
}
389390
CheckImpliedWfMode::Skip => {
@@ -400,6 +401,141 @@ fn compare_method_predicate_entailment<'tcx>(
400401
Ok(())
401402
}
402403

404+
fn extract_bad_args_for_implies_lint<'tcx>(
405+
tcx: TyCtxt<'tcx>,
406+
errors: &[infer::RegionResolutionError<'tcx>],
407+
(trait_m, trait_sig): (&ty::AssocItem, ty::FnSig<'tcx>),
408+
(impl_m, impl_sig): (&ty::AssocItem, ty::FnSig<'tcx>),
409+
hir_id: hir::HirId,
410+
) -> Vec<(Span, Option<String>)> {
411+
let mut blame_generics = vec![];
412+
for error in errors {
413+
// Look for the subregion origin that contains an input/output type
414+
let origin = match error {
415+
infer::RegionResolutionError::ConcreteFailure(o, ..) => o,
416+
infer::RegionResolutionError::GenericBoundFailure(o, ..) => o,
417+
infer::RegionResolutionError::SubSupConflict(_, _, o, ..) => o,
418+
infer::RegionResolutionError::UpperBoundUniverseConflict(.., o, _) => o,
419+
};
420+
// Extract (possible) input/output types from origin
421+
match origin {
422+
infer::SubregionOrigin::Subtype(trace) => {
423+
if let Some((a, b)) = trace.values.ty() {
424+
blame_generics.extend([a, b]);
425+
}
426+
}
427+
infer::SubregionOrigin::RelateParamBound(_, ty, _) => blame_generics.push(*ty),
428+
infer::SubregionOrigin::ReferenceOutlivesReferent(ty, _) => blame_generics.push(*ty),
429+
_ => {}
430+
}
431+
}
432+
433+
let fn_decl = tcx.hir().fn_decl_by_hir_id(hir_id).unwrap();
434+
let opt_ret_ty = match fn_decl.output {
435+
hir::FnRetTy::DefaultReturn(_) => None,
436+
hir::FnRetTy::Return(ty) => Some(ty),
437+
};
438+
439+
// Map late-bound regions from trait to impl, so the names are right.
440+
let mapping = std::iter::zip(
441+
tcx.fn_sig(trait_m.def_id).bound_vars(),
442+
tcx.fn_sig(impl_m.def_id).bound_vars(),
443+
)
444+
.filter_map(|(impl_bv, trait_bv)| {
445+
if let ty::BoundVariableKind::Region(impl_bv) = impl_bv
446+
&& let ty::BoundVariableKind::Region(trait_bv) = trait_bv
447+
{
448+
Some((impl_bv, trait_bv))
449+
} else {
450+
None
451+
}
452+
})
453+
.collect();
454+
455+
// For each arg, see if it was in the "blame" of any of the region errors.
456+
// If so, then try to produce a suggestion to replace the argument type with
457+
// one from the trait.
458+
let mut bad_args = vec![];
459+
for (idx, (ty, hir_ty)) in
460+
std::iter::zip(impl_sig.inputs_and_output, fn_decl.inputs.iter().chain(opt_ret_ty))
461+
.enumerate()
462+
{
463+
let expected_ty = trait_sig.inputs_and_output[idx]
464+
.fold_with(&mut RemapLateBound { tcx, mapping: &mapping });
465+
if blame_generics.iter().any(|blame| ty.contains(*blame)) {
466+
let expected_ty_sugg = expected_ty.to_string();
467+
bad_args.push((
468+
hir_ty.span,
469+
// Only suggest something if it actually changed.
470+
(expected_ty_sugg != ty.to_string()).then_some(expected_ty_sugg),
471+
));
472+
}
473+
}
474+
475+
bad_args
476+
}
477+
478+
struct RemapLateBound<'a, 'tcx> {
479+
tcx: TyCtxt<'tcx>,
480+
mapping: &'a FxHashMap<ty::BoundRegionKind, ty::BoundRegionKind>,
481+
}
482+
483+
impl<'tcx> TypeFolder<'tcx> for RemapLateBound<'_, 'tcx> {
484+
fn tcx(&self) -> TyCtxt<'tcx> {
485+
self.tcx
486+
}
487+
488+
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
489+
if let ty::ReFree(fr) = *r {
490+
self.tcx.mk_region(ty::ReFree(ty::FreeRegion {
491+
bound_region: self
492+
.mapping
493+
.get(&fr.bound_region)
494+
.copied()
495+
.unwrap_or(fr.bound_region),
496+
..fr
497+
}))
498+
} else {
499+
r
500+
}
501+
}
502+
}
503+
504+
fn emit_implied_wf_lint<'tcx>(
505+
tcx: TyCtxt<'tcx>,
506+
impl_m: &ty::AssocItem,
507+
hir_id: hir::HirId,
508+
bad_args: Vec<(Span, Option<String>)>,
509+
) {
510+
let span: MultiSpan = if bad_args.is_empty() {
511+
tcx.def_span(impl_m.def_id).into()
512+
} else {
513+
bad_args.iter().map(|(span, _)| *span).collect::<Vec<_>>().into()
514+
};
515+
tcx.struct_span_lint_hir(
516+
rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT,
517+
hir_id,
518+
span,
519+
"impl method assumes more implied bounds than the corresponding trait method",
520+
|lint| {
521+
let bad_args: Vec<_> =
522+
bad_args.into_iter().filter_map(|(span, sugg)| Some((span, sugg?))).collect();
523+
if !bad_args.is_empty() {
524+
lint.multipart_suggestion(
525+
format!(
526+
"replace {} type{} to make the impl signature compatible",
527+
pluralize!("this", bad_args.len()),
528+
pluralize!(bad_args.len())
529+
),
530+
bad_args,
531+
Applicability::MaybeIncorrect,
532+
);
533+
}
534+
lint
535+
},
536+
);
537+
}
538+
403539
#[derive(Debug, PartialEq, Eq)]
404540
enum CheckImpliedWfMode {
405541
/// Checks implied well-formedness of the impl method. If it fails, we will

compiler/rustc_lint_defs/src/builtin.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4033,10 +4033,10 @@ declare_lint! {
40334033
///
40344034
/// This can be used to implement an unsound API if used incorrectly.
40354035
pub IMPLIED_BOUNDS_ENTAILMENT,
4036-
Warn,
4036+
Deny,
40374037
"impl method assumes more implied bounds than its corresponding trait method",
40384038
@future_incompatible = FutureIncompatibleInfo {
40394039
reference: "issue #105572 <https://github.com/rust-lang/rust/issues/105572>",
4040-
reason: FutureIncompatibilityReason::FutureReleaseError,
4040+
reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
40414041
};
40424042
}

tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: impl method assumes more implied bounds than the corresponding trait method
2-
--> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:5
2+
--> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:31
33
|
44
LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `()`
66
|
77
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
88
= note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
@@ -14,3 +14,18 @@ LL | #![deny(implied_bounds_entailment)]
1414

1515
error: aborting due to previous error
1616

17+
Future incompatibility report: Future breakage diagnostic:
18+
error: impl method assumes more implied bounds than the corresponding trait method
19+
--> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:31
20+
|
21+
LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
22+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `()`
23+
|
24+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
25+
= note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
26+
note: the lint level is defined here
27+
--> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:1:9
28+
|
29+
LL | #![deny(implied_bounds_entailment)]
30+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
31+

tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: impl method assumes more implied bounds than the corresponding trait method
2-
--> $DIR/impl-implied-bounds-compatibility.rs:14:5
2+
--> $DIR/impl-implied-bounds-compatibility.rs:14:35
33
|
44
LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `&'b MessageListeners<'b>`
66
|
77
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
88
= note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
@@ -14,3 +14,18 @@ LL | #![deny(implied_bounds_entailment)]
1414

1515
error: aborting due to previous error
1616

17+
Future incompatibility report: Future breakage diagnostic:
18+
error: impl method assumes more implied bounds than the corresponding trait method
19+
--> $DIR/impl-implied-bounds-compatibility.rs:14:35
20+
|
21+
LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
22+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `&'b MessageListeners<'b>`
23+
|
24+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
25+
= note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
26+
note: the lint level is defined here
27+
--> $DIR/impl-implied-bounds-compatibility.rs:1:9
28+
|
29+
LL | #![deny(implied_bounds_entailment)]
30+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
31+

0 commit comments

Comments
 (0)