@@ -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
@@ -70,28 +72,129 @@ struct PlaceBuilder<'tcx> {
70
72
projection : Vec < PlaceElem < ' tcx > > ,
71
73
}
72
74
73
- fn capture_matching_projections < ' a , ' tcx > (
75
+ /// Given a list of MIR projections, convert them to list of HIR ProjectionKind.
76
+ /// The projections are truncated to represent a path that might be captured by a
77
+ /// closure/generator. This implies the vector returned from this function doesn't contain
78
+ /// ProjectionElems `Downcast`, `ConstantIndex`, `Index`, or `Subslice` because those will never be
79
+ /// part of a path that is captued by a closure. We stop applying projections once we see the first
80
+ /// projection that isn't captured by a closure.
81
+ fn convert_to_hir_projections_and_truncate_for_capture < ' tcx > (
82
+ mir_projections : & Vec < PlaceElem < ' tcx > > ,
83
+ ) -> Vec < HirProjectionKind > {
84
+
85
+ let mut hir_projections = Vec :: new ( ) ;
86
+
87
+ for mir_projection in mir_projections {
88
+ let hir_projection = match mir_projection {
89
+ ProjectionElem :: Deref => HirProjectionKind :: Deref ,
90
+ ProjectionElem :: Field ( field, _) => {
91
+ // We will never encouter this for multivariant enums,
92
+ // read the comment for `Downcast`.
93
+ HirProjectionKind :: Field ( field. index ( ) as u32 , VariantIdx :: new ( 0 ) )
94
+ } ,
95
+ ProjectionElem :: Downcast ( ..) => {
96
+ // This projections exist only for enums that have
97
+ // multiple variants. Since such enums that are captured
98
+ // completely, we can stop here.
99
+ break
100
+ } ,
101
+ ProjectionElem :: Index ( ..)
102
+ | ProjectionElem :: ConstantIndex { .. }
103
+ | ProjectionElem :: Subslice { .. } => {
104
+ // We don't capture array-access projections.
105
+ // We can stop here as arrays are captured completely.
106
+ break
107
+ } ,
108
+ } ;
109
+
110
+ hir_projections. push ( hir_projection) ;
111
+ }
112
+
113
+ hir_projections
114
+ }
115
+
116
+ /// Return true if the `proj_possible_ancestor` represents an ancestor path
117
+ /// to `proj_capture` or `proj_possible_ancestor` is same as `proj_capture`,
118
+ /// assuming they both start off of the same root variable.
119
+ ///
120
+ /// **Note:** It's the caller's responsibility to ensure that both lists of projections
121
+ /// start off of the same root variable.
122
+ ///
123
+ /// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of
124
+ /// `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`.
125
+ /// Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`.
126
+ /// 2. Since we only look at the projections here function will return `bar.x` as an a valid
127
+ /// ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections
128
+ /// list are being applied to the same root variable.
129
+ fn is_ancestor_or_same_capture (
130
+ proj_possible_ancestor : & Vec < HirProjectionKind > ,
131
+ proj_capture : & Vec < HirProjectionKind > ,
132
+ ) -> bool {
133
+ // We want to make sure `is_ancestor_or_same_capture("x.0.0", "x.0")` to return false.
134
+ // Therefore we can't just check if all projections are same in the zipped iterator below.
135
+ if proj_possible_ancestor. len ( ) > proj_capture. len ( ) {
136
+ return false ;
137
+ }
138
+
139
+ proj_possible_ancestor. iter ( ) . zip ( proj_capture) . all ( |( a, b) | a == b)
140
+ }
141
+
142
+ /// Computes the index of a capture within the desugared closure provided the closure's
143
+ /// `closure_min_captures` and the capture's index of the capture in the
144
+ /// `ty::MinCaptureList` of the root variable `var_hir_id`.
145
+ fn compute_capture_idx < ' tcx > (
146
+ closure_min_captures : & ty:: RootVariableMinCaptureList < ' tcx > ,
147
+ var_hir_id : HirId ,
148
+ root_var_idx : usize ,
149
+ ) -> usize {
150
+ let mut res = 0 ;
151
+ for ( var_id, capture_list) in closure_min_captures {
152
+ if * var_id == var_hir_id {
153
+ res += root_var_idx;
154
+ break ;
155
+ } else {
156
+ res += capture_list. len ( ) ;
157
+ }
158
+ }
159
+
160
+ res
161
+ }
162
+
163
+ /// Given a closure, returns the index of a capture within the desugared closure struct and the
164
+ /// `ty::CapturedPlace` which is the ancestor of the Place represented using the `var_hir_id`
165
+ /// and `projection`.
166
+ ///
167
+ /// Note there will be at most one ancestor for any given Place.
168
+ ///
169
+ /// Returns None, when the ancestor is not found.
170
+ fn find_capture_matching_projections < ' a , ' tcx > (
74
171
typeck_results : & ' a ty:: TypeckResults < ' tcx > ,
75
172
var_hir_id : HirId ,
76
173
closure_def_id : DefId ,
77
- _projections : & Vec < PlaceElem < ' tcx > > ,
78
- ) -> Option < ( usize , ty:: UpvarCapture < ' tcx > ) > {
79
- typeck_results
80
- . closure_captures
81
- . get ( & closure_def_id)
82
- . and_then ( |captures| captures. get_full ( & var_hir_id) )
83
- . and_then ( |( capture_index, _, _) |{
84
- let upvar_id = ty:: UpvarId :: new ( var_hir_id, closure_def_id. expect_local ( ) ) ;
85
- let capture_kind = typeck_results. upvar_capture ( upvar_id) ;
86
- Some ( ( capture_index, capture_kind) )
87
- } )
174
+ projections : & Vec < PlaceElem < ' tcx > > ,
175
+ ) -> Option < ( usize , & ' a ty:: CapturedPlace < ' tcx > ) > {
176
+ let closure_min_captures = typeck_results. closure_min_captures . get ( & closure_def_id) ?;
177
+ let root_variable_min_captures = closure_min_captures. get ( & var_hir_id) ?;
178
+
179
+ let hir_projections = convert_to_hir_projections_and_truncate_for_capture ( projections) ;
180
+
181
+ // If an ancestor is found, `idx` is the index within the list of captured places
182
+ // for root variable `var_hir_id` and `capture` is the `ty::CapturedPlace` itself.
183
+ let ( idx, capture) = root_variable_min_captures. iter ( ) . enumerate ( ) . find ( |( _, capture) | {
184
+ let possible_ancestor_proj_kinds =
185
+ capture. place . projections . iter ( ) . map ( |proj| proj. kind ) . collect ( ) ;
186
+ is_ancestor_or_same_capture ( & possible_ancestor_proj_kinds, & hir_projections)
187
+ } ) ?;
188
+
189
+ // Convert index to be from the presepective of the entire closure_min_captures map
190
+ // instead of just the root variable capture list
191
+ Some ( ( compute_capture_idx ( closure_min_captures, var_hir_id, idx) , capture) )
88
192
}
89
193
90
- /// Takes a PlaceBuilder and resolves the upvar (if any) within it,
91
- /// so that the PlaceBuilder now starts from PlaceBase::Local.
194
+ /// Takes a PlaceBuilder and resolves the upvar (if any) within it, so that the
195
+ /// ` PlaceBuilder` now starts from ` PlaceBase::Local` .
92
196
///
93
- /// Returns a Result with the error being the HirId of the
94
- /// Upvar that was not found.
197
+ /// Returns a Result with the error being the HirId of the Upvar that was not found.
95
198
fn to_upvars_resolved_place_builder < ' a , ' tcx > (
96
199
from_builder : PlaceBuilder < ' tcx > ,
97
200
tcx : TyCtxt < ' tcx > ,
@@ -110,8 +213,8 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
110
213
ty:: ClosureKind :: FnOnce => { }
111
214
}
112
215
113
- let ( capture_index, capture_kind ) =
114
- if let Some ( capture_details) = capture_matching_projections (
216
+ let ( capture_index, capture ) =
217
+ if let Some ( capture_details) = find_capture_matching_projections (
115
218
typeck_results,
116
219
var_hir_id,
117
220
closure_def_id,
@@ -149,21 +252,24 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
149
252
// Access the capture by accessing the field within the Closure struct.
150
253
//
151
254
// We must have inferred the capture types since we are building MIR, therefore
152
- // it's safe to call `upvar_tys ` and we can unwrap here because
255
+ // it's safe to call `tuple_element_ty ` and we can unwrap here because
153
256
// we know that the capture exists and is the `capture_index`-th capture.
154
- let var_ty = substs. upvar_tys ( ) . nth ( capture_index) . unwrap ( ) ;
257
+ let var_ty = substs. tupled_upvars_ty ( ) . tuple_element_ty ( capture_index) . unwrap ( ) ;
155
258
156
259
upvar_resolved_place_builder = upvar_resolved_place_builder. field ( Field :: new ( capture_index) , var_ty) ;
157
260
158
261
// If the variable is captured via ByRef(Immutable/Mutable) Borrow,
159
262
// we need to deref it
160
- upvar_resolved_place_builder = match capture_kind {
263
+ upvar_resolved_place_builder = match capture . info . capture_kind {
161
264
ty:: UpvarCapture :: ByRef ( _) => upvar_resolved_place_builder. deref ( ) ,
162
265
ty:: UpvarCapture :: ByValue ( _) => upvar_resolved_place_builder,
163
266
} ;
164
267
165
- let next_projection = 0 ;
268
+ let next_projection = capture . place . projections . len ( ) ;
166
269
let mut curr_projections = from_builder. projection ;
270
+
271
+ // We used some of the projections to build the capture itself,
272
+ // now we apply the remaining to the upvar resolved place.
167
273
upvar_resolved_place_builder. projection . extend (
168
274
curr_projections. drain ( next_projection..) ) ;
169
275
0 commit comments