Skip to content

Commit a6f2821

Browse files
Check use<..> in RPITIT for refinement
1 parent 209799f commit a6f2821

File tree

5 files changed

+204
-1
lines changed

5 files changed

+204
-1
lines changed

compiler/rustc_hir_analysis/messages.ftl

+5
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,11 @@ hir_analysis_rpitit_refined = impl trait in impl method signature does not match
448448
.note = add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
449449
.feedback_note = we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
450450
451+
hir_analysis_rpitit_refined_lifetimes = impl trait in impl method captures fewer lifetimes than in trait
452+
.suggestion = add a `use<..>` bound to capture the same lifetimes that the trait does
453+
.note = add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
454+
.feedback_note = we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
455+
451456
hir_analysis_self_in_impl_self =
452457
`Self` is not valid in the self type of an impl block
453458
.note = replace `Self` with a different type

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

+101-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
use itertools::Itertools as _;
12
use rustc_data_structures::fx::FxIndexSet;
23
use rustc_hir as hir;
3-
use rustc_hir::def_id::DefId;
4+
use rustc_hir::def_id::{DefId, LocalDefId};
45
use rustc_infer::infer::TyCtxtInferExt;
56
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
67
use rustc_lint_defs::builtin::{REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE};
@@ -75,6 +76,8 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
7576
let mut trait_bounds = vec![];
7677
// Bounds that we find on the RPITITs in the impl signature.
7778
let mut impl_bounds = vec![];
79+
// Pairs of trait and impl opaques.
80+
let mut pairs = vec![];
7881

7982
for trait_projection in collector.types.into_iter().rev() {
8083
let impl_opaque_args = trait_projection.args.rebase_onto(tcx, trait_m.def_id, impl_m_args);
@@ -121,6 +124,8 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
121124
tcx.explicit_item_bounds(impl_opaque.def_id)
122125
.iter_instantiated_copied(tcx, impl_opaque.args),
123126
));
127+
128+
pairs.push((trait_projection, impl_opaque));
124129
}
125130

126131
let hybrid_preds = tcx
@@ -212,6 +217,39 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
212217
return;
213218
}
214219
}
220+
221+
// Make sure that the RPITIT doesn't capture fewer regions than
222+
// the trait definition. We hard-error if it captures *more*, since that
223+
// is literally unrepresentable in the type system; however, we may be
224+
// promising stronger outlives guarantees if we capture *fewer* regions.
225+
for (trait_projection, impl_opaque) in pairs {
226+
let impl_variances = tcx.variances_of(impl_opaque.def_id);
227+
let impl_captures: FxIndexSet<_> = impl_opaque
228+
.args
229+
.iter()
230+
.zip_eq(impl_variances)
231+
.filter(|(_, v)| **v == ty::Invariant)
232+
.map(|(arg, _)| arg)
233+
.collect();
234+
235+
let trait_variances = tcx.variances_of(trait_projection.def_id);
236+
let mut trait_captures = FxIndexSet::default();
237+
for (arg, variance) in trait_projection.args.iter().zip_eq(trait_variances) {
238+
if *variance != ty::Invariant {
239+
continue;
240+
}
241+
arg.visit_with(&mut CollectParams { params: &mut trait_captures });
242+
}
243+
244+
if !trait_captures.iter().all(|arg| impl_captures.contains(arg)) {
245+
report_mismatched_rpitit_captures(
246+
tcx,
247+
impl_opaque.def_id.expect_local(),
248+
trait_captures,
249+
is_internal,
250+
);
251+
}
252+
}
215253
}
216254

217255
struct ImplTraitInTraitCollector<'tcx> {
@@ -342,3 +380,65 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Anonymize<'tcx> {
342380
self.tcx.anonymize_bound_vars(t)
343381
}
344382
}
383+
384+
struct CollectParams<'a, 'tcx> {
385+
params: &'a mut FxIndexSet<ty::GenericArg<'tcx>>,
386+
}
387+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CollectParams<'_, 'tcx> {
388+
fn visit_ty(&mut self, ty: Ty<'tcx>) {
389+
if let ty::Param(_) = ty.kind() {
390+
self.params.insert(ty.into());
391+
} else {
392+
ty.super_visit_with(self);
393+
}
394+
}
395+
fn visit_region(&mut self, r: ty::Region<'tcx>) {
396+
match r.kind() {
397+
ty::ReEarlyParam(_) | ty::ReLateParam(_) => {
398+
self.params.insert(r.into());
399+
}
400+
_ => {}
401+
}
402+
}
403+
fn visit_const(&mut self, ct: ty::Const<'tcx>) {
404+
if let ty::ConstKind::Param(_) = ct.kind() {
405+
self.params.insert(ct.into());
406+
} else {
407+
ct.super_visit_with(self);
408+
}
409+
}
410+
}
411+
412+
fn report_mismatched_rpitit_captures<'tcx>(
413+
tcx: TyCtxt<'tcx>,
414+
impl_opaque_def_id: LocalDefId,
415+
mut trait_captured_args: FxIndexSet<ty::GenericArg<'tcx>>,
416+
is_internal: bool,
417+
) {
418+
let Some(use_bound_span) =
419+
tcx.hir_node_by_def_id(impl_opaque_def_id).expect_opaque_ty().bounds.iter().find_map(
420+
|bound| match *bound {
421+
rustc_hir::GenericBound::Use(_, span) => Some(span),
422+
hir::GenericBound::Trait(_) | hir::GenericBound::Outlives(_) => None,
423+
},
424+
)
425+
else {
426+
// I have no idea when you would ever undercapture without a `use<..>`.
427+
tcx.dcx().delayed_bug("expected use<..> to undercapture in an impl opaque");
428+
return;
429+
};
430+
431+
trait_captured_args
432+
.sort_by_cached_key(|arg| !matches!(arg.unpack(), ty::GenericArgKind::Lifetime(_)));
433+
let suggestion = format!("use<{}>", trait_captured_args.iter().join(", "));
434+
435+
tcx.emit_node_span_lint(
436+
if is_internal { REFINING_IMPL_TRAIT_INTERNAL } else { REFINING_IMPL_TRAIT_REACHABLE },
437+
tcx.local_def_id_to_hir_id(impl_opaque_def_id),
438+
use_bound_span,
439+
crate::errors::ReturnPositionImplTraitInTraitRefinedLifetimes {
440+
suggestion_span: use_bound_span,
441+
suggestion,
442+
},
443+
);
444+
}

compiler/rustc_hir_analysis/src/errors.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,16 @@ pub(crate) struct ReturnPositionImplTraitInTraitRefined<'tcx> {
11531153
pub return_ty: Ty<'tcx>,
11541154
}
11551155

1156+
#[derive(LintDiagnostic)]
1157+
#[diag(hir_analysis_rpitit_refined_lifetimes)]
1158+
#[note]
1159+
#[note(hir_analysis_feedback_note)]
1160+
pub(crate) struct ReturnPositionImplTraitInTraitRefinedLifetimes {
1161+
#[suggestion(applicability = "maybe-incorrect", code = "{suggestion}")]
1162+
pub suggestion_span: Span,
1163+
pub suggestion: String,
1164+
}
1165+
11561166
#[derive(Diagnostic)]
11571167
#[diag(hir_analysis_inherent_ty_outside, code = E0390)]
11581168
#[help]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#![feature(precise_capturing_in_traits)]
2+
3+
trait LifetimeParam<'a> {
4+
fn test() -> impl Sized;
5+
}
6+
// Refining via capturing fewer lifetimes than the trait definition.
7+
impl<'a> LifetimeParam<'a> for i32 {
8+
fn test() -> impl Sized + use<> {}
9+
//~^ WARN impl trait in impl method captures fewer lifetimes than in trait
10+
}
11+
// If the lifetime is substituted, then we don't refine anything.
12+
impl LifetimeParam<'static> for u32 {
13+
fn test() -> impl Sized + use<> {}
14+
// Ok
15+
}
16+
17+
trait TypeParam<T> {
18+
fn test() -> impl Sized;
19+
}
20+
// Indirectly capturing a lifetime param through a type param substitution.
21+
impl<'a> TypeParam<&'a ()> for i32 {
22+
fn test() -> impl Sized + use<> {}
23+
//~^ WARN impl trait in impl method captures fewer lifetimes than in trait
24+
}
25+
// Two of them, but only one is captured...
26+
impl<'a, 'b> TypeParam<(&'a (), &'b ())> for u32 {
27+
fn test() -> impl Sized + use<'b> {}
28+
//~^ WARN impl trait in impl method captures fewer lifetimes than in trait
29+
}
30+
// What if we don't capture a type param? That should be an error otherwise.
31+
impl<T> TypeParam<T> for u64 {
32+
fn test() -> impl Sized + use<> {}
33+
//~^ ERROR `impl Trait` must mention all type parameters in scope in `use<...>`
34+
}
35+
36+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
warning: impl trait in impl method captures fewer lifetimes than in trait
2+
--> $DIR/refine-captures.rs:8:31
3+
|
4+
LL | fn test() -> impl Sized + use<> {}
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: `#[warn(refining_impl_trait_internal)]` on by default
10+
help: add a `use<..>` bound to capture the same lifetimes that the trait does
11+
|
12+
LL | fn test() -> impl Sized + use<'a> {}
13+
| ~~~~~~~
14+
15+
warning: impl trait in impl method captures fewer lifetimes than in trait
16+
--> $DIR/refine-captures.rs:22:31
17+
|
18+
LL | fn test() -> impl Sized + use<> {}
19+
| ^^^^^
20+
|
21+
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
22+
= note: we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
23+
help: add a `use<..>` bound to capture the same lifetimes that the trait does
24+
|
25+
LL | fn test() -> impl Sized + use<'a> {}
26+
| ~~~~~~~
27+
28+
warning: impl trait in impl method captures fewer lifetimes than in trait
29+
--> $DIR/refine-captures.rs:27:31
30+
|
31+
LL | fn test() -> impl Sized + use<'b> {}
32+
| ^^^^^^^
33+
|
34+
= note: add `#[allow(refining_impl_trait)]` if it is intended for this to be part of the public API of this crate
35+
= note: we are soliciting feedback, see issue #121718 <https://github.com/rust-lang/rust/issues/121718> for more information
36+
help: add a `use<..>` bound to capture the same lifetimes that the trait does
37+
|
38+
LL | fn test() -> impl Sized + use<'a, 'b> {}
39+
| ~~~~~~~~~~~
40+
41+
error: `impl Trait` must mention all type parameters in scope in `use<...>`
42+
--> $DIR/refine-captures.rs:32:18
43+
|
44+
LL | impl<T> TypeParam<T> for u64 {
45+
| - type parameter is implicitly captured by this `impl Trait`
46+
LL | fn test() -> impl Sized + use<> {}
47+
| ^^^^^^^^^^^^^^^^^^
48+
|
49+
= note: currently, all type parameters are required to be mentioned in the precise captures list
50+
51+
error: aborting due to 1 previous error; 3 warnings emitted
52+

0 commit comments

Comments
 (0)