Skip to content

Commit aa7c6ba

Browse files
committed
Auto merge of #117984 - compiler-errors:implied-bounds-entailment, r=<try>
Make `IMPLIED_BOUNDS_ENTAILMENT` into a hard error from a lint closes #105572 Removes the `IMPLIED_BOUNDS_ENTAILMENT` and makes the `compare_method_predicate_entailment` logic just run once. r? lcnr
2 parents 00bfd6b + 16f7de6 commit aa7c6ba

8 files changed

+89
-299
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+11-189
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ use super::potentially_plural_count;
22
use crate::errors::LifetimesOrBoundsMismatchOnTrait;
33
use hir::def_id::{DefId, LocalDefId};
44
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
5-
use rustc_errors::{
6-
pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed, MultiSpan,
7-
};
5+
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed};
86
use rustc_hir as hir;
97
use rustc_hir::def::{DefKind, Res};
108
use rustc_hir::intravisit;
@@ -50,13 +48,7 @@ pub(super) fn compare_impl_method<'tcx>(
5048

5149
let _: Result<_, ErrorGuaranteed> = try {
5250
check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, false)?;
53-
compare_method_predicate_entailment(
54-
tcx,
55-
impl_m,
56-
trait_m,
57-
impl_trait_ref,
58-
CheckImpliedWfMode::Check,
59-
)?;
51+
compare_method_predicate_entailment(tcx, impl_m, trait_m, impl_trait_ref)?;
6052
refine::check_refining_return_position_impl_trait_in_trait(
6153
tcx,
6254
impl_m,
@@ -170,7 +162,6 @@ fn compare_method_predicate_entailment<'tcx>(
170162
impl_m: ty::AssocItem,
171163
trait_m: ty::AssocItem,
172164
impl_trait_ref: ty::TraitRef<'tcx>,
173-
check_implied_wf: CheckImpliedWfMode,
174165
) -> Result<(), ErrorGuaranteed> {
175166
let trait_to_impl_args = impl_trait_ref.args;
176167

@@ -317,7 +308,7 @@ fn compare_method_predicate_entailment<'tcx>(
317308
return Err(emitted);
318309
}
319310

320-
if check_implied_wf == CheckImpliedWfMode::Check && !(impl_sig, trait_sig).references_error() {
311+
if !(impl_sig, trait_sig).references_error() {
321312
// Select obligations to make progress on inference before processing
322313
// the wf obligation below.
323314
// FIXME(-Ztrait-solver=next): Not needed when the hack below is removed.
@@ -334,7 +325,8 @@ fn compare_method_predicate_entailment<'tcx>(
334325
// region outlives obligations.
335326
//
336327
// FIXME(-Ztrait-solver=next): Try removing this hack again once
337-
// the new solver is stable.
328+
// the new solver is stable. We should just be able to register a WF pred for
329+
// the fn sig.
338330
let mut wf_args: smallvec::SmallVec<[_; 4]> =
339331
unnormalized_impl_sig.inputs_and_output.iter().map(|ty| ty.into()).collect();
340332
// Annoyingly, asking for the WF predicates of an array (with an unevaluated const (only?))
@@ -357,7 +349,7 @@ fn compare_method_predicate_entailment<'tcx>(
357349
// We need to register Projection oblgiations too, because we may end up with
358350
// an implied `X::Item: 'a`, which gets desugared into `X::Item = ?0`, `?0: 'a`.
359351
// If we only register the region outlives obligation, this leads to an unconstrained var.
360-
// See `implied_bounds_entailment_alias_var` test.
352+
// See `implied_bounds_entailment_alias_var.rs` test.
361353
ty::PredicateKind::Clause(
362354
ty::ClauseKind::RegionOutlives(..)
363355
| ty::ClauseKind::TypeOutlives(..)
@@ -378,26 +370,8 @@ fn compare_method_predicate_entailment<'tcx>(
378370
// version.
379371
let errors = ocx.select_all_or_error();
380372
if !errors.is_empty() {
381-
match check_implied_wf {
382-
CheckImpliedWfMode::Check => {
383-
let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m_def_id);
384-
return compare_method_predicate_entailment(
385-
tcx,
386-
impl_m,
387-
trait_m,
388-
impl_trait_ref,
389-
CheckImpliedWfMode::Skip,
390-
)
391-
.map(|()| {
392-
// If the skip-mode was successful, emit a lint.
393-
emit_implied_wf_lint(infcx.tcx, impl_m, impl_m_hir_id, vec![]);
394-
});
395-
}
396-
CheckImpliedWfMode::Skip => {
397-
let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
398-
return Err(reported);
399-
}
400-
}
373+
let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
374+
return Err(reported);
401375
}
402376

403377
// Finally, resolve all regions. This catches wily misuses of
@@ -408,119 +382,14 @@ fn compare_method_predicate_entailment<'tcx>(
408382
);
409383
let errors = infcx.resolve_regions(&outlives_env);
410384
if !errors.is_empty() {
411-
// FIXME(compiler-errors): This can be simplified when IMPLIED_BOUNDS_ENTAILMENT
412-
// becomes a hard error (i.e. ideally we'd just call `resolve_regions_and_report_errors`
413-
let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m_def_id);
414-
match check_implied_wf {
415-
CheckImpliedWfMode::Check => {
416-
return compare_method_predicate_entailment(
417-
tcx,
418-
impl_m,
419-
trait_m,
420-
impl_trait_ref,
421-
CheckImpliedWfMode::Skip,
422-
)
423-
.map(|()| {
424-
let bad_args = extract_bad_args_for_implies_lint(
425-
tcx,
426-
&errors,
427-
(trait_m, trait_sig),
428-
// Unnormalized impl sig corresponds to the HIR types written
429-
(impl_m, unnormalized_impl_sig),
430-
impl_m_hir_id,
431-
);
432-
// If the skip-mode was successful, emit a lint.
433-
emit_implied_wf_lint(tcx, impl_m, impl_m_hir_id, bad_args);
434-
});
435-
}
436-
CheckImpliedWfMode::Skip => {
437-
if infcx.tainted_by_errors().is_none() {
438-
infcx.err_ctxt().report_region_errors(impl_m_def_id, &errors);
439-
}
440-
return Err(tcx
441-
.sess
442-
.delay_span_bug(rustc_span::DUMMY_SP, "error should have been emitted"));
443-
}
444-
}
385+
return Err(infcx
386+
.tainted_by_errors()
387+
.unwrap_or_else(|| infcx.err_ctxt().report_region_errors(impl_m_def_id, &errors)));
445388
}
446389

447390
Ok(())
448391
}
449392

