@@ -7,10 +7,12 @@ use crate::thir::*;
7
7
use rustc_hir:: def_id:: DefId ;
8
8
use rustc_hir:: HirId ;
9
9
use rustc_middle:: middle:: region;
10
+ use rustc_middle:: hir:: place:: ProjectionKind as HirProjectionKind ;
10
11
use rustc_middle:: mir:: AssertKind :: BoundsCheck ;
11
12
use rustc_middle:: mir:: * ;
12
13
use rustc_middle:: ty:: { self , CanonicalUserTypeAnnotation , Ty , TyCtxt , Variance } ;
13
14
use rustc_span:: Span ;
15
+ use rustc_target:: abi:: VariantIdx ;
14
16
15
17
use rustc_index:: vec:: Idx ;
16
18
@@ -32,59 +34,141 @@ struct PlaceBuilder<'tcx> {
32
34
projection : Vec < PlaceElem < ' tcx > > ,
33
35
}
34
36
35
- fn capture_matching_projections < ' a , ' tcx > (
36
- tcx : TyCtxt < ' tcx > ,
37
- typeck_results : & ' a ty:: TypeckResults < ' tcx > ,
37
+ /// Given a list of MIR projections, convert them to list of hir ProjectionKind.
38
+ fn convert_to_hir_projections_for_capture < ' tcx > (
39
+ mir_projections : & Vec < PlaceElem < ' tcx > > ) -> Vec < HirProjectionKind > {
40
+
41
+ let mut hir_projections = Vec :: new ( ) ;
42
+
43
+ for mir_projection in mir_projections {
44
+ let hir_projection = match mir_projection {
45
+ ProjectionElem :: Deref => HirProjectionKind :: Deref ,
46
+ ProjectionElem :: Field ( field, _) => {
47
+ // We will never encouter this for multivariant enums,
48
+ // read the comment for `Downcast`.
49
+ HirProjectionKind :: Field ( field. index ( ) as u32 , VariantIdx :: new ( 0 ) )
50
+ } ,
51
+ ProjectionElem :: Downcast ( ..) => {
52
+ // This projections exist only for enums that have
53
+ // multiple variants. Since such enums that are captured
54
+ // completely, we can stop here.
55
+ break
56
+ } ,
57
+ ProjectionElem :: Index ( ..)
58
+ | ProjectionElem :: ConstantIndex { .. }
59
+ | ProjectionElem :: Subslice { .. } => {
60
+ // We don't capture array-access projections.
61
+ // We can stop here as arrays are captured completely.
62
+ break
63
+ } ,
64
+ } ;
65
+
66
+ hir_projections. push ( hir_projection) ;
67
+ }
68
+
69
+ hir_projections
70
+ }
71
+
72
+ fn is_ancestor_of_capture (
73
+ proj_possible_ancestor : & Vec < HirProjectionKind > ,
74
+ proj_capture : & Vec < HirProjectionKind > ) -> bool {
75
+
76
+ for ( proj_ans, proj_capture) in proj_possible_ancestor. iter ( ) . zip ( proj_capture. iter ( ) ) {
77
+ if proj_ans != proj_capture {
78
+ return false ;
79
+ }
80
+ }
81
+
82
+ // In cases like
83
+ // ```rust,ignore(wont-compile-because-pseudocode)
84
+ // let c = || {
85
+ // x.0.1 += 1;
86
+ // let _ = x.0;
87
+ // }
88
+ // ```
89
+ //
90
+ // Here with `capture_disjoint_fields` enabled, x.0 won't be
91
+ // captured but x.0.1 would be.
92
+ //
93
+ // Therefore we can't just return true here.
94
+ proj_possible_ancestor. len ( ) <= proj_capture. len ( )
95
+ }
96
+
97
+ /// Computes the index of a capture within the desugared closure provided the closure's
98
+ /// `closure_min_captures` and the capture's index of the capture in the
99
+ /// `ty::MinCaptureList` of the root variable `var_hir_id`.
100
+ fn compute_capture_idx < ' tcx > (
101
+ closure_min_captures : & ty:: RootVariableMinCaptureList < ' tcx > ,
38
102
var_hir_id : HirId ,
39
- closure_def_id : DefId ,
40
- projections : & Vec < PlaceElem < ' tcx > > ,
41
- ) -> Option < ( usize , Ty < ' tcx > , ty:: UpvarCapture < ' tcx > ) > {
42
- let capture = typeck_results
43
- . closure_captures
44
- . get ( & closure_def_id)
45
- . and_then ( |captures| captures. get_full ( & var_hir_id) ) ;
46
-
47
- if capture. is_none ( ) {
48
- if !tcx. features ( ) . capture_disjoint_fields {
49
- bug ! (
50
- "No associated capture found for {:?}[{:#?}] even though \
51
- capture_disjoint_fields isn't enabled",
52
- var_hir_id,
53
- projections
54
- )
103
+ root_var_idx : usize ) -> usize {
104
+ let mut res = 0 ;
105
+ for ( var_id, capture_list) in closure_min_captures {
106
+ if * var_id == var_hir_id {
107
+ res += root_var_idx;
108
+ break ;
109
+ } else {
110
+ res += capture_list. len ( ) ;
55
111
}
56
112
}
57
113
58
- // Unwrap until the FIXME has been resolved
59
- let ( capture_index , _ , _ ) = capture . unwrap ( ) ;
114
+ res
115
+ }
60
116
61
- let closure_ty =
62
- typeck_results. node_type ( tcx. hir ( ) . local_def_id_to_hir_id ( closure_def_id. expect_local ( ) ) ) ;
117
+ /// Given a closure, returns the index of a capture within the desugared closure struct and the
118
+ /// `ty::CapturedPlace` which is the ancestor of the Place represented using the `var_hir_id`
119
+ /// and `projection`.
120
+ ///
121
+ /// Note there will be atmost one ancestor for any given Place.
122
+ ///
123
+ /// Returns None, when the ancestor is not found.
124
+ fn find_capture_matching_projections < ' a , ' tcx > (
125
+ typeck_results : & ' a ty:: TypeckResults < ' tcx > ,
126
+ var_hir_id : HirId ,
127
+ closure_def_id : DefId ,
128
+ projections : & Vec < PlaceElem < ' tcx > > ,
129
+ ) -> Option < ( usize , & ' a ty:: CapturedPlace < ' tcx > ) > {
130
+ let closure_min_captures = typeck_results
131
+ . closure_min_captures
132
+ . get ( & closure_def_id) ;
133
+
134
+ let closure_min_captures = if let Some ( min_captures) = closure_min_captures {
135
+ min_captures
136
+ } else {
137
+ return None ;
138
+ } ;
63
139
64
- let substs = match closure_ty. kind ( ) {
65
- ty:: Closure ( _, substs) => ty:: UpvarSubsts :: Closure ( substs) ,
66
- ty:: Generator ( _, substs, _) => ty:: UpvarSubsts :: Generator ( substs) ,
67
- _ => bug ! ( "Lowering capture for non-closure type {:?}" , closure_ty) ,
140
+ let root_variable_min_captures =
141
+ if let Some ( captures) = closure_min_captures. get ( & var_hir_id) {
142
+ captures
143
+ } else {
144
+ return None
68
145
} ;
69
146
70
- // Access the capture by accessing the field within the Closure struct.
71
- //
72
- // We must have inferred the capture types since we are building MIR, therefore
73
- // it's safe to call `upvar_tys` and we can unwrap here because
74
- // we know that the capture exists and is the `capture_index`-th capture.
75
- let var_ty = substs. upvar_tys ( ) . nth ( capture_index) . unwrap ( ) ;
147
+ let hir_projections = convert_to_hir_projections_for_capture ( projections) ;
148
+
149
+ let find_ancestor = || {
150
+ for ( i, capture) in root_variable_min_captures. iter ( ) . enumerate ( ) {
151
+ let possible_ancestor_proj_kinds =
152
+ capture. place . projections . iter ( ) . map ( |proj| proj. kind ) . collect ( ) ;
153
+ if is_ancestor_of_capture ( & possible_ancestor_proj_kinds, & hir_projections) {
154
+ return Some ( ( i, capture) ) ;
155
+ }
156
+ }
157
+ return None ;
158
+ } ;
76
159
77
- let upvar_id = ty:: UpvarId :: new ( var_hir_id, closure_def_id. expect_local ( ) ) ;
78
- let capture_kind = typeck_results. upvar_capture ( upvar_id) ;
79
160
80
- Some ( ( capture_index, var_ty, capture_kind) )
161
+ if let Some ( ( idx, capture) ) = find_ancestor ( ) {
162
+ Some ( ( compute_capture_idx ( closure_min_captures, var_hir_id, idx) , capture) )
163
+ } else {
164
+ None
165
+ }
81
166
}
82
167
83
- /// Takes a PlaceBuilder and resolves the upvar (if any) within it,
84
- /// so that the PlaceBuilder now starts from PlaceBase::Local.
168
+ /// Takes a PlaceBuilder and resolves the upvar (if any) within it, so that the
169
+ /// ` PlaceBuilder` now starts from ` PlaceBase::Local` .
85
170
///
86
- /// Returns a Result with the error being the HirId of the
87
- /// Upvar that was not found.
171
+ /// Returns a Result with the error being the HirId of the Upvar that was not found.
88
172
fn to_upvars_resolved_place_builder < ' a , ' tcx > (
89
173
from_builder : PlaceBuilder < ' tcx > ,
90
174
tcx : TyCtxt < ' tcx > ,
@@ -104,29 +188,50 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
104
188
ty:: ClosureKind :: FnOnce => { }
105
189
}
106
190
107
- let ( capture_index, var_ty, capture_kind) =
108
- if let Some ( capture_details) = capture_matching_projections (
109
- tcx,
191
+ let ( capture_index, capture) =
192
+ if let Some ( capture_details) = find_capture_matching_projections (
110
193
typeck_results,
111
194
var_hir_id,
112
195
closure_def_id,
113
196
& from_builder. projection ,
114
197
) {
115
198
capture_details
116
199
} else {
200
+ if !tcx. features ( ) . capture_disjoint_fields {
201
+ bug ! (
202
+ "No associated capture found for {:?}[{:#?}] even though \
203
+ capture_disjoint_fields isn't enabled",
204
+ var_hir_id,
205
+ from_builder. projection,
206
+ )
207
+ } else {
208
+ // FIXME(project-rfc-2229#24): Handle this case properly
209
+ println ! (
210
+ "No associated capture found for {:?}[{:#?}]" ,
211
+ var_hir_id,
212
+ from_builder. projection,
213
+ )
214
+ }
117
215
return Err ( var_hir_id) ;
118
216
} ;
119
217
218
+ let closure_ty = typeck_results
219
+ . node_type ( tcx. hir ( ) . local_def_id_to_hir_id ( closure_def_id. expect_local ( ) ) ) ;
220
+ let substs = match closure_ty. kind ( ) {
221
+ ty:: Closure ( _, substs) => ty:: UpvarSubsts :: Closure ( substs) ,
222
+ ty:: Generator ( _, substs, _) => ty:: UpvarSubsts :: Generator ( substs) ,
223
+ _ => bug ! ( "Lowering capture for non-closure type {:?}" , closure_ty)
224
+ } ;
225
+
226
+ let var_ty = substs. tupled_upvars_ty ( ) . tuple_get_field ( capture_index) . unwrap ( ) ;
120
227
place_builder = place_builder. field ( Field :: new ( capture_index) , var_ty) ;
121
228
122
- // If the variable is captured via ByRef(Immutable/Mutable) Borrow,
123
- // we need to deref it
124
- place_builder = match capture_kind {
229
+ place_builder = match capture. info . capture_kind {
125
230
ty:: UpvarCapture :: ByRef ( _) => place_builder. deref ( ) ,
126
231
ty:: UpvarCapture :: ByValue ( _) => place_builder,
127
232
} ;
128
233
129
- let next_projection = 0 ;
234
+ let next_projection = capture . place . projections . len ( ) ;
130
235
let mut curr_projections = from_builder. projection ;
131
236
place_builder. projection . extend ( curr_projections. drain ( next_projection..) ) ;
132
237
0 commit comments