Skip to content

Commit c24e166

Browse files
committed
Auto merge of rust-lang#132840 - matthiaskrgr:rollup-ibarl2r, r=matthiaskrgr
Rollup of 2 pull requests Successful merges: - rust-lang#132136 (ABI compatibility: remove section on target features) - rust-lang#132816 (Dont suggest `use<impl Trait>` when we have an edition-2024-related borrowck issue) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 6689597 + c1f3c70 commit c24e166

File tree

11 files changed

+323
-105
lines changed

11 files changed

+323
-105
lines changed

compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs

+45-26
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44
use std::ops::ControlFlow;
55

66
use either::Either;
7+
use itertools::Itertools as _;
78
use rustc_data_structures::fx::FxIndexSet;
8-
use rustc_errors::{Applicability, Diag};
9+
use rustc_errors::{Diag, Subdiagnostic};
910
use rustc_hir as hir;
1011
use rustc_hir::def_id::DefId;
1112
use rustc_middle::mir::{self, ConstraintCategory, Location};
1213
use rustc_middle::ty::{
1314
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
1415
};
15-
use rustc_span::Symbol;
16+
use rustc_trait_selection::errors::impl_trait_overcapture_suggestion;
1617

1718
use crate::MirBorrowckCtxt;
1819
use crate::borrow_set::BorrowData;
@@ -61,6 +62,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
6162
// *does* mention. We'll use that for the `+ use<'a>` suggestion below.
6263
let mut visitor = CheckExplicitRegionMentionAndCollectGenerics {
6364
tcx,
65+
generics: tcx.generics_of(opaque_def_id),
6466
offending_region_idx,
6567
seen_opaques: [opaque_def_id].into_iter().collect(),
6668
seen_lifetimes: Default::default(),
@@ -83,34 +85,50 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
8385
"this call may capture more lifetimes than intended, \
8486
because Rust 2024 has adjusted the `impl Trait` lifetime capture rules",
8587
);
86-
let mut seen_generics: Vec<_> =
87-
visitor.seen_lifetimes.iter().map(ToString::to_string).collect();
88-
// Capture all in-scope ty/const params.
89-
seen_generics.extend(
90-
ty::GenericArgs::identity_for_item(tcx, opaque_def_id)
91-
.iter()
92-
.filter(|arg| {
93-
matches!(
94-
arg.unpack(),
95-
ty::GenericArgKind::Type(_) | ty::GenericArgKind::Const(_)
96-
)
97-
})
98-
.map(|arg| arg.to_string()),
99-
);
100-
if opaque_def_id.is_local() {
101-
diag.span_suggestion_verbose(
102-
tcx.def_span(opaque_def_id).shrink_to_hi(),
103-
"add a precise capturing bound to avoid overcapturing",
104-
format!(" + use<{}>", seen_generics.join(", ")),
105-
Applicability::MaybeIncorrect,
106-
);
88+
let mut captured_args = visitor.seen_lifetimes;
89+
// Add in all of the type and const params, too.
90+
// Ordering here is kinda strange b/c we're walking backwards,
91+
// but we're trying to provide *a* suggestion, not a nice one.
92+
let mut next_generics = Some(visitor.generics);
93+
let mut any_synthetic = false;
94+
while let Some(generics) = next_generics {
95+
for param in &generics.own_params {
96+
if param.kind.is_ty_or_const() {
97+
captured_args.insert(param.def_id);
98+
}
99+
if param.kind.is_synthetic() {
100+
any_synthetic = true;
101+
}
102+
}
103+
next_generics = generics.parent.map(|def_id| tcx.generics_of(def_id));
104+
}
105+
106+
if let Some(opaque_def_id) = opaque_def_id.as_local()
107+
&& let hir::OpaqueTyOrigin::FnReturn { parent, .. } =
108+
tcx.hir().expect_opaque_ty(opaque_def_id).origin
109+
{
110+
if let Some(sugg) = impl_trait_overcapture_suggestion(
111+
tcx,
112+
opaque_def_id,
113+
parent,
114+
captured_args,
115+
) {
116+
sugg.add_to_diag(diag);
117+
}
107118
} else {
108119
diag.span_help(
109120
tcx.def_span(opaque_def_id),
110121
format!(
111122
"if you can modify this crate, add a precise \
112123
capturing bound to avoid overcapturing: `+ use<{}>`",
113-
seen_generics.join(", ")
124+
if any_synthetic {
125+
"/* Args */".to_string()
126+
} else {
127+
captured_args
128+
.into_iter()
129+
.map(|def_id| tcx.item_name(def_id))
130+
.join(", ")
131+
}
114132
),
115133
);
116134
}
@@ -182,9 +200,10 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
182200

183201
struct CheckExplicitRegionMentionAndCollectGenerics<'tcx> {
184202
tcx: TyCtxt<'tcx>,
203+
generics: &'tcx ty::Generics,
185204
offending_region_idx: usize,
186205
seen_opaques: FxIndexSet<DefId>,
187-
seen_lifetimes: FxIndexSet<Symbol>,
206+
seen_lifetimes: FxIndexSet<DefId>,
188207
}
189208

190209
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CheckExplicitRegionMentionAndCollectGenerics<'tcx> {
@@ -214,7 +233,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CheckExplicitRegionMentionAndCollectGen
214233
if param.index as usize == self.offending_region_idx {
215234
ControlFlow::Break(())
216235
} else {
217-
self.seen_lifetimes.insert(param.name);
236+
self.seen_lifetimes.insert(self.generics.region_param(param, self.tcx).def_id);
218237
ControlFlow::Continue(())
219238
}
220239
}

compiler/rustc_lint/messages.ftl

-1
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,6 @@ lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than pos
346346
*[other] these lifetimes are
347347
} in scope but not mentioned in the type's bounds
348348
.note2 = all lifetimes in scope will be captured by `impl Trait`s in edition 2024
349-
.suggestion = use the precise capturing `use<...>` syntax to make the captures explicit
350349
351350
lint_impl_trait_redundant_captures = all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
352351
.suggestion = remove the `use<...>` syntax

compiler/rustc_lint/src/impl_trait_overcaptures.rs

+13-35
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::cell::LazyCell;
33

44
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
55
use rustc_data_structures::unord::UnordSet;
6-
use rustc_errors::{Applicability, LintDiagnostic};
6+
use rustc_errors::{LintDiagnostic, Subdiagnostic};
77
use rustc_hir as hir;
88
use rustc_hir::def::DefKind;
99
use rustc_hir::def_id::{DefId, LocalDefId};
@@ -22,6 +22,9 @@ use rustc_session::lint::FutureIncompatibilityReason;
2222
use rustc_session::{declare_lint, declare_lint_pass};
2323
use rustc_span::edition::Edition;
2424
use rustc_span::{Span, Symbol};
25+
use rustc_trait_selection::errors::{
26+
AddPreciseCapturingForOvercapture, impl_trait_overcapture_suggestion,
27+
};
2528
use rustc_trait_selection::traits::ObligationCtxt;
2629
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt;
2730

@@ -334,32 +337,12 @@ where
334337
// If we have uncaptured args, and if the opaque doesn't already have
335338
// `use<>` syntax on it, and we're < edition 2024, then warn the user.
336339
if !uncaptured_args.is_empty() {
337-
let suggestion = if let Ok(snippet) =
338-
self.tcx.sess.source_map().span_to_snippet(opaque_span)
339-
&& snippet.starts_with("impl ")
340-
{
341-
let (lifetimes, others): (Vec<_>, Vec<_>) =
342-
captured.into_iter().partition(|def_id| {
343-
self.tcx.def_kind(*def_id) == DefKind::LifetimeParam
344-
});
345-
// Take all lifetime params first, then all others (ty/ct).
346-
let generics: Vec<_> = lifetimes
347-
.into_iter()
348-
.chain(others)
349-
.map(|def_id| self.tcx.item_name(def_id).to_string())
350-
.collect();
351-
// Make sure that we're not trying to name any APITs
352-
if generics.iter().all(|name| !name.starts_with("impl ")) {
353-
Some((
354-
format!(" + use<{}>", generics.join(", ")),
355-
opaque_span.shrink_to_hi(),
356-
))
357-
} else {
358-
None
359-
}
360-
} else {
361-
None
362-
};
340+
let suggestion = impl_trait_overcapture_suggestion(
341+
self.tcx,
342+
opaque_def_id,
343+
self.parent_def_id,
344+
captured,
345+
);
363346

364347
let uncaptured_spans: Vec<_> = uncaptured_args
365348
.into_iter()
@@ -451,7 +434,7 @@ struct ImplTraitOvercapturesLint<'tcx> {
451434
uncaptured_spans: Vec<Span>,
452435
self_ty: Ty<'tcx>,
453436
num_captured: usize,
454-
suggestion: Option<(String, Span)>,
437+
suggestion: Option<AddPreciseCapturingForOvercapture>,
455438
}
456439

457440
impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
@@ -461,13 +444,8 @@ impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
461444
.arg("num_captured", self.num_captured)
462445
.span_note(self.uncaptured_spans, fluent::lint_note)
463446
.note(fluent::lint_note2);
464-
if let Some((suggestion, span)) = self.suggestion {
465-
diag.span_suggestion(
466-
span,
467-
fluent::lint_suggestion,
468-
suggestion,
469-
Applicability::MachineApplicable,
470-
);
447+
if let Some(suggestion) = self.suggestion {
448+
suggestion.add_to_diag(diag);
471449
}
472450
}
473451
}

compiler/rustc_trait_selection/messages.ftl

+5-1
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@ trait_selection_precise_capturing_new = add a `use<...>` bound to explicitly cap
282282
283283
trait_selection_precise_capturing_new_but_apit = add a `use<...>` bound to explicitly capture `{$new_lifetime}` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate
284284
285+
trait_selection_precise_capturing_overcaptures = use the precise capturing `use<...>` syntax to make the captures explicit
286+
285287
trait_selection_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here...
286288
trait_selection_prlf_defined_without_sub = the lifetime defined here...
287289
trait_selection_prlf_known_limitation = this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
@@ -455,7 +457,9 @@ trait_selection_unable_to_construct_constant_value = unable to construct a const
455457
trait_selection_unknown_format_parameter_for_on_unimplemented_attr = there is no parameter `{$argument_name}` on trait `{$trait_name}`
456458
.help = expect either a generic argument name or {"`{Self}`"} as format argument
457459
458-
trait_selection_warn_removing_apit_params = you could use a `use<...>` bound to explicitly capture `{$new_lifetime}`, but argument-position `impl Trait`s are not nameable
460+
trait_selection_warn_removing_apit_params_for_overcapture = you could use a `use<...>` bound to explicitly specify captures, but argument-position `impl Trait`s are not nameable
461+
462+
trait_selection_warn_removing_apit_params_for_undercapture = you could use a `use<...>` bound to explicitly capture `{$new_lifetime}`, but argument-position `impl Trait`s are not nameable
459463
460464
trait_selection_where_copy_predicates = copy the `where` clause predicates from the trait
461465

compiler/rustc_trait_selection/src/errors.rs

+128-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use std::path::PathBuf;
22

3-
use rustc_data_structures::fx::FxHashSet;
3+
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
44
use rustc_errors::codes::*;
55
use rustc_errors::{
66
Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic,
77
EmissionGuarantee, IntoDiagArg, Level, MultiSpan, SubdiagMessageOp, Subdiagnostic,
88
};
99
use rustc_hir as hir;
10-
use rustc_hir::def_id::LocalDefId;
10+
use rustc_hir::def::DefKind;
11+
use rustc_hir::def_id::{DefId, LocalDefId};
1112
use rustc_hir::intravisit::{Visitor, walk_ty};
1213
use rustc_hir::{FnRetTy, GenericParamKind};
1314
use rustc_macros::{Diagnostic, Subdiagnostic};
@@ -1792,6 +1793,130 @@ impl Subdiagnostic for AddPreciseCapturingAndParams {
17921793
self.suggs,
17931794
Applicability::MaybeIncorrect,
17941795
);
1795-
diag.span_note(self.apit_spans, fluent::trait_selection_warn_removing_apit_params);
1796+
diag.span_note(
1797+
self.apit_spans,
1798+
fluent::trait_selection_warn_removing_apit_params_for_undercapture,
1799+
);
1800+
}
1801+
}
1802+
1803+
/// Given a set of captured `DefId` for an RPIT (opaque_def_id) and a given
1804+
/// function (fn_def_id), try to suggest adding `+ use<...>` to capture just
1805+
/// the specified parameters. If one of those parameters is an APIT, then try
1806+
/// to suggest turning it into a regular type parameter.
1807+
pub fn impl_trait_overcapture_suggestion<'tcx>(
1808+
tcx: TyCtxt<'tcx>,
1809+
opaque_def_id: LocalDefId,
1810+
fn_def_id: LocalDefId,
1811+
captured_args: FxIndexSet<DefId>,
1812+
) -> Option<AddPreciseCapturingForOvercapture> {
1813+
let generics = tcx.generics_of(fn_def_id);
1814+
1815+
let mut captured_lifetimes = FxIndexSet::default();
1816+
let mut captured_non_lifetimes = FxIndexSet::default();
1817+
let mut synthetics = vec![];
1818+
1819+
for arg in captured_args {
1820+
if tcx.def_kind(arg) == DefKind::LifetimeParam {
1821+
captured_lifetimes.insert(tcx.item_name(arg));
1822+
} else {
1823+
let idx = generics.param_def_id_to_index(tcx, arg).expect("expected arg in scope");
1824+
let param = generics.param_at(idx as usize, tcx);
1825+
if param.kind.is_synthetic() {
1826+
synthetics.push((tcx.def_span(arg), param.name));
1827+
} else {
1828+
captured_non_lifetimes.insert(tcx.item_name(arg));
1829+
}
1830+
}
1831+
}
1832+
1833+
let mut next_fresh_param = || {
1834+
["T", "U", "V", "W", "X", "Y", "A", "B", "C"]
1835+
.into_iter()
1836+
.map(Symbol::intern)
1837+
.chain((0..).map(|i| Symbol::intern(&format!("T{i}"))))
1838+
.find(|s| captured_non_lifetimes.insert(*s))
1839+
.unwrap()
1840+
};
1841+
1842+
let mut suggs = vec![];
1843+
let mut apit_spans = vec![];
1844+
1845+
if !synthetics.is_empty() {
1846+
let mut new_params = String::new();
1847+
for (i, (span, name)) in synthetics.into_iter().enumerate() {
1848+
apit_spans.push(span);
1849+
1850+
let fresh_param = next_fresh_param();
1851+
1852+
// Suggest renaming.
1853+
suggs.push((span, fresh_param.to_string()));
1854+
1855+
// Super jank. Turn `impl Trait` into `T: Trait`.
1856+
//
1857+
// This currently involves stripping the `impl` from the name of
1858+
// the parameter, since APITs are always named after how they are
1859+
// rendered in the AST. This sucks! But to recreate the bound list
1860+
// from the APIT itself would be miserable, so we're stuck with
1861+
// this for now!
1862+
if i > 0 {
1863+
new_params += ", ";
1864+
}
1865+
let name_as_bounds = name.as_str().trim_start_matches("impl").trim_start();
1866+
new_params += fresh_param.as_str();
1867+
new_params += ": ";
1868+
new_params += name_as_bounds;
1869+
}
1870+
1871+
let Some(generics) = tcx.hir().get_generics(fn_def_id) else {
1872+
// This shouldn't happen, but don't ICE.
1873+
return None;
1874+
};
1875+
1876+
// Add generics or concatenate to the end of the list.
1877+
suggs.push(if let Some(params_span) = generics.span_for_param_suggestion() {
1878+
(params_span, format!(", {new_params}"))
1879+
} else {
1880+
(generics.span, format!("<{new_params}>"))
1881+
});
1882+
}
1883+
1884+
let concatenated_bounds = captured_lifetimes
1885+
.into_iter()
1886+
.chain(captured_non_lifetimes)
1887+
.map(|sym| sym.to_string())
1888+
.collect::<Vec<_>>()
1889+
.join(", ");
1890+
1891+
suggs.push((
1892+
tcx.def_span(opaque_def_id).shrink_to_hi(),
1893+
format!(" + use<{concatenated_bounds}>"),
1894+
));
1895+
1896+
Some(AddPreciseCapturingForOvercapture { suggs, apit_spans })
1897+
}
1898+
1899+
pub struct AddPreciseCapturingForOvercapture {
1900+
pub suggs: Vec<(Span, String)>,
1901+
pub apit_spans: Vec<Span>,
1902+
}
1903+
1904+
impl Subdiagnostic for AddPreciseCapturingForOvercapture {
1905+
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
1906+
self,
1907+
diag: &mut Diag<'_, G>,
1908+
_f: &F,
1909+
) {
1910+
diag.multipart_suggestion_verbose(
1911+
fluent::trait_selection_precise_capturing_overcaptures,
1912+
self.suggs,
1913+
Applicability::MaybeIncorrect,
1914+
);
1915+
if !self.apit_spans.is_empty() {
1916+
diag.span_note(
1917+
self.apit_spans,
1918+
fluent::trait_selection_warn_removing_apit_params_for_overcapture,
1919+
);
1920+
}
17961921
}
17971922
}

0 commit comments

Comments
 (0)