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