Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 6b1892c

Browse files
committed
Auto merge of #106163 - compiler-errors:🅱-IMPLIED_BOUNDS_ENTAILMENT, r=Mark-Simulacrum
Backport `IMPLIED_BOUNDS_ENTAILMENT` lint r? `@Mark-Simulacrum` as requested #105575 (comment)
2 parents 6711f7a + dc442c5 commit 6b1892c

File tree

9 files changed

+223
-15
lines changed

9 files changed

+223
-15
lines changed

‎compiler/rustc_hir_analysis/src/check/compare_method.rs‎

Lines changed: 98 additions & 12 deletions
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

@@ -251,15 +258,15 @@ fn compare_predicate_entailment<'tcx>(
251258

252259
let mut wf_tys = FxIndexSet::default();
253260

254-
let impl_sig = infcx.replace_bound_vars_with_fresh_vars(
261+
let unnormalized_impl_sig = infcx.replace_bound_vars_with_fresh_vars(
255262
impl_m_span,
256263
infer::HigherRankedType,
257264
tcx.fn_sig(impl_m.def_id),
258265
);
266+
let unnormalized_impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(unnormalized_impl_sig));
259267

260268
let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id);
261-
let impl_sig = ocx.normalize(&norm_cause, param_env, impl_sig);
262-
let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig));
269+
let impl_fty = ocx.normalize(&norm_cause, param_env, unnormalized_impl_fty);
263270
debug!("compare_impl_method: impl_fty={:?}", impl_fty);
264271

265272
let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
@@ -300,29 +307,108 @@ fn compare_predicate_entailment<'tcx>(
300307
return Err(emitted);
301308
}
302309

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+
));
319+
}
320+
let emit_implied_wf_lint = || {
321+
infcx.tcx.struct_span_lint_hir(
322+
rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT,
323+
impl_m_hir_id,
324+
infcx.tcx.def_span(impl_m.def_id),
325+
"impl method assumes more implied bounds than the corresponding trait method",
326+
|lint| lint,
327+
);
328+
};
329+
303330
// Check that all obligations are satisfied by the implementation's
304331
// version.
305332
let errors = ocx.select_all_or_error();
306333
if !errors.is_empty() {
307-
let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
308-
return Err(reported);
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+
}
309354
}
310355

311356
// Finally, resolve all regions. This catches wily misuses of
312357
// lifetime parameters.
313-
let outlives_environment = OutlivesEnvironment::with_bounds(
358+
let outlives_env = OutlivesEnvironment::with_bounds(
314359
param_env,
315360
Some(infcx),
316-
infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys),
361+
infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys.clone()),
317362
);
318-
infcx.check_region_obligations_and_report_errors(
319-
impl_m.def_id.expect_local(),
320-
&outlives_environment,
363+
infcx.process_registered_region_obligations(
364+
outlives_env.region_bound_pairs(),
365+
outlives_env.param_env,
321366
);
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+
}
395+
}
322396

323397
Ok(())
324398
}
325399

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,
410+
}
411+
326412
#[instrument(skip(tcx), level = "debug", ret)]
327413
pub fn collect_trait_impl_trait_tys<'tcx>(
328414
tcx: TyCtxt<'tcx>,

‎compiler/rustc_infer/src/infer/mod.rs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1693,7 +1693,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
16931693
&self,
16941694
generic_param_scope: LocalDefId,
16951695
outlives_env: &OutlivesEnvironment<'tcx>,
1696-
) {
1696+
) -> Option<ErrorGuaranteed>{
16971697
let errors = self.resolve_regions(outlives_env);
16981698

16991699
if let None = self.tainted_by_errors() {
@@ -1704,6 +1704,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
17041704
// errors from silly ones.
17051705
self.report_region_errors(generic_param_scope, &errors);
17061706
}
1707+
1708+
(!errors.is_empty()).then(|| {
1709+
self.tcx.sess.delay_span_bug(rustc_span::DUMMY_SP, "error should have been emitted")
1710+
})
17071711
}
17081712

17091713
// [Note-Type-error-reporting]