450-
fn extract_bad_args_for_implies_lint<'tcx>(
451-
tcx: TyCtxt<'tcx>,
452-
errors: &[infer::RegionResolutionError<'tcx>],
453-
(trait_m, trait_sig): (ty::AssocItem, ty::FnSig<'tcx>),
454-
(impl_m, impl_sig): (ty::AssocItem, ty::FnSig<'tcx>),
455-
hir_id: hir::HirId,
456-
) -> Vec<(Span, Option<String>)> {
457-
let mut blame_generics = vec![];
458-
for error in errors {
459-
// Look for the subregion origin that contains an input/output type
460-
let origin = match error {
461-
infer::RegionResolutionError::ConcreteFailure(o, ..) => o,
462-
infer::RegionResolutionError::GenericBoundFailure(o, ..) => o,
463-
infer::RegionResolutionError::SubSupConflict(_, _, o, ..) => o,
464-
infer::RegionResolutionError::UpperBoundUniverseConflict(.., o, _) => o,
465-
};
466-
// Extract (possible) input/output types from origin
467-
match origin {
468-
infer::SubregionOrigin::Subtype(trace) => {
469-
if let Some((a, b)) = trace.values.ty() {
470-
blame_generics.extend([a, b]);
471-
}
472-
}
473-
infer::SubregionOrigin::RelateParamBound(_, ty, _) => blame_generics.push(*ty),
474-
infer::SubregionOrigin::ReferenceOutlivesReferent(ty, _) => blame_generics.push(*ty),
475-
_ => {}
476-
}
477-
}
478-
479-
let fn_decl = tcx.hir().fn_decl_by_hir_id(hir_id).unwrap();
480-
let opt_ret_ty = match fn_decl.output {
481-
hir::FnRetTy::DefaultReturn(_) => None,
482-
hir::FnRetTy::Return(ty) => Some(ty),
483-
};
484-
485-
// Map late-bound regions from trait to impl, so the names are right.
486-
let mapping = std::iter::zip(
487-
tcx.fn_sig(trait_m.def_id).skip_binder().bound_vars(),
488-
tcx.fn_sig(impl_m.def_id).skip_binder().bound_vars(),
489-
)
490-
.filter_map(|(impl_bv, trait_bv)| {
491-
if let ty::BoundVariableKind::Region(impl_bv) = impl_bv
492-
&& let ty::BoundVariableKind::Region(trait_bv) = trait_bv
493-
{
494-
Some((impl_bv, trait_bv))
495-
} else {
496-
None
497-
}
498-
})
499-
.collect();
500-
501-
// For each arg, see if it was in the "blame" of any of the region errors.
502-
// If so, then try to produce a suggestion to replace the argument type with
503-
// one from the trait.
504-
let mut bad_args = vec![];
505-
for (idx, (ty, hir_ty)) in
506-
std::iter::zip(impl_sig.inputs_and_output, fn_decl.inputs.iter().chain(opt_ret_ty))
507-
.enumerate()
508-
{
509-
let expected_ty = trait_sig.inputs_and_output[idx]
510-
.fold_with(&mut RemapLateBound { tcx, mapping: &mapping });
511-
if blame_generics.iter().any(|blame| ty.contains(*blame)) {
512-
let expected_ty_sugg = expected_ty.to_string();
513-
bad_args.push((
514-
hir_ty.span,
515-
// Only suggest something if it actually changed.
516-
(expected_ty_sugg != ty.to_string()).then_some(expected_ty_sugg),
517-
));
518-
}
519-
}
520-
521-
bad_args
522-
}
523-
524393
struct RemapLateBound<'a, 'tcx> {
525394
tcx: TyCtxt<'tcx>,
526395
mapping: &'a FxHashMap<ty::BoundRegionKind, ty::BoundRegionKind>,
@@ -544,53 +413,6 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RemapLateBound<'_, 'tcx> {
544413
}
545414
}
546415

547-
fn emit_implied_wf_lint<'tcx>(
548-
tcx: TyCtxt<'tcx>,
549-
impl_m: ty::AssocItem,
550-
hir_id: hir::HirId,
551-
bad_args: Vec<(Span, Option<String>)>,
552-
) {
553-
let span: MultiSpan = if bad_args.is_empty() {
554-
tcx.def_span(impl_m.def_id).into()
555-
} else {
556-
bad_args.iter().map(|(span, _)| *span).collect::<Vec<_>>().into()
557-
};
558-
tcx.struct_span_lint_hir(
559-
rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT,
560-
hir_id,
561-
span,
562-
"impl method assumes more implied bounds than the corresponding trait method",
563-
|lint| {
564-
let bad_args: Vec<_> =
565-
bad_args.into_iter().filter_map(|(span, sugg)| Some((span, sugg?))).collect();
566-
if !bad_args.is_empty() {
567-
lint.multipart_suggestion(
568-
format!(
569-
"replace {} type{} to make the impl signature compatible",
570-
pluralize!("this", bad_args.len()),
571-
pluralize!(bad_args.len())
572-
),
573-
bad_args,
574-
Applicability::MaybeIncorrect,
575-
);
576-
}
577-
lint
578-
},
579-
);
580-
}
581-
582-
#[derive(Debug, PartialEq, Eq)]
583-
enum CheckImpliedWfMode {
584-
/// Checks implied well-formedness of the impl method. If it fails, we will
585-
/// re-check with `Skip`, and emit a lint if it succeeds.
586-
Check,
587-
/// Skips checking implied well-formedness of the impl method, but will emit
588-
/// a lint if the `compare_method_predicate_entailment` succeeded. This means that
589-
/// the reason that we had failed earlier during `Check` was due to the impl
590-
/// having stronger requirements than the trait.
591-
Skip,
592-
}
593-
594416
fn compare_asyncness<'tcx>(
595417
tcx: TyCtxt<'tcx>,
596418
impl_m: ty::AssocItem,

compiler/rustc_lint_defs/src/builtin.rs

-42
Original file line numberDiff line numberDiff line change
@@ -3387,7 +3387,6 @@ declare_lint_pass! {
33873387
HIDDEN_GLOB_REEXPORTS,
33883388
ILL_FORMED_ATTRIBUTE_INPUT,
33893389
ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
3390-
IMPLIED_BOUNDS_ENTAILMENT,
33913390
INCOMPLETE_INCLUDE,
33923391
INDIRECT_STRUCTURAL_MATCH,
33933392
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
@@ -4232,47 +4231,6 @@ declare_lint! {
42324231
"named arguments in format used positionally"
42334232
}
42344233

4235-
declare_lint! {
4236-
/// The `implied_bounds_entailment` lint detects cases where the arguments of an impl method
4237-
/// have stronger implied bounds than those from the trait method it's implementing.
4238-
///
4239-
/// ### Example
4240-
///
4241-
/// ```rust,compile_fail
4242-
/// #![deny(implied_bounds_entailment)]
4243-
///
4244-
/// trait Trait {
4245-
/// fn get<'s>(s: &'s str, _: &'static &'static ()) -> &'static str;
4246-
/// }
4247-
///
4248-
/// impl Trait for () {
4249-
/// fn get<'s>(s: &'s str, _: &'static &'s ()) -> &'static str {
4250-
/// s
4251-
/// }
4252-
/// }
4253-
///
4254-
/// let val = <() as Trait>::get(&String::from("blah blah blah"), &&());
4255-
/// println!("{}", val);
4256-
/// ```
4257-
///
4258-
/// {{produces}}
4259-
///
4260-
/// ### Explanation
4261-
///
4262-
/// Neither the trait method, which provides no implied bounds about `'s`, nor the impl,
4263-
/// requires the main function to prove that 's: 'static, but the impl method is allowed
4264-
/// to assume that `'s: 'static` within its own body.
4265-
///
4266-
/// This can be used to implement an unsound API if used incorrectly.
4267-
pub IMPLIED_BOUNDS_ENTAILMENT,
4268-
Deny,
4269-
"impl method assumes more implied bounds than its corresponding trait method",
4270-
@future_incompatible = FutureIncompatibleInfo {
4271-
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
4272-
reference: "issue #105572 <https://github.com/rust-lang/rust/issues/105572>",
4273-
};
4274-
}
4275-
42764234
declare_lint! {
42774235
/// The `byte_slice_in_packed_struct_with_derive` lint detects cases where a byte slice field
42784236
/// (`[u8]`) or string slice field (`str`) is used in a `packed` struct that derives one or

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

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#![deny(implied_bounds_entailment)]
2-
31
trait Project {
42
type Ty;
53
}
@@ -11,8 +9,7 @@ trait Trait {
119
}
1210
impl Trait for () {
1311
fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
14-
//~^ ERROR impl method assumes more implied bounds than the corresponding trait method
15-
//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
12+
//~^ ERROR cannot infer an appropriate lifetime for lifetime parameter 's in generic type due to conflicting requirements
1613
s
1714
}
1815
}
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,28 @@
1-
error: impl method assumes more implied bounds than the corresponding trait method
2-
--> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:31
1+
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 's in generic type due to conflicting requirements
2+
--> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:5
33
|
44
LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `()`
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
7-
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
8-
= note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
9-
note: the lint level is defined here
10-
--> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:1:9
11-
|
12-
LL | #![deny(implied_bounds_entailment)]
13-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
14-
15-
error: aborting due to previous error
16-
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
7+
note: first, the lifetime cannot outlive the lifetime `'s` as defined here...
8+
--> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:12
209
|
2110
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: `()`
11+
| ^^
12+
note: ...so that the method type is compatible with trait
13+
--> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:5
2314
|
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
15+
LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
17+
= note: expected `fn(&'s str, ()) -> &'static str`
18+
found `fn(&str, ()) -> &'static str`
19+
= note: but, the lifetime must be valid for the static lifetime...
20+
note: ...so that the reference type `&'static &()` does not outlive the data it points at
21+
--> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:11:5
2822
|
29-
LL | #![deny(implied_bounds_entailment)]
30-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
23+
LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
25+
26+
error: aborting due to previous error
3127

28+
For more information about this error, try `rustc --explain E0495`.

0 commit comments

Comments
 (0)