Skip to content

Commit 0f3042b

Browse files
authored
Unrolled build for rust-lang#132795
Rollup merge of rust-lang#132795 - compiler-errors:refine-rpitit, r=lcnr Check `use<..>` in RPITIT for refinement `#![feature(precise_capturing_in_traits)]` allows users to write `+ use<>` bounds on RPITITs to control what lifetimes are captured by the RPITIT. Since RPITITs currently also warn for refinement in implementations, this PR extends that refinement check for cases where we *undercapture* in an implementation, since that may be indirectly "promising" a more relaxed outlives bound than the impl author intended. For an opaque to be refining, we need to capture *fewer* parameters than those mentioned in the captured params of the trait. For example: ``` trait TypeParam<T> { fn test() -> impl Sized; } // Indirectly capturing a lifetime param through a type param substitution. impl<'a> TypeParam<&'a ()> for i32 { fn test() -> impl Sized + use<> {} //~^ WARN impl trait in impl method captures fewer lifetimes than in trait } ``` Since the opaque in the method (implicitly) captures `use<Self, T>`, and `Self = i32, T = &'a ()` in the impl, we must mention `'a` in our `use<..>` on the impl. Tracking: * rust-lang#130044
2 parents 3fb7e44 + 32d2340 commit 0f3042b

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 = modify the `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: modify the `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: modify the `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: modify the `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)