Skip to content

Commit 0995508

Browse files
authored
Rollup merge of #121720 - tmandry:split-refining, r=compiler-errors
Split refining_impl_trait lint into _reachable, _internal variants As discussed in #119535 (comment): > We discussed this today in triage and developed a consensus to: > > * Add a separate lint against impls that refine a return type defined with RPITIT even when the trait is not crate public. > * Place that in a lint group along with the analogous crate public lint. > * Create an issue to solicit feedback on these lints (or perhaps two separate ones). > * Have the warnings displayed with each lint reference this issue in a similar manner to how we do that today with the required `Self: '0'` bound on GATs. > * Make a note to review this feedback on 2-3 release cycles. This points users to #121718 to leave feedback.
2 parents 79c1e58 + c121a26 commit 0995508

File tree

13 files changed

+239
-33
lines changed

13 files changed

+239
-33
lines changed

compiler/rustc_hir_analysis/messages.ftl

+1
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ hir_analysis_rpitit_refined = impl trait in impl method signature does not match
351351
.label = return type from trait method defined here
352352
.unmatched_bound_label = this bound is stronger than that defined on the trait
353353
.note = add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
354+
.feedback_note = we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
354355
355356
hir_analysis_self_in_impl_self =
356357
`Self` is not valid in the self type of an impl block

compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs

+17-16
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxIndexSet;
22
use rustc_hir as hir;
33
use rustc_hir::def_id::DefId;
44
use rustc_infer::infer::{outlives::env::OutlivesEnvironment, TyCtxtInferExt};
5-
use rustc_lint_defs::builtin::REFINING_IMPL_TRAIT;
5+
use rustc_lint_defs::builtin::{REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE};
66
use rustc_middle::traits::{ObligationCause, Reveal};
77
use rustc_middle::ty::{
88
self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperVisitable, TypeVisitable, TypeVisitor,
@@ -23,26 +23,23 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
2323
if !tcx.impl_method_has_trait_impl_trait_tys(impl_m.def_id) {
2424
return;
2525
}
26+
2627
// unreachable traits don't have any library guarantees, there's no need to do this check.
27-
if trait_m
28+
let is_internal = trait_m
2829
.container_id(tcx)
2930
.as_local()
3031
.is_some_and(|trait_def_id| !tcx.effective_visibilities(()).is_reachable(trait_def_id))
31-
{
32-
return;
33-
}
32+
// If a type in the trait ref is private, then there's also no reason to do this check.
33+
|| impl_trait_ref.args.iter().any(|arg| {
34+
if let Some(ty) = arg.as_type()
35+
&& let Some(self_visibility) = type_visibility(tcx, ty)
36+
{
37+
return !self_visibility.is_public();
38+
}
39+
false
40+
});
3441

35-
// If a type in the trait ref is private, then there's also no reason to do this check.
3642
let impl_def_id = impl_m.container_id(tcx);
37-
for arg in impl_trait_ref.args {
38-
if let Some(ty) = arg.as_type()
39-
&& let Some(self_visibility) = type_visibility(tcx, ty)
40-
&& !self_visibility.is_public()
41-
{
42-
return;
43-
}
44-
}
45-
4643
let impl_m_args = ty::GenericArgs::identity_for_item(tcx, impl_m.def_id);
4744
let trait_m_to_impl_m_args = impl_m_args.rebase_onto(tcx, impl_def_id, impl_trait_ref.args);
4845
let bound_trait_m_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_m_to_impl_m_args);
@@ -85,6 +82,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
8582
trait_m.def_id,
8683
impl_m.def_id,
8784
None,
85+
is_internal,
8886
);
8987
return;
9088
};
@@ -104,6 +102,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
104102
trait_m.def_id,
105103
impl_m.def_id,
106104
None,
105+
is_internal,
107106
);
108107
return;
109108
}
@@ -198,6 +197,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
198197
trait_m.def_id,
199198
impl_m.def_id,
200199
Some(span),
200+
is_internal,
201201
);
202202
return;
203203
}
@@ -235,6 +235,7 @@ fn report_mismatched_rpitit_signature<'tcx>(
235235
trait_m_def_id: DefId,
236236
impl_m_def_id: DefId,
237237
unmatched_bound: Option<Span>,
238+
is_internal: bool,
238239
) {
239240
let mapping = std::iter::zip(
240241
tcx.fn_sig(trait_m_def_id).skip_binder().bound_vars(),
@@ -287,7 +288,7 @@ fn report_mismatched_rpitit_signature<'tcx>(
287288

288289
let span = unmatched_bound.unwrap_or(span);
289290
tcx.emit_node_span_lint(
290-
REFINING_IMPL_TRAIT,
291+
if is_internal { REFINING_IMPL_TRAIT_INTERNAL } else { REFINING_IMPL_TRAIT_REACHABLE },
291292
tcx.local_def_id_to_hir_id(impl_m_def_id.expect_local()),
292293
span,
293294
crate::errors::ReturnPositionImplTraitInTraitRefined {

compiler/rustc_hir_analysis/src/errors.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,7 @@ pub struct UnusedAssociatedTypeBounds {
10721072
#[derive(LintDiagnostic)]
10731073
#[diag(hir_analysis_rpitit_refined)]
10741074
#[note]
1075+
#[note(hir_analysis_feedback_note)]
10751076
pub(crate) struct ReturnPositionImplTraitInTraitRefined<'tcx> {
10761077
#[suggestion(applicability = "maybe-incorrect", code = "{pre}{return_ty}{post}")]
10771078
pub impl_return_span: Span,

compiler/rustc_lint/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,12 @@ fn register_builtins(store: &mut LintStore) {
313313
// MACRO_USE_EXTERN_CRATE
314314
);
315315

316+
add_lint_group!(
317+
"refining_impl_trait",
318+
REFINING_IMPL_TRAIT_REACHABLE,
319+
REFINING_IMPL_TRAIT_INTERNAL
320+
);
321+
316322
// Register renamed and removed lints.
317323
store.register_renamed("single_use_lifetime", "single_use_lifetimes");
318324
store.register_renamed("elided_lifetime_in_path", "elided_lifetimes_in_paths");

compiler/rustc_lint_defs/src/builtin.rs

+80-10
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ declare_lint_pass! {
7979
PROC_MACRO_BACK_COMPAT,
8080
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
8181
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
82-
REFINING_IMPL_TRAIT,
82+
REFINING_IMPL_TRAIT_INTERNAL,
83+
REFINING_IMPL_TRAIT_REACHABLE,
8384
RENAMED_AND_REMOVED_LINTS,
8485
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
8586
RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES,
@@ -4402,8 +4403,10 @@ declare_lint! {
44024403
}
44034404

44044405
declare_lint! {
4405-
/// The `refining_impl_trait` lint detects usages of return-position impl
4406-
/// traits in trait signatures which are refined by implementations.
4406+
/// The `refining_impl_trait_reachable` lint detects `impl Trait` return
4407+
/// types in method signatures that are refined by a publically reachable
4408+
/// trait implementation, meaning the implementation adds information about
4409+
/// the return type that is not present in the trait.
44074410
///
44084411
/// ### Example
44094412
///
@@ -4425,21 +4428,88 @@ declare_lint! {
44254428
/// fn main() {
44264429
/// // users can observe that the return type of
44274430
/// // `<&str as AsDisplay>::as_display()` is `&str`.
4428-
/// let x: &str = "".as_display();
4431+
/// let _x: &str = "".as_display();
44294432
/// }
44304433
/// ```
44314434
///
44324435
/// {{produces}}
44334436
///
44344437
/// ### Explanation
44354438
///
4436-
/// Return-position impl trait in traits (RPITITs) desugar to associated types,
4437-
/// and callers of methods for types where the implementation is known are
4439+
/// Callers of methods for types where the implementation is known are
44384440
/// able to observe the types written in the impl signature. This may be
4439-
/// intended behavior, but may also pose a semver hazard for authors of libraries
4440-
/// who do not wish to make stronger guarantees about the types than what is
4441-
/// written in the trait signature.
4442-
pub REFINING_IMPL_TRAIT,
4441+
/// intended behavior, but may also lead to implementation details being
4442+
/// revealed unintentionally. In particular, it may pose a semver hazard
4443+
/// for authors of libraries who do not wish to make stronger guarantees
4444+
/// about the types than what is written in the trait signature.
4445+
///
4446+
/// `refining_impl_trait` is a lint group composed of two lints:
4447+
///
4448+
/// * `refining_impl_trait_reachable`, for refinements that are publically
4449+
/// reachable outside a crate, and
4450+
/// * `refining_impl_trait_internal`, for refinements that are only visible
4451+
/// within a crate.
4452+
///
4453+
/// We are seeking feedback on each of these lints; see issue
4454+
/// [#121718](https://github.com/rust-lang/rust/issues/121718) for more
4455+
/// information.
4456+
pub REFINING_IMPL_TRAIT_REACHABLE,
4457+
Warn,
4458+
"impl trait in impl method signature does not match trait method signature",
4459+
}
4460+
4461+
declare_lint! {
4462+
/// The `refining_impl_trait_internal` lint detects `impl Trait` return
4463+
/// types in method signatures that are refined by a trait implementation,
4464+
/// meaning the implementation adds information about the return type that
4465+
/// is not present in the trait.
4466+
///
4467+
/// ### Example
4468+
///
4469+
/// ```rust,compile_fail
4470+
/// #![deny(refining_impl_trait)]
4471+
///
4472+
/// use std::fmt::Display;
4473+
///
4474+
/// trait AsDisplay {
4475+
/// fn as_display(&self) -> impl Display;
4476+
/// }
4477+
///
4478+
/// impl<'s> AsDisplay for &'s str {
4479+
/// fn as_display(&self) -> Self {
4480+
/// *self
4481+
/// }
4482+
/// }
4483+
///
4484+
/// fn main() {
4485+
/// // users can observe that the return type of
4486+
/// // `<&str as AsDisplay>::as_display()` is `&str`.
4487+
/// let _x: &str = "".as_display();
4488+
/// }
4489+
/// ```
4490+
///
4491+
/// {{produces}}
4492+
///
4493+
/// ### Explanation
4494+
///
4495+
/// Callers of methods for types where the implementation is known are
4496+
/// able to observe the types written in the impl signature. This may be
4497+
/// intended behavior, but may also lead to implementation details being
4498+
/// revealed unintentionally. In particular, it may pose a semver hazard
4499+
/// for authors of libraries who do not wish to make stronger guarantees
4500+
/// about the types than what is written in the trait signature.
4501+
///
4502+
/// `refining_impl_trait` is a lint group composed of two lints:
4503+
///
4504+
/// * `refining_impl_trait_reachable`, for refinements that are publically
4505+
/// reachable outside a crate, and
4506+
/// * `refining_impl_trait_internal`, for refinements that are only visible
4507+
/// within a crate.
4508+
///
4509+
/// We are seeking feedback on each of these lints; see issue
4510+
/// [#121718](https://github.com/rust-lang/rust/issues/121718) for more
4511+
/// information.
4512+
pub REFINING_IMPL_TRAIT_INTERNAL,
44434513
Warn,
44444514
"impl trait in impl method signature does not match trait method signature",
44454515
}

src/tools/lint-docs/src/groups.rs

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[
1616
("rust-2018-compatibility", "Lints used to transition code from the 2015 edition to 2018"),
1717
("rust-2021-compatibility", "Lints used to transition code from the 2018 edition to 2021"),
1818
("rust-2024-compatibility", "Lints used to transition code from the 2021 edition to 2024"),
19+
(
20+
"refining-impl-trait",
21+
"Detects refinement of `impl Trait` return types by trait implementations",
22+
),
1923
];
2024

2125
type LintGroups = BTreeMap<String, BTreeSet<String>>;

tests/ui/async-await/in-trait/async-example-desugared-boxed.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ LL | fn foo(&self) -> Pin<Box<dyn Future<Output = i32> + '_>> {
88
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
99
|
1010
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
11+
= note: we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
1112
note: the lint level is defined here
1213
--> $DIR/async-example-desugared-boxed.rs:13:12
1314
|
1415
LL | #[warn(refining_impl_trait)]
1516
| ^^^^^^^^^^^^^^^^^^^
17+
= note: `#[warn(refining_impl_trait_reachable)]` implied by `#[warn(refining_impl_trait)]`
1618
help: replace the return type so that it matches the trait
1719
|
1820
LL | fn foo(&self) -> impl Future<Output = i32> {

tests/ui/async-await/in-trait/async-example-desugared-manual.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ LL | fn foo(&self) -> MyFuture {
88
| ^^^^^^^^
99
|
1010
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
11+
= note: we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
1112
note: the lint level is defined here
1213
--> $DIR/async-example-desugared-manual.rs:21:12
1314
|
1415
LL | #[warn(refining_impl_trait)]
1516
| ^^^^^^^^^^^^^^^^^^^
17+
= note: `#[warn(refining_impl_trait_reachable)]` implied by `#[warn(refining_impl_trait)]`
1618
help: replace the return type so that it matches the trait
1719
|
1820
LL | fn foo(&self) -> impl Future<Output = i32> {

tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ LL | fn iter(&self) -> impl 'a + Iterator<Item = I::Item<'a>> {
2222
| ^^ this bound is stronger than that defined on the trait
2323
|
2424
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
25-
= note: `#[warn(refining_impl_trait)]` on by default
25+
= note: we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
26+
= note: `#[warn(refining_impl_trait_reachable)]` on by default
2627
help: replace the return type so that it matches the trait
2728
|
2829
LL | fn iter(&self) -> impl Iterator<Item = <Self as Iterable>::Item<'_>> + '_ {

tests/ui/impl-trait/in-trait/foreign.rs

+26-4
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,29 @@ impl Foo for Local {
1717
}
1818
}
1919

20-
struct LocalIgnoreRefining;
21-
impl Foo for LocalIgnoreRefining {
22-
#[deny(refining_impl_trait)]
20+
struct LocalOnlyRefiningA;
21+
impl Foo for LocalOnlyRefiningA {
22+
#[warn(refining_impl_trait)]
23+
fn bar(self) -> Arc<String> {
24+
//~^ WARN impl method signature does not match trait method signature
25+
Arc::new(String::new())
26+
}
27+
}
28+
29+
struct LocalOnlyRefiningB;
30+
impl Foo for LocalOnlyRefiningB {
31+
#[warn(refining_impl_trait)]
32+
#[allow(refining_impl_trait_reachable)]
33+
fn bar(self) -> Arc<String> {
34+
//~^ WARN impl method signature does not match trait method signature
35+
Arc::new(String::new())
36+
}
37+
}
38+
39+
struct LocalOnlyRefiningC;
40+
impl Foo for LocalOnlyRefiningC {
41+
#[warn(refining_impl_trait)]
42+
#[allow(refining_impl_trait_internal)]
2343
fn bar(self) -> Arc<String> {
2444
Arc::new(String::new())
2545
}
@@ -34,5 +54,7 @@ fn main() {
3454
let &() = Foreign.bar();
3555

3656
let x: Arc<String> = Local.bar();
37-
let x: Arc<String> = LocalIgnoreRefining.bar();
57+
let x: Arc<String> = LocalOnlyRefiningA.bar();
58+
let x: Arc<String> = LocalOnlyRefiningB.bar();
59+
let x: Arc<String> = LocalOnlyRefiningC.bar();
3860
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
warning: impl trait in impl method signature does not match trait method signature
2+
--> $DIR/foreign.rs:23:21
3+
|
4+
LL | fn bar(self) -> Arc<String> {
5+
| ^^^^^^^^^^^
6+
|
7+
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
8+
= note: we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
9+
note: the lint level is defined here
10+
--> $DIR/foreign.rs:22:12
11+
|
12+
LL | #[warn(refining_impl_trait)]
13+
| ^^^^^^^^^^^^^^^^^^^
14+
= note: `#[warn(refining_impl_trait_internal)]` implied by `#[warn(refining_impl_trait)]`
15+
help: replace the return type so that it matches the trait
16+
|
17+
LL | fn bar(self) -> impl Deref<Target = impl Sized> {
18+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19+
20+
warning: impl trait in impl method signature does not match trait method signature
21+
--> $DIR/foreign.rs:33:21
22+
|
23+
LL | fn bar(self) -> Arc<String> {
24+
| ^^^^^^^^^^^
25+
|
26+
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
27+
= note: we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
28+
note: the lint level is defined here
29+
--> $DIR/foreign.rs:31:12
30+
|
31+
LL | #[warn(refining_impl_trait)]
32+
| ^^^^^^^^^^^^^^^^^^^
33+
help: replace the return type so that it matches the trait
34+
|
35+
LL | fn bar(self) -> impl Deref<Target = impl Sized> {
36+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
37+
38+
warning: 2 warnings emitted
39+

tests/ui/impl-trait/in-trait/refine.rs

+3
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ impl Foo for C {
2525
struct Private;
2626
impl Foo for Private {
2727
fn bar() -> () {}
28+
//~^ ERROR impl method signature does not match trait method signature
2829
}
2930

3031
pub trait Arg<A> {
3132
fn bar() -> impl Sized;
3233
}
3334
impl Arg<Private> for A {
3435
fn bar() -> () {}
36+
//~^ ERROR impl method signature does not match trait method signature
3537
}
3638

3739
pub trait Late {
@@ -52,6 +54,7 @@ mod unreachable {
5254
struct E;
5355
impl UnreachablePub for E {
5456
fn bar() {}
57+
//~^ ERROR impl method signature does not match trait method signature
5558
}
5659
}
5760

0 commit comments

Comments
 (0)