rustc_borrowck/diagnostics/
opaque_types.rs1#![allow(rustc::diagnostic_outside_of_impl)]
2#![allow(rustc::untranslatable_diagnostic)]
3
4use std::ops::ControlFlow;
5
6use either::Either;
7use itertools::Itertools as _;
8use rustc_data_structures::fx::FxIndexSet;
9use rustc_errors::{Diag, Subdiagnostic};
10use rustc_hir as hir;
11use rustc_hir::def_id::DefId;
12use rustc_middle::mir::{self, ConstraintCategory, Location};
13use rustc_middle::ty::{
14 self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
15};
16use rustc_trait_selection::errors::impl_trait_overcapture_suggestion;
17
18use crate::MirBorrowckCtxt;
19use crate::borrow_set::BorrowData;
20use crate::consumers::RegionInferenceContext;
21use crate::region_infer::opaque_types::DeferredOpaqueTypeError;
22use crate::type_check::Locations;
23
24impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
25 pub(crate) fn report_opaque_type_errors(&mut self, errors: Vec<DeferredOpaqueTypeError<'tcx>>) {
26 if errors.is_empty() {
27 return;
28 }
29 let mut guar = None;
30 for error in errors {
31 guar = Some(match error {
32 DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(self.infcx),
33 DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(err) => {
34 self.infcx.dcx().emit_err(err)
35 }
36 });
37 }
38 let guar = guar.unwrap();
39 self.root_cx.set_tainted_by_errors(guar);
40 self.infcx.set_tainted_by_errors(guar);
41 }
42
43 pub(crate) fn note_due_to_edition_2024_opaque_capture_rules(
48 &self,
49 borrow: &BorrowData<'tcx>,
50 diag: &mut Diag<'_>,
51 ) {
52 for ty in self.body.local_decls.iter().map(|local| local.ty) {
56 if !ty.has_opaque_types() {
57 continue;
58 }
59
60 let tcx = self.infcx.tcx;
61 let ControlFlow::Break((opaque_def_id, offending_region_idx, location)) = ty
62 .visit_with(&mut FindOpaqueRegion {
63 regioncx: &self.regioncx,
64 tcx,
65 borrow_region: borrow.region,
66 })
67 else {
68 continue;
69 };
70
71 if tcx.rendered_precise_capturing_args(opaque_def_id).is_some() {
74 continue;
75 }
76
77 let mut visitor = CheckExplicitRegionMentionAndCollectGenerics {
83 tcx,
84 generics: tcx.generics_of(opaque_def_id),
85 offending_region_idx,
86 seen_opaques: [opaque_def_id].into_iter().collect(),
87 seen_lifetimes: Default::default(),
88 };
89 if tcx
90 .explicit_item_bounds(opaque_def_id)
91 .skip_binder()
92 .visit_with(&mut visitor)
93 .is_break()
94 {
95 continue;
96 }
97
98 match self.body.stmt_at(location) {
101 Either::Right(mir::Terminator { source_info, .. }) => {
102 diag.span_note(
103 source_info.span,
104 "this call may capture more lifetimes than intended, \
105 because Rust 2024 has adjusted the `impl Trait` lifetime capture rules",
106 );
107 let mut captured_args = visitor.seen_lifetimes;
108 let mut next_generics = Some(visitor.generics);
112 let mut any_synthetic = false;
113 while let Some(generics) = next_generics {
114 for param in &generics.own_params {
115 if param.kind.is_ty_or_const() {
116 captured_args.insert(param.def_id);
117 }
118 if param.kind.is_synthetic() {
119 any_synthetic = true;
120 }
121 }
122 next_generics = generics.parent.map(|def_id| tcx.generics_of(def_id));
123 }
124
125 if let Some(opaque_def_id) = opaque_def_id.as_local()
126 && let hir::OpaqueTyOrigin::FnReturn { parent, .. } =
127 tcx.hir_expect_opaque_ty(opaque_def_id).origin
128 {
129 if let Some(sugg) = impl_trait_overcapture_suggestion(
130 tcx,
131 opaque_def_id,
132 parent,
133 captured_args,
134 ) {
135 sugg.add_to_diag(diag);
136 }
137 } else {
138 diag.span_help(
139 tcx.def_span(opaque_def_id),
140 format!(
141 "if you can modify this crate, add a precise \
142 capturing bound to avoid overcapturing: `+ use<{}>`",
143 if any_synthetic {
144 "/* Args */".to_string()
145 } else {
146 captured_args
147 .into_iter()
148 .map(|def_id| tcx.item_name(def_id))
149 .join(", ")
150 }
151 ),
152 );
153 }
154 return;
155 }
156 Either::Left(_) => {}
157 }
158 }
159 }
160}
161
162struct FindOpaqueRegion<'a, 'tcx> {
164 tcx: TyCtxt<'tcx>,
165 regioncx: &'a RegionInferenceContext<'tcx>,
166 borrow_region: ty::RegionVid,
167}
168
169impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
170 type Result = ControlFlow<(DefId, usize, Location), ()>;
171
172 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
173 if let ty::Alias(ty::Opaque, opaque) = *ty.kind()
176 && let hir::OpaqueTyOrigin::FnReturn { parent, in_trait_or_impl: None } =
177 self.tcx.opaque_ty_origin(opaque.def_id)
178 {
179 let variances = self.tcx.variances_of(opaque.def_id);
180 for (idx, (arg, variance)) in std::iter::zip(opaque.args, variances).enumerate() {
181 if *variance == ty::Bivariant {
183 continue;
184 }
185 let Some(opaque_region) = arg.as_region() else {
187 continue;
188 };
189 if opaque_region.is_bound() {
191 continue;
192 }
193 let opaque_region_vid = self.regioncx.to_region_vid(opaque_region);
194
195 if let Some((path, _)) =
197 self.regioncx.find_constraint_paths_between_regions(self.borrow_region, |r| {
198 r == opaque_region_vid
199 })
200 {
201 for constraint in path {
202 if let ConstraintCategory::CallArgument(Some(call_ty)) = constraint.category
204 && let ty::FnDef(call_def_id, _) = *call_ty.kind()
205 && call_def_id == parent
207 && let Locations::Single(location) = constraint.locations
208 {
209 return ControlFlow::Break((opaque.def_id, idx, location));
210 }
211 }
212 }
213 }
214 }
215
216 ty.super_visit_with(self)
217 }
218}
219
220struct CheckExplicitRegionMentionAndCollectGenerics<'tcx> {
221 tcx: TyCtxt<'tcx>,
222 generics: &'tcx ty::Generics,
223 offending_region_idx: usize,
224 seen_opaques: FxIndexSet<DefId>,
225 seen_lifetimes: FxIndexSet<DefId>,
226}
227
228impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CheckExplicitRegionMentionAndCollectGenerics<'tcx> {
229 type Result = ControlFlow<(), ()>;
230
231 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
232 match *ty.kind() {
233 ty::Alias(ty::Opaque, opaque) => {
234 if self.seen_opaques.insert(opaque.def_id) {
235 for (bound, _) in self
236 .tcx
237 .explicit_item_bounds(opaque.def_id)
238 .iter_instantiated_copied(self.tcx, opaque.args)
239 {
240 bound.visit_with(self)?;
241 }
242 }
243 ControlFlow::Continue(())
244 }
245 _ => ty.super_visit_with(self),
246 }
247 }
248
249 fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
250 match r.kind() {
251 ty::ReEarlyParam(param) => {
252 if param.index as usize == self.offending_region_idx {
253 ControlFlow::Break(())
254 } else {
255 self.seen_lifetimes.insert(self.generics.region_param(param, self.tcx).def_id);
256 ControlFlow::Continue(())
257 }
258 }
259 _ => ControlFlow::Continue(()),
260 }
261 }
262}