]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/ty/closure.rs
New upstream version 1.64.0+dfsg1
[rustc.git] / compiler / rustc_middle / src / ty / closure.rs
CommitLineData
6a06907d
XL
1use crate::hir::place::{
2 Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
3};
17df50a5 4use crate::{mir, ty};
6a06907d 5
94222f64
XL
6use std::fmt::Write;
7
6a06907d
XL
8use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
9use rustc_hir as hir;
10use rustc_hir::def_id::{DefId, LocalDefId};
94222f64 11use rustc_span::{Span, Symbol};
6a06907d
XL
12
13use super::{Ty, TyCtxt};
14
15use self::BorrowKind::*;
16
17df50a5
XL
17// Captures are represented using fields inside a structure.
18// This represents accessing self in the closure structure
19pub const CAPTURE_STRUCT_LOCAL: mir::Local = mir::Local::from_u32(1);
20
064997fb
FG
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
22#[derive(TypeFoldable, TypeVisitable)]
6a06907d
XL
23pub struct UpvarPath {
24 pub hir_id: hir::HirId,
25}
26
27/// Upvars do not get their own `NodeId`. Instead, we use the pair of
28/// the original var ID (that is, the root variable that is referenced
29/// by the upvar) and the ID of the closure expression.
064997fb
FG
30#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
31#[derive(TypeFoldable, TypeVisitable)]
6a06907d
XL
32pub struct UpvarId {
33 pub var_path: UpvarPath,
34 pub closure_expr_id: LocalDefId,
35}
36
37impl UpvarId {
38 pub fn new(var_hir_id: hir::HirId, closure_def_id: LocalDefId) -> UpvarId {
39 UpvarId { var_path: UpvarPath { hir_id: var_hir_id }, closure_expr_id: closure_def_id }
40 }
41}
42
43/// Information describing the capture of an upvar. This is computed
44/// during `typeck`, specifically by `regionck`.
064997fb
FG
45#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)]
46#[derive(TypeFoldable, TypeVisitable)]
5099ac24 47pub enum UpvarCapture {
6a06907d
XL
48 /// Upvar is captured by value. This is always true when the
49 /// closure is labeled `move`, but can also be true in other cases
50 /// depending on inference.
5099ac24 51 ByValue,
6a06907d
XL
52
53 /// Upvar is captured by reference.
5099ac24 54 ByRef(BorrowKind),
6a06907d
XL
55}
56
57pub type UpvarListMap = FxHashMap<DefId, FxIndexMap<hir::HirId, UpvarId>>;
5099ac24 58pub type UpvarCaptureMap = FxHashMap<UpvarId, UpvarCapture>;
6a06907d
XL
59
60/// Given the closure DefId this map provides a map of root variables to minimum
61/// set of `CapturedPlace`s that need to be tracked to support all captures of that closure.
064997fb 62pub type MinCaptureInformationMap<'tcx> = FxHashMap<LocalDefId, RootVariableMinCaptureList<'tcx>>;
6a06907d
XL
63
64/// Part of `MinCaptureInformationMap`; Maps a root variable to the list of `CapturedPlace`.
65/// Used to track the minimum set of `Place`s that need to be captured to support all
66/// Places captured by the closure starting at a given root variable.
67///
68/// This provides a convenient and quick way of checking if a variable being used within
69/// a closure is a capture of a local variable.
70pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureList<'tcx>>;
71
72/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s.
73pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
74
75/// Represents the various closure traits in the language. This
76/// will determine the type of the environment (`self`, in the
77/// desugaring) argument that the closure expects.
78///
79/// You can get the environment type of a closure using
80/// `tcx.closure_env_ty()`.
81#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
82#[derive(HashStable)]
83pub enum ClosureKind {
84 // Warning: Ordering is significant here! The ordering is chosen
85 // because the trait Fn is a subtrait of FnMut and so in turn, and
86 // hence we order it so that Fn < FnMut < FnOnce.
87 Fn,
88 FnMut,
89 FnOnce,
90}
91
92impl<'tcx> ClosureKind {
93 // This is the initial value used when doing upvar inference.
94 pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn;
95
6a06907d
XL
96 /// Returns `true` if a type that impls this closure kind
97 /// must also implement `other`.
98 pub fn extends(self, other: ty::ClosureKind) -> bool {
99 matches!(
100 (self, other),
101 (ClosureKind::Fn, ClosureKind::Fn)
102 | (ClosureKind::Fn, ClosureKind::FnMut)
103 | (ClosureKind::Fn, ClosureKind::FnOnce)
104 | (ClosureKind::FnMut, ClosureKind::FnMut)
105 | (ClosureKind::FnMut, ClosureKind::FnOnce)
106 | (ClosureKind::FnOnce, ClosureKind::FnOnce)
107 )
108 }
109
110 /// Returns the representative scalar type for this closure kind.
5099ac24 111 /// See `Ty::to_opt_closure_kind` for more details.
6a06907d
XL
112 pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
113 match self {
5e7ed085
FG
114 ClosureKind::Fn => tcx.types.i8,
115 ClosureKind::FnMut => tcx.types.i16,
116 ClosureKind::FnOnce => tcx.types.i32,
117 }
118 }
119
120 pub fn from_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ClosureKind> {
121 if Some(def_id) == tcx.lang_items().fn_once_trait() {
122 Some(ClosureKind::FnOnce)
123 } else if Some(def_id) == tcx.lang_items().fn_mut_trait() {
124 Some(ClosureKind::FnMut)
125 } else if Some(def_id) == tcx.lang_items().fn_trait() {
126 Some(ClosureKind::Fn)
127 } else {
128 None
6a06907d
XL
129 }
130 }
064997fb
FG
131
132 pub fn to_def_id(&self, tcx: TyCtxt<'_>) -> DefId {
133 match self {
134 ClosureKind::Fn => tcx.lang_items().fn_once_trait().unwrap(),
135 ClosureKind::FnMut => tcx.lang_items().fn_mut_trait().unwrap(),
136 ClosureKind::FnOnce => tcx.lang_items().fn_trait().unwrap(),
137 }
138 }
6a06907d
XL
139}
140
141/// A composite describing a `Place` that is captured by a closure.
064997fb
FG
142#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
143#[derive(TypeFoldable, TypeVisitable)]
6a06907d
XL
144pub struct CapturedPlace<'tcx> {
145 /// The `Place` that is captured.
146 pub place: HirPlace<'tcx>,
147
148 /// `CaptureKind` and expression(s) that resulted in such capture of `place`.
5099ac24 149 pub info: CaptureInfo,
6a06907d
XL
150
151 /// Represents if `place` can be mutated or not.
152 pub mutability: hir::Mutability,
5099ac24
FG
153
154 /// Region of the resulting reference if the upvar is captured by ref.
155 pub region: Option<ty::Region<'tcx>>,
6a06907d
XL
156}
157
a2a8927a 158impl<'tcx> CapturedPlace<'tcx> {
17df50a5
XL
159 pub fn to_string(&self, tcx: TyCtxt<'tcx>) -> String {
160 place_to_string_for_capture(tcx, &self.place)
161 }
162
94222f64
XL
163 /// Returns a symbol of the captured upvar, which looks like `name__field1__field2`.
164 fn to_symbol(&self, tcx: TyCtxt<'tcx>) -> Symbol {
165 let hir_id = match self.place.base {
166 HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
167 base => bug!("Expected an upvar, found {:?}", base),
168 };
169 let mut symbol = tcx.hir().name(hir_id).as_str().to_string();
170
171 let mut ty = self.place.base_ty;
172 for proj in self.place.projections.iter() {
173 match proj.kind {
174 HirProjectionKind::Field(idx, variant) => match ty.kind() {
175 ty::Tuple(_) => write!(&mut symbol, "__{}", idx).unwrap(),
176 ty::Adt(def, ..) => {
177 write!(
178 &mut symbol,
179 "__{}",
5e7ed085 180 def.variant(variant).fields[idx as usize].name.as_str(),
94222f64
XL
181 )
182 .unwrap();
183 }
184 ty => {
064997fb
FG
185 span_bug!(
186 self.get_capture_kind_span(tcx),
187 "Unexpected type {:?} for `Field` projection",
188 ty
189 )
94222f64
XL
190 }
191 },
192
193 // Ignore derefs for now, as they are likely caused by
194 // autoderefs that don't appear in the original code.
195 HirProjectionKind::Deref => {}
196 proj => bug!("Unexpected projection {:?} in captured place", proj),
197 }
198 ty = proj.ty;
199 }
200
201 Symbol::intern(&symbol)
202 }
203
6a06907d
XL
204 /// Returns the hir-id of the root variable for the captured place.
205 /// e.g., if `a.b.c` was captured, would return the hir-id for `a`.
206 pub fn get_root_variable(&self) -> hir::HirId {
207 match self.place.base {
208 HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
209 base => bug!("Expected upvar, found={:?}", base),
210 }
211 }
212
cdc7bbd5 213 /// Returns the `LocalDefId` of the closure that captured this Place
6a06907d
XL
214 pub fn get_closure_local_def_id(&self) -> LocalDefId {
215 match self.place.base {
216 HirPlaceBase::Upvar(upvar_id) => upvar_id.closure_expr_id,
217 base => bug!("expected upvar, found={:?}", base),
218 }
219 }
220
17df50a5
XL
221 /// Return span pointing to use that resulted in selecting the captured path
222 pub fn get_path_span(&self, tcx: TyCtxt<'tcx>) -> Span {
223 if let Some(path_expr_id) = self.info.path_expr_id {
224 tcx.hir().span(path_expr_id)
225 } else if let Some(capture_kind_expr_id) = self.info.capture_kind_expr_id {
226 tcx.hir().span(capture_kind_expr_id)
227 } else {
228 // Fallback on upvars mentioned if neither path or capture expr id is captured
229
230 // Safe to unwrap since we know this place is captured by the closure, therefore the closure must have upvars.
231 tcx.upvars_mentioned(self.get_closure_local_def_id()).unwrap()
232 [&self.get_root_variable()]
233 .span
234 }
235 }
236
6a06907d
XL
237 /// Return span pointing to use that resulted in selecting the current capture kind
238 pub fn get_capture_kind_span(&self, tcx: TyCtxt<'tcx>) -> Span {
239 if let Some(capture_kind_expr_id) = self.info.capture_kind_expr_id {
240 tcx.hir().span(capture_kind_expr_id)
241 } else if let Some(path_expr_id) = self.info.path_expr_id {
242 tcx.hir().span(path_expr_id)
243 } else {
244 // Fallback on upvars mentioned if neither path or capture expr id is captured
245
246 // Safe to unwrap since we know this place is captured by the closure, therefore the closure must have upvars.
247 tcx.upvars_mentioned(self.get_closure_local_def_id()).unwrap()
248 [&self.get_root_variable()]
249 .span
250 }
251 }
252}
253
94222f64
XL
254fn symbols_for_closure_captures<'tcx>(
255 tcx: TyCtxt<'tcx>,
064997fb 256 def_id: (LocalDefId, LocalDefId),
94222f64
XL
257) -> Vec<Symbol> {
258 let typeck_results = tcx.typeck(def_id.0);
259 let captures = typeck_results.closure_min_captures_flattened(def_id.1);
260 captures.into_iter().map(|captured_place| captured_place.to_symbol(tcx)).collect()
261}
262
6a06907d
XL
263/// Return true if the `proj_possible_ancestor` represents an ancestor path
264/// to `proj_capture` or `proj_possible_ancestor` is same as `proj_capture`,
265/// assuming they both start off of the same root variable.
266///
267/// **Note:** It's the caller's responsibility to ensure that both lists of projections
268/// start off of the same root variable.
269///
270/// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of
271/// `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`.
272/// Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`.
273/// 2. Since we only look at the projections here function will return `bar.x` as an a valid
274/// ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections
275/// list are being applied to the same root variable.
276pub fn is_ancestor_or_same_capture(
277 proj_possible_ancestor: &[HirProjectionKind],
278 proj_capture: &[HirProjectionKind],
279) -> bool {
280 // We want to make sure `is_ancestor_or_same_capture("x.0.0", "x.0")` to return false.
281 // Therefore we can't just check if all projections are same in the zipped iterator below.
282 if proj_possible_ancestor.len() > proj_capture.len() {
283 return false;
284 }
285
286 proj_possible_ancestor.iter().zip(proj_capture).all(|(a, b)| a == b)
287}
288
289/// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move)
290/// for a particular capture as well as identifying the part of the source code
291/// that triggered this capture to occur.
064997fb
FG
292#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)]
293#[derive(TypeFoldable, TypeVisitable)]
5099ac24 294pub struct CaptureInfo {
6a06907d
XL
295 /// Expr Id pointing to use that resulted in selecting the current capture kind
296 ///
297 /// Eg:
298 /// ```rust,no_run
299 /// let mut t = (0,1);
300 ///
301 /// let c = || {
04454e1e 302 /// println!("{t:?}"); // L1
6a06907d
XL
303 /// t.1 = 4; // L2
304 /// };
305 /// ```
306 /// `capture_kind_expr_id` will point to the use on L2 and `path_expr_id` will point to the
307 /// use on L1.
308 ///
309 /// If the user doesn't enable feature `capture_disjoint_fields` (RFC 2229) then, it is
310 /// possible that we don't see the use of a particular place resulting in capture_kind_expr_id being
311 /// None. In such case we fallback on uvpars_mentioned for span.
312 ///
313 /// Eg:
314 /// ```rust,no_run
315 /// let x = 5;
316 ///
317 /// let c = || {
04454e1e 318 /// let _ = x;
6a06907d
XL
319 /// };
320 /// ```
321 ///
322 /// In this example, if `capture_disjoint_fields` is **not** set, then x will be captured,
323 /// but we won't see it being used during capture analysis, since it's essentially a discard.
324 pub capture_kind_expr_id: Option<hir::HirId>,
325 /// Expr Id pointing to use that resulted the corresponding place being captured
326 ///
327 /// See `capture_kind_expr_id` for example.
328 ///
329 pub path_expr_id: Option<hir::HirId>,
330
331 /// Capture mode that was selected
5099ac24 332 pub capture_kind: UpvarCapture,
6a06907d
XL
333}
334
a2a8927a 335pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String {
136023e0 336 let mut curr_string: String = match place.base {
6a06907d
XL
337 HirPlaceBase::Upvar(upvar_id) => tcx.hir().name(upvar_id.var_path.hir_id).to_string(),
338 _ => bug!("Capture_information should only contain upvars"),
339 };
6a06907d
XL
340
341 for (i, proj) in place.projections.iter().enumerate() {
342 match proj.kind {
343 HirProjectionKind::Deref => {
344 curr_string = format!("*{}", curr_string);
345 }
346 HirProjectionKind::Field(idx, variant) => match place.ty_before_projection(i).kind() {
347 ty::Adt(def, ..) => {
348 curr_string = format!(
349 "{}.{}",
350 curr_string,
5e7ed085 351 def.variant(variant).fields[idx as usize].name.as_str()
6a06907d
XL
352 );
353 }
354 ty::Tuple(_) => {
355 curr_string = format!("{}.{}", curr_string, idx);
356 }
357 _ => {
358 bug!(
359 "Field projection applied to a type other than Adt or Tuple: {:?}.",
360 place.ty_before_projection(i).kind()
361 )
362 }
363 },
364 proj => bug!("{:?} unexpected because it isn't captured", proj),
365 }
366 }
367
136023e0 368 curr_string
6a06907d
XL
369}
370
064997fb
FG
371#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)]
372#[derive(TypeFoldable, TypeVisitable)]
6a06907d
XL
373pub enum BorrowKind {
374 /// Data must be immutable and is aliasable.
375 ImmBorrow,
376
377 /// Data must be immutable but not aliasable. This kind of borrow
378 /// cannot currently be expressed by the user and is used only in
379 /// implicit closure bindings. It is needed when the closure
380 /// is borrowing or mutating a mutable referent, e.g.:
381 ///
382 /// ```
04454e1e
FG
383 /// let mut z = 3;
384 /// let x: &mut isize = &mut z;
6a06907d
XL
385 /// let y = || *x += 5;
386 /// ```
387 ///
388 /// If we were to try to translate this closure into a more explicit
389 /// form, we'd encounter an error with the code as written:
390 ///
04454e1e
FG
391 /// ```compile_fail,E0594
392 /// struct Env<'a> { x: &'a &'a mut isize }
393 /// let mut z = 3;
394 /// let x: &mut isize = &mut z;
395 /// let y = (&mut Env { x: &x }, fn_ptr); // Closure is pair of env and fn
6a06907d
XL
396 /// fn fn_ptr(env: &mut Env) { **env.x += 5; }
397 /// ```
398 ///
399 /// This is then illegal because you cannot mutate a `&mut` found
400 /// in an aliasable location. To solve, you'd have to translate with
401 /// an `&mut` borrow:
402 ///
04454e1e
FG
403 /// ```compile_fail,E0596
404 /// struct Env<'a> { x: &'a mut &'a mut isize }
405 /// let mut z = 3;
406 /// let x: &mut isize = &mut z;
407 /// let y = (&mut Env { x: &mut x }, fn_ptr); // changed from &x to &mut x
6a06907d
XL
408 /// fn fn_ptr(env: &mut Env) { **env.x += 5; }
409 /// ```
410 ///
411 /// Now the assignment to `**env.x` is legal, but creating a
412 /// mutable pointer to `x` is not because `x` is not mutable. We
413 /// could fix this by declaring `x` as `let mut x`. This is ok in
414 /// user code, if awkward, but extra weird for closures, since the
415 /// borrow is hidden.
416 ///
417 /// So we introduce a "unique imm" borrow -- the referent is
418 /// immutable, but not aliasable. This solves the problem. For
419 /// simplicity, we don't give users the way to express this
420 /// borrow, it's just used when translating closures.
421 UniqueImmBorrow,
422
423 /// Data is mutable and not aliasable.
424 MutBorrow,
425}
426
427impl BorrowKind {
428 pub fn from_mutbl(m: hir::Mutability) -> BorrowKind {
429 match m {
430 hir::Mutability::Mut => MutBorrow,
431 hir::Mutability::Not => ImmBorrow,
432 }
433 }
434
435 /// Returns a mutability `m` such that an `&m T` pointer could be used to obtain this borrow
436 /// kind. Because borrow kinds are richer than mutabilities, we sometimes have to pick a
437 /// mutability that is stronger than necessary so that it at least *would permit* the borrow in
438 /// question.
439 pub fn to_mutbl_lossy(self) -> hir::Mutability {
440 match self {
441 MutBorrow => hir::Mutability::Mut,
442 ImmBorrow => hir::Mutability::Not,
443
444 // We have no type corresponding to a unique imm borrow, so
94222f64 445 // use `&mut`. It gives all the capabilities of a `&uniq`
6a06907d
XL
446 // and hence is a safe "over approximation".
447 UniqueImmBorrow => hir::Mutability::Mut,
448 }
449 }
6a06907d 450}
94222f64
XL
451
452pub fn provide(providers: &mut ty::query::Providers) {
453 *providers = ty::query::Providers { symbols_for_closure_captures, ..*providers }
454}