]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/check/generator_interior.rs
factor into struct, and comments
[rust.git] / compiler / rustc_typeck / src / check / generator_interior.rs
1 //! This calculates the types which has storage which lives across a suspension point in a
2 //! generator from the perspective of typeck. The actual types used at runtime
3 //! is calculated in `rustc_mir::transform::generator` and may be a subset of the
4 //! types computed here.
5
6 use super::FnCtxt;
7 use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
8 use rustc_errors::pluralize;
9 use rustc_hir as hir;
10 use rustc_hir::def::{CtorKind, DefKind, Res};
11 use rustc_hir::def_id::DefId;
12 use rustc_hir::hir_id::HirIdSet;
13 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
14 use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind};
15 use rustc_middle::middle::region::{self, YieldData};
16 use rustc_middle::ty::{self, Ty, TyCtxt};
17 use rustc_span::symbol::sym;
18 use rustc_span::Span;
19 use smallvec::SmallVec;
20 use tracing::debug;
21
22 struct InteriorVisitor<'a, 'tcx> {
23     fcx: &'a FnCtxt<'a, 'tcx>,
24     types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>,
25     region_scope_tree: &'tcx region::ScopeTree,
26     expr_count: usize,
27     kind: hir::GeneratorKind,
28     prev_unresolved_span: Option<Span>,
29     /// Match arm guards have temporary borrows from the pattern bindings.
30     /// In case there is a yield point in a guard with a reference to such bindings,
31     /// such borrows can span across this yield point.
32     /// As such, we need to track these borrows and record them despite of the fact
33     /// that they may succeed the said yield point in the post-order.
34     guard_bindings: SmallVec<[SmallVec<[HirId; 4]>; 1]>,
35     guard_bindings_set: HirIdSet,
36 }
37
38 impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
39     fn record(
40         &mut self,
41         ty: Ty<'tcx>,
42         hir_id: HirId,
43         scope: Option<region::Scope>,
44         expr: Option<&'tcx Expr<'tcx>>,
45         source_span: Span,
46         guard_borrowing_from_pattern: bool,
47     ) {
48         use rustc_span::DUMMY_SP;
49
50         debug!(
51             "generator_interior: attempting to record type {:?} {:?} {:?} {:?}",
52             ty, scope, expr, source_span
53         );
54
55         let live_across_yield = scope
56             .map(|s| {
57                 self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| {
58                     // If we are recording an expression that is the last yield
59                     // in the scope, or that has a postorder CFG index larger
60                     // than the one of all of the yields, then its value can't
61                     // be storage-live (and therefore live) at any of the yields.
62                     //
63                     // See the mega-comment at `yield_in_scope` for a proof.
64
65                     debug!(
66                         "comparing counts yield: {} self: {}, source_span = {:?}",
67                         yield_data.expr_and_pat_count, self.expr_count, source_span
68                     );
69
70                     // If it is a borrowing happening in the guard,
71                     // it needs to be recorded regardless because they
72                     // do live across this yield point.
73                     if guard_borrowing_from_pattern
74                         || yield_data.expr_and_pat_count >= self.expr_count
75                     {
76                         Some(yield_data)
77                     } else {
78                         None
79                     }
80                 })
81             })
82             .unwrap_or_else(|| {
83                 Some(YieldData { span: DUMMY_SP, expr_and_pat_count: 0, source: self.kind.into() })
84             });
85
86         if let Some(yield_data) = live_across_yield {
87             let ty = self.fcx.resolve_vars_if_possible(ty);
88             debug!(
89                 "type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}",
90                 expr, scope, ty, self.expr_count, yield_data.span
91             );
92
93             if let Some((unresolved_type, unresolved_type_span)) =
94                 self.fcx.unresolved_type_vars(&ty)
95             {
96                 // If unresolved type isn't a ty_var then unresolved_type_span is None
97                 let span = self
98                     .prev_unresolved_span
99                     .unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span));
100
101                 // If we encounter an int/float variable, then inference fallback didn't
102                 // finish due to some other error. Don't emit spurious additional errors.
103                 if let ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) =
104                     unresolved_type.kind()
105                 {
106                     self.fcx
107                         .tcx
108                         .sess
109                         .delay_span_bug(span, &format!("Encountered var {:?}", unresolved_type));
110                 } else {
111                     let note = format!(
112                         "the type is part of the {} because of this {}",
113                         self.kind, yield_data.source
114                     );
115
116                     self.fcx
117                         .need_type_info_err_in_generator(self.kind, span, unresolved_type)
118                         .span_note(yield_data.span, &*note)
119                         .emit();
120                 }
121             } else {
122                 // Insert the type into the ordered set.
123                 let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree));
124
125                 check_must_not_suspend_ty(
126                     self.fcx,
127                     ty,
128                     hir_id,
129                     SuspendCheckData {
130                         expr,
131                         source_span,
132                         yield_span: yield_data.span,
133                         plural_len: 1,
134                         ..Default::default()
135                     },
136                 );
137
138                 self.types.insert(ty::GeneratorInteriorTypeCause {
139                     span: source_span,
140                     ty: &ty,
141                     scope_span,
142                     yield_span: yield_data.span,
143                     expr: expr.map(|e| e.hir_id),
144                 });
145             }
146         } else {
147             debug!(
148                 "no type in expr = {:?}, count = {:?}, span = {:?}",
149                 expr,
150                 self.expr_count,
151                 expr.map(|e| e.span)
152             );
153             let ty = self.fcx.resolve_vars_if_possible(ty);
154             if let Some((unresolved_type, unresolved_type_span)) =
155                 self.fcx.unresolved_type_vars(&ty)
156             {
157                 debug!(
158                     "remained unresolved_type = {:?}, unresolved_type_span: {:?}",
159                     unresolved_type, unresolved_type_span
160                 );
161                 self.prev_unresolved_span = unresolved_type_span;
162             }
163         }
164     }
165 }
166
167 pub fn resolve_interior<'a, 'tcx>(
168     fcx: &'a FnCtxt<'a, 'tcx>,
169     def_id: DefId,
170     body_id: hir::BodyId,
171     interior: Ty<'tcx>,
172     kind: hir::GeneratorKind,
173 ) {
174     let body = fcx.tcx.hir().body(body_id);
175     let mut visitor = InteriorVisitor {
176         fcx,
177         types: FxIndexSet::default(),
178         region_scope_tree: fcx.tcx.region_scope_tree(def_id),
179         expr_count: 0,
180         kind,
181         prev_unresolved_span: None,
182         guard_bindings: <_>::default(),
183         guard_bindings_set: <_>::default(),
184     };
185     intravisit::walk_body(&mut visitor, body);
186
187     // Check that we visited the same amount of expressions and the RegionResolutionVisitor
188     let region_expr_count = visitor.region_scope_tree.body_expr_count(body_id).unwrap();
189     assert_eq!(region_expr_count, visitor.expr_count);
190
191     // The types are already kept in insertion order.
192     let types = visitor.types;
193
194     // The types in the generator interior contain lifetimes local to the generator itself,
195     // which should not be exposed outside of the generator. Therefore, we replace these
196     // lifetimes with existentially-bound lifetimes, which reflect the exact value of the
197     // lifetimes not being known by users.
198     //
199     // These lifetimes are used in auto trait impl checking (for example,
200     // if a Sync generator contains an &'α T, we need to check whether &'α T: Sync),
201     // so knowledge of the exact relationships between them isn't particularly important.
202
203     debug!("types in generator {:?}, span = {:?}", types, body.value.span);
204
205     let mut counter = 0;
206     let mut captured_tys = FxHashSet::default();
207     let type_causes: Vec<_> = types
208         .into_iter()
209         .filter_map(|mut cause| {
210             // Erase regions and canonicalize late-bound regions to deduplicate as many types as we
211             // can.
212             let erased = fcx.tcx.erase_regions(cause.ty);
213             if captured_tys.insert(erased) {
214                 // Replace all regions inside the generator interior with late bound regions.
215                 // Note that each region slot in the types gets a new fresh late bound region,
216                 // which means that none of the regions inside relate to any other, even if
217                 // typeck had previously found constraints that would cause them to be related.
218                 let folded = fcx.tcx.fold_regions(erased, &mut false, |_, current_depth| {
219                     let br = ty::BoundRegion {
220                         var: ty::BoundVar::from_u32(counter),
221                         kind: ty::BrAnon(counter),
222                     };
223                     let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br));
224                     counter += 1;
225                     r
226                 });
227
228                 cause.ty = folded;
229                 Some(cause)
230             } else {
231                 None
232             }
233         })
234         .collect();
235
236     // Extract type components to build the witness type.
237     let type_list = fcx.tcx.mk_type_list(type_causes.iter().map(|cause| cause.ty));
238     let bound_vars = fcx.tcx.mk_bound_variable_kinds(
239         (0..counter).map(|i| ty::BoundVariableKind::Region(ty::BrAnon(i))),
240     );
241     let witness =
242         fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone()));
243
244     // Store the generator types and spans into the typeck results for this generator.
245     visitor.fcx.inh.typeck_results.borrow_mut().generator_interior_types =
246         ty::Binder::bind_with_vars(type_causes, bound_vars);
247
248     debug!(
249         "types in generator after region replacement {:?}, span = {:?}",
250         witness, body.value.span
251     );
252
253     // Unify the type variable inside the generator with the new witness
254     match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(interior, witness) {
255         Ok(ok) => fcx.register_infer_ok_obligations(ok),
256         _ => bug!(),
257     }
258 }
259
260 // This visitor has to have the same visit_expr calls as RegionResolutionVisitor in
261 // librustc_middle/middle/region.rs since `expr_count` is compared against the results
262 // there.
263 impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
264     type Map = intravisit::ErasedMap<'tcx>;
265
266     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
267         NestedVisitorMap::None
268     }
269
270     fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) {
271         let Arm { guard, pat, body, .. } = arm;
272         self.visit_pat(pat);
273         if let Some(ref g) = guard {
274             self.guard_bindings.push(<_>::default());
275             ArmPatCollector {
276                 guard_bindings_set: &mut self.guard_bindings_set,
277                 guard_bindings: self
278                     .guard_bindings
279                     .last_mut()
280                     .expect("should have pushed at least one earlier"),
281             }
282             .visit_pat(pat);
283
284             match g {
285                 Guard::If(ref e) => {
286                     self.visit_expr(e);
287                 }
288                 Guard::IfLet(ref pat, ref e) => {
289                     self.visit_pat(pat);
290                     self.visit_expr(e);
291                 }
292             }
293
294             let mut scope_var_ids =
295                 self.guard_bindings.pop().expect("should have pushed at least one earlier");
296             for var_id in scope_var_ids.drain(..) {
297                 self.guard_bindings_set.remove(&var_id);
298             }
299         }
300         self.visit_expr(body);
301     }
302
303     fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
304         intravisit::walk_pat(self, pat);
305
306         self.expr_count += 1;
307
308         if let PatKind::Binding(..) = pat.kind {
309             let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id);
310             let ty = self.fcx.typeck_results.borrow().pat_ty(pat);
311             self.record(ty, pat.hir_id, Some(scope), None, pat.span, false);
312         }
313     }
314
315     fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
316         let mut guard_borrowing_from_pattern = false;
317         match &expr.kind {
318             ExprKind::Call(callee, args) => match &callee.kind {
319                 ExprKind::Path(qpath) => {
320                     let res = self.fcx.typeck_results.borrow().qpath_res(qpath, callee.hir_id);
321                     match res {
322                         // Direct calls never need to keep the callee `ty::FnDef`
323                         // ZST in a temporary, so skip its type, just in case it
324                         // can significantly complicate the generator type.
325                         Res::Def(
326                             DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn),
327                             _,
328                         ) => {
329                             // NOTE(eddyb) this assumes a path expression has
330                             // no nested expressions to keep track of.
331                             self.expr_count += 1;
332
333                             // Record the rest of the call expression normally.
334                             for arg in *args {
335                                 self.visit_expr(arg);
336                             }
337                         }
338                         _ => intravisit::walk_expr(self, expr),
339                     }
340                 }
341                 _ => intravisit::walk_expr(self, expr),
342             },
343             ExprKind::Path(qpath) => {
344                 intravisit::walk_expr(self, expr);
345                 let res = self.fcx.typeck_results.borrow().qpath_res(qpath, expr.hir_id);
346                 match res {
347                     Res::Local(id) if self.guard_bindings_set.contains(&id) => {
348                         guard_borrowing_from_pattern = true;
349                     }
350                     _ => {}
351                 }
352             }
353             _ => intravisit::walk_expr(self, expr),
354         }
355
356         self.expr_count += 1;
357
358         let scope = self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
359
360         // If there are adjustments, then record the final type --
361         // this is the actual value that is being produced.
362         if let Some(adjusted_ty) = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr) {
363             self.record(
364                 adjusted_ty,
365                 expr.hir_id,
366                 scope,
367                 Some(expr),
368                 expr.span,
369                 guard_borrowing_from_pattern,
370             );
371         }
372
373         // Also record the unadjusted type (which is the only type if
374         // there are no adjustments). The reason for this is that the
375         // unadjusted value is sometimes a "temporary" that would wind
376         // up in a MIR temporary.
377         //
378         // As an example, consider an expression like `vec![].push(x)`.
379         // Here, the `vec![]` would wind up MIR stored into a
380         // temporary variable `t` which we can borrow to invoke
381         // `<Vec<_>>::push(&mut t, x)`.
382         //
383         // Note that an expression can have many adjustments, and we
384         // are just ignoring those intermediate types. This is because
385         // those intermediate values are always linearly "consumed" by
386         // the other adjustments, and hence would never be directly
387         // captured in the MIR.
388         //
389         // (Note that this partly relies on the fact that the `Deref`
390         // traits always return references, which means their content
391         // can be reborrowed without needing to spill to a temporary.
392         // If this were not the case, then we could conceivably have
393         // to create intermediate temporaries.)
394         //
395         // The type table might not have information for this expression
396         // if it is in a malformed scope. (#66387)
397         if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) {
398             if guard_borrowing_from_pattern {
399                 // Match guards create references to all the bindings in the pattern that are used
400                 // in the guard, e.g. `y if is_even(y) => ...` becomes `is_even(*r_y)` where `r_y`
401                 // is a reference to `y`, so we must record a reference to the type of the binding.
402                 let tcx = self.fcx.tcx;
403                 let ref_ty = tcx.mk_ref(
404                     // Use `ReErased` as `resolve_interior` is going to replace all the regions anyway.
405                     tcx.mk_region(ty::RegionKind::ReErased),
406                     ty::TypeAndMut { ty, mutbl: hir::Mutability::Not },
407                 );
408                 self.record(
409                     ref_ty,
410                     expr.hir_id,
411                     scope,
412                     Some(expr),
413                     expr.span,
414                     guard_borrowing_from_pattern,
415                 );
416             }
417             self.record(
418                 ty,
419                 expr.hir_id,
420                 scope,
421                 Some(expr),
422                 expr.span,
423                 guard_borrowing_from_pattern,
424             );
425         } else {
426             self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node");
427         }
428     }
429 }
430
431 struct ArmPatCollector<'a> {
432     guard_bindings_set: &'a mut HirIdSet,
433     guard_bindings: &'a mut SmallVec<[HirId; 4]>,
434 }
435
436 impl<'a, 'tcx> Visitor<'tcx> for ArmPatCollector<'a> {
437     type Map = intravisit::ErasedMap<'tcx>;
438
439     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
440         NestedVisitorMap::None
441     }
442
443     fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
444         intravisit::walk_pat(self, pat);
445         if let PatKind::Binding(_, id, ..) = pat.kind {
446             self.guard_bindings.push(id);
447             self.guard_bindings_set.insert(id);
448         }
449     }
450 }
451
452 #[derive(Default)]
453 pub struct SuspendCheckData<'a, 'tcx> {
454     expr: Option<&'tcx Expr<'tcx>>,
455     source_span: Span,
456     yield_span: Span,
457     descr_pre: &'a str,
458     descr_post: &'a str,
459     plural_len: usize,
460 }
461
462 // Returns whether it emitted a diagnostic or not
463 // Note that this fn and the proceding one are based on the code
464 // for creating must_use diagnostics
465 pub fn check_must_not_suspend_ty<'tcx>(
466     fcx: &FnCtxt<'_, 'tcx>,
467     ty: Ty<'tcx>,
468     hir_id: HirId,
469     data: SuspendCheckData<'_, 'tcx>,
470 ) -> bool {
471     if ty.is_unit()
472     // FIXME: should this check `is_ty_uninhabited_from`. This query is not available in this stage
473     // of typeck (before ReVar and RePlaceholder are removed), but may remove noise, like in
474     // `must_use`
475     // || fcx.tcx.is_ty_uninhabited_from(fcx.tcx.parent_module(hir_id).to_def_id(), ty, fcx.param_env)
476     {
477         return false;
478     }
479
480     let plural_suffix = pluralize!(data.plural_len);
481
482     match *ty.kind() {
483         ty::Adt(..) if ty.is_box() => {
484             let boxed_ty = ty.boxed_ty();
485             let descr_pre = &format!("{}boxed ", data.descr_pre);
486             check_must_not_suspend_ty(fcx, boxed_ty, hir_id, SuspendCheckData { descr_pre, ..data })
487         }
488         ty::Adt(def, _) => check_must_not_suspend_def(fcx.tcx, def.did, hir_id, data),
489         // FIXME: support adding the attribute to TAITs
490         ty::Opaque(def, _) => {
491             let mut has_emitted = false;
492             for &(predicate, _) in fcx.tcx.explicit_item_bounds(def) {
493                 // We only look at the `DefId`, so it is safe to skip the binder here.
494                 if let ty::PredicateKind::Trait(ref poly_trait_predicate) =
495                     predicate.kind().skip_binder()
496                 {
497                     let def_id = poly_trait_predicate.trait_ref.def_id;
498                     let descr_pre = &format!("{}implementer{} of ", data.descr_pre, plural_suffix);
499                     if check_must_not_suspend_def(
500                         fcx.tcx,
501                         def_id,
502                         hir_id,
503                         SuspendCheckData { descr_pre, ..data },
504                     ) {
505                         has_emitted = true;
506                         break;
507                     }
508                 }
509             }
510             has_emitted
511         }
512         ty::Dynamic(binder, _) => {
513             let mut has_emitted = false;
514             for predicate in binder.iter() {
515                 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
516                     let def_id = trait_ref.def_id;
517                     let descr_post = &format!(" trait object{}{}", plural_suffix, data.descr_post);
518                     if check_must_not_suspend_def(
519                         fcx.tcx,
520                         def_id,
521                         hir_id,
522                         SuspendCheckData { descr_post, ..data },
523                     ) {
524                         has_emitted = true;
525                         break;
526                     }
527                 }
528             }
529             has_emitted
530         }
531         ty::Tuple(ref tys) => {
532             let mut has_emitted = false;
533             let spans = if let Some(hir::ExprKind::Tup(comps)) = data.expr.map(|e| &e.kind) {
534                 debug_assert_eq!(comps.len(), tys.len());
535                 comps.iter().map(|e| e.span).collect()
536             } else {
537                 vec![]
538             };
539             for (i, ty) in tys.iter().map(|k| k.expect_ty()).enumerate() {
540                 let descr_post = &format!(" in tuple element {}", i);
541                 let span = *spans.get(i).unwrap_or(&data.source_span);
542                 if check_must_not_suspend_ty(
543                     fcx,
544                     ty,
545                     hir_id,
546                     SuspendCheckData { descr_post, source_span: span, ..data },
547                 ) {
548                     has_emitted = true;
549                 }
550             }
551             has_emitted
552         }
553         ty::Array(ty, len) => {
554             let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix);
555             check_must_not_suspend_ty(
556                 fcx,
557                 ty,
558                 hir_id,
559                 SuspendCheckData {
560                     descr_pre,
561                     plural_len: len.try_eval_usize(fcx.tcx, fcx.param_env).unwrap_or(0) as usize
562                         + 1,
563                     ..data
564                 },
565             )
566         }
567         _ => false,
568     }
569 }
570
571 fn check_must_not_suspend_def(
572     tcx: TyCtxt<'_>,
573     def_id: DefId,
574     hir_id: HirId,
575     data: SuspendCheckData<'_, '_>,
576 ) -> bool {
577     for attr in tcx.get_attrs(def_id).iter() {
578         if attr.has_name(sym::must_not_suspend) {
579             tcx.struct_span_lint_hir(
580                 rustc_session::lint::builtin::MUST_NOT_SUSPEND,
581                 hir_id,
582                 data.source_span,
583                 |lint| {
584                     let msg = format!(
585                         "{}`{}`{} held across a suspend point, but should not be",
586                         data.descr_pre,
587                         tcx.def_path_str(def_id),
588                         data.descr_post,
589                     );
590                     let mut err = lint.build(&msg);
591
592                     // add span pointing to the offending yield/await
593                     err.span_label(data.yield_span, "the value is held across this suspend point");
594
595                     // Add optional reason note
596                     if let Some(note) = attr.value_str() {
597                         // FIXME(guswynn): consider formatting this better
598                         err.span_note(data.source_span, &note.as_str());
599                     }
600
601                     // Add some quick suggestions on what to do
602                     // FIXME: can `drop` work as a suggestion here as well?
603                     err.span_help(
604                         data.source_span,
605                         "consider using a block (`{ ... }`) \
606                         to shrink the value's scope, ending before the suspend point",
607                     );
608
609                     err.emit();
610                 },
611             );
612
613             return true;
614         }
615     }
616     false
617 }