rustc_borrowck/diagnostics/
opaque_types.rs1use std::ops::ControlFlow;
2
3use either::Either;
4use itertools::Itertools as _;
5use rustc_data_structures::fx::FxIndexSet;
6use rustc_errors::{Diag, Subdiagnostic};
7use rustc_hir as hir;
8use rustc_hir::def_id::DefId;
9use rustc_middle::mir::{self, ConstraintCategory, Location};
10use rustc_middle::ty::{
11 self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
12 Unnormalized,
13};
14use rustc_span::Span;
15use rustc_trait_selection::diagnostics::impl_trait_overcapture_suggestion;
16use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic;
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
30 let infcx = self.infcx;
31 let mut guar = None;
32 let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> =
33 None;
34 for error in errors {
35 guar = Some(match error {
36 DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(infcx),
37 DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(err) => {
38 infcx.dcx().emit_err(err)
39 }
40 DeferredOpaqueTypeError::UnexpectedHiddenRegion {
41 opaque_type_key,
42 hidden_type,
43 member_region,
44 } => {
45 let named_ty =
46 self.regioncx.name_regions_for_member_constraint(infcx.tcx, hidden_type.ty);
47 let named_key = self
48 .regioncx
49 .name_regions_for_member_constraint(infcx.tcx, opaque_type_key);
50 let named_region =
51 self.regioncx.name_regions_for_member_constraint(infcx.tcx, member_region);
52 let diag = unexpected_hidden_region_diagnostic(
53 infcx,
54 self.mir_def_id(),
55 hidden_type.span,
56 named_ty,
57 named_region,
58 named_key,
59 );
60 if last_unexpected_hidden_region
61 != Some((hidden_type.span, named_ty, named_key))
62 {
63 last_unexpected_hidden_region =
64 Some((hidden_type.span, named_ty, named_key));
65 diag.emit()
66 } else {
67 diag.delay_as_bug()
68 }
69 }
70 DeferredOpaqueTypeError::NonDefiningUseInDefiningScope {
71 span,
72 opaque_type_key,
73 } => infcx.dcx().span_err(
74 span,
75 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("non-defining use of `{0}` in the defining scope",
Ty::new_opaque(infcx.tcx, ty::IsRigid::No,
opaque_type_key.def_id.to_def_id(), opaque_type_key.args)))
})format!(
76 "non-defining use of `{}` in the defining scope",
77 Ty::new_opaque(
78 infcx.tcx,
79 ty::IsRigid::No,
80 opaque_type_key.def_id.to_def_id(),
81 opaque_type_key.args
82 )
83 ),
84 ),
85 });
86 }
87 let guar = guar.unwrap();
88 self.root_cx.set_tainted_by_errors(guar);
89 self.infcx.set_tainted_by_errors(guar);
90 }
91
92 pub(crate) fn note_due_to_edition_2024_opaque_capture_rules(
97 &self,
98 borrow: &BorrowData<'tcx>,
99 diag: &mut Diag<'_>,
100 ) {
101 for ty in self.body.local_decls.iter().map(|local| local.ty) {
105 if !ty.has_opaque_types() {
106 continue;
107 }
108
109 let tcx = self.infcx.tcx;
110 let ControlFlow::Break((opaque_def_id, offending_region_idx, location)) = ty
111 .visit_with(&mut FindOpaqueRegion {
112 regioncx: &self.regioncx,
113 tcx,
114 borrow_region: borrow.region,
115 })
116 else {
117 continue;
118 };
119
120 if tcx.rendered_precise_capturing_args(opaque_def_id).is_some() {
123 continue;
124 }
125
126 let mut visitor = CheckExplicitRegionMentionAndCollectGenerics {
132 tcx,
133 generics: tcx.generics_of(opaque_def_id),
134 offending_region_idx,
135 seen_opaques: [opaque_def_id].into_iter().collect(),
136 seen_lifetimes: Default::default(),
137 };
138 if tcx
139 .explicit_item_bounds(opaque_def_id)
140 .skip_binder()
141 .visit_with(&mut visitor)
142 .is_break()
143 {
144 continue;
145 }
146
147 match self.body.stmt_at(location) {
150 Either::Right(mir::Terminator { source_info, .. }) => {
151 diag.span_note(
152 source_info.span,
153 "this call may capture more lifetimes than intended, \
154 because Rust 2024 has adjusted the `impl Trait` lifetime capture rules",
155 );
156 let mut captured_args = visitor.seen_lifetimes;
157 let mut next_generics = Some(visitor.generics);
161 let mut any_synthetic = false;
162 while let Some(generics) = next_generics {
163 for param in &generics.own_params {
164 if param.kind.is_ty_or_const() {
165 captured_args.insert(param.def_id);
166 }
167 if param.kind.is_synthetic() {
168 any_synthetic = true;
169 }
170 }
171 next_generics = generics.parent.map(|def_id| tcx.generics_of(def_id));
172 }
173
174 if let Some(opaque_def_id) = opaque_def_id.as_local()
175 && let hir::OpaqueTyOrigin::FnReturn { parent, .. } =
176 tcx.hir_expect_opaque_ty(opaque_def_id).origin
177 {
178 if let Some(sugg) = impl_trait_overcapture_suggestion(
179 tcx,
180 opaque_def_id,
181 parent,
182 captured_args,
183 ) {
184 sugg.add_to_diag(diag);
185 }
186 } else {
187 diag.span_help(
188 tcx.def_span(opaque_def_id),
189 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("if you can modify this crate, add a precise capturing bound to avoid overcapturing: `+ use<{0}>`",
if any_synthetic {
"/* Args */".to_string()
} else {
captured_args.into_iter().map(|def_id|
tcx.item_name(def_id)).join(", ")
}))
})format!(
190 "if you can modify this crate, add a precise \
191 capturing bound to avoid overcapturing: `+ use<{}>`",
192 if any_synthetic {
193 "/* Args */".to_string()
194 } else {
195 captured_args
196 .into_iter()
197 .map(|def_id| tcx.item_name(def_id))
198 .join(", ")
199 }
200 ),
201 );
202 }
203 return;
204 }
205 Either::Left(_) => {}
206 }
207 }
208 }
209}
210
211struct FindOpaqueRegion<'a, 'tcx> {
213 tcx: TyCtxt<'tcx>,
214 regioncx: &'a RegionInferenceContext<'tcx>,
215 borrow_region: ty::RegionVid,
216}
217
218impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
219 type Result = ControlFlow<(DefId, usize, Location), ()>;
220
221 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
222 if let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = *ty.kind()
225 && let hir::OpaqueTyOrigin::FnReturn { parent, in_trait_or_impl: None } =
226 self.tcx.opaque_ty_origin(def_id)
227 {
228 let variances = self.tcx.variances_of(def_id);
229 for (idx, (arg, variance)) in std::iter::zip(args, variances).enumerate() {
230 if *variance == ty::Bivariant {
232 continue;
233 }
234 let Some(opaque_region) = arg.as_region() else {
236 continue;
237 };
238 if opaque_region.is_bound() {
240 continue;
241 }
242 let opaque_region_vid = self.regioncx.to_region_vid(opaque_region);
243
244 if let Some(path) = self
246 .regioncx
247 .constraint_path_between_regions(self.borrow_region, opaque_region_vid)
248 {
249 for constraint in path {
250 if let ConstraintCategory::CallArgument(Some(call_ty)) = constraint.category
252 && let ty::FnDef(call_def_id, _) = *call_ty.kind()
253 && call_def_id == parent
255 && let Locations::Single(location) = constraint.locations
256 {
257 return ControlFlow::Break((def_id, idx, location));
258 }
259 }
260 }
261 }
262 }
263
264 ty.super_visit_with(self)
265 }
266}
267
268struct CheckExplicitRegionMentionAndCollectGenerics<'tcx> {
269 tcx: TyCtxt<'tcx>,
270 generics: &'tcx ty::Generics,
271 offending_region_idx: usize,
272 seen_opaques: FxIndexSet<DefId>,
273 seen_lifetimes: FxIndexSet<DefId>,
274}
275
276impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CheckExplicitRegionMentionAndCollectGenerics<'tcx> {
277 type Result = ControlFlow<(), ()>;
278
279 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
280 match *ty.kind() {
281 ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => {
282 if self.seen_opaques.insert(def_id) {
283 for (bound, _) in self
284 .tcx
285 .explicit_item_bounds(def_id)
286 .iter_instantiated_copied(self.tcx, args)
287 .map(Unnormalized::skip_norm_wip)
288 {
289 bound.visit_with(self)?;
290 }
291 }
292 ControlFlow::Continue(())
293 }
294 _ => ty.super_visit_with(self),
295 }
296 }
297
298 fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
299 match r.kind() {
300 ty::ReEarlyParam(param) => {
301 if param.index as usize == self.offending_region_idx {
302 ControlFlow::Break(())
303 } else {
304 self.seen_lifetimes.insert(self.generics.region_param(param, self.tcx).def_id);
305 ControlFlow::Continue(())
306 }
307 }
308 _ => ControlFlow::Continue(()),
309 }
310 }
311}