‎compiler/rustc_infer/src/infer/outlives/obligations.rs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ use crate::infer::{
6868
};
6969
use crate::traits::{ObligationCause, ObligationCauseCode};
7070
use rustc_data_structures::undo_log::UndoLogs;
71+
use rustc_errors::ErrorGuaranteed;
7172
use rustc_hir::def_id::DefId;
7273
use rustc_hir::def_id::LocalDefId;
7374
use rustc_middle::mir::ConstraintCategory;
@@ -177,7 +178,7 @@ impl<'tcx> InferCtxt<'tcx> {
177178
&self,
178179
generic_param_scope: LocalDefId,
179180
outlives_env: &OutlivesEnvironment<'tcx>,
180-
) {
181+
) -> Option<ErrorGuaranteed>{
181182
self.process_registered_region_obligations(
182183
outlives_env.region_bound_pairs(),
183184
outlives_env.param_env,

‎compiler/rustc_lint_defs/src/builtin.rs‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3270,6 +3270,7 @@ declare_lint_pass! {
32703270
FFI_UNWIND_CALLS,
32713271
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
32723272
NAMED_ARGUMENTS_USED_POSITIONALLY,
3273+
IMPLIED_BOUNDS_ENTAILMENT,
32733274
]
32743275
}
32753276

@@ -3957,3 +3958,44 @@ declare_lint! {
39573958
Warn,
39583959
"named arguments in format used positionally"
39593960
}
3961+
3962+
declare_lint! {
3963+
/// The `implied_bounds_entailment` lint detects cases where the arguments of an impl method
3964+
/// have stronger implied bounds than those from the trait method it's implementing.
3965+
///
3966+
/// ### Example
3967+
///
3968+
/// ```rust,compile_fail
3969+
/// #![deny(implied_bounds_entailment)]
3970+
///
3971+
/// trait Trait {
3972+
/// fn get<'s>(s: &'s str, _: &'static &'static ()) -> &'static str;
3973+
/// }
3974+
///
3975+
/// impl Trait for () {
3976+
/// fn get<'s>(s: &'s str, _: &'static &'s ()) -> &'static str {
3977+
/// s
3978+
/// }
3979+
/// }
3980+
///
3981+
/// let val = <() as Trait>::get(&String::from("blah blah blah"), &&());
3982+
/// println!("{}", val);
3983+
/// ```
3984+
///
3985+
/// {{produces}}
3986+
///
3987+
/// ### Explanation
3988+
///
3989+
/// Neither the trait method, which provides no implied bounds about `'s`, nor the impl,
3990+
/// requires the main function to prove that 's: 'static, but the impl method is allowed
3991+
/// to assume that `'s: 'static` within its own body.
3992+
///
3993+
/// This can be used to implement an unsound API if used incorrectly.
3994+
pub IMPLIED_BOUNDS_ENTAILMENT,
3995+
Warn,
3996+
"impl method assumes more implied bounds than its corresponding trait method",
3997+
@future_incompatible = FutureIncompatibleInfo {
3998+
reference: "issue #105572 <https://github.com/rust-lang/rust/issues/105572>",
3999+
reason: FutureIncompatibilityReason::FutureReleaseError,
4000+
};
4001+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#![deny(implied_bounds_entailment)]
2+
3+
trait Project {
4+
type Ty;
5+
}
6+
impl Project for &'_ &'_ () {
7+
type Ty = ();
8+
}
9+
trait Trait {
10+
fn get<'s>(s: &'s str, _: ()) -> &'static str;
11+
}
12+
impl Trait for () {
13+
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!
16+
s
17+
}
18+
}
19+
fn main() {
20+
let val = <() as Trait>::get(&String::from("blah blah blah"), ());
21+
println!("{}", val);
22+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: impl method assumes more implied bounds than the corresponding trait method
2+
--> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:5
3+
|
4+
LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
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+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![deny(implied_bounds_entailment)]
2+
3+
use std::cell::RefCell;
4+
5+
pub struct MessageListeners<'a> {
6+
listeners: RefCell<Vec<Box<dyn FnMut(()) + 'a>>>,
7+
}
8+
9+
pub trait MessageListenersInterface {
10+
fn listeners<'c>(&'c self) -> &'c MessageListeners<'c>;
11+
}
12+
13+
impl<'a> MessageListenersInterface for MessageListeners<'a> {
14+
fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
15+
//~^ ERROR impl method assumes more implied bounds than the corresponding trait method
16+
//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
17+
self
18+
}
19+
}
20+
21+
fn main() {}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: impl method assumes more implied bounds than the corresponding trait method
2+
--> $DIR/impl-implied-bounds-compatibility.rs:14:5
3+
|
4+
LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
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.rs:1:9
11+
|
12+
LL | #![deny(implied_bounds_entailment)]
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
error: aborting due to previous error
16+

‎src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl<T> StaticRef<T> {
4242
impl<T> std::ops::Deref for StaticRef<T> {
4343
type Target = T;
4444

45-
fn deref(&self) -> &'staticT {
45+
fn deref(&self) -> &T {
4646
unsafe { &*self.ptr }
4747
}
4848
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /