Skip to content

Commit 0ff7e65

Browse files
relocate upvars to Unresumed state and make coroutine prefix trivial
Co-authored-by: Dario Nieuwenhuis <[email protected]>
1 parent 3ea711f commit 0ff7e65

File tree

65 files changed

+1313
-579
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+1313
-579
lines changed

Diff for: compiler/rustc_abi/src/layout.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -215,15 +215,15 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
215215
>(
216216
&self,
217217
local_layouts: &IndexSlice<LocalIdx, F>,
218-
prefix_layouts: IndexVec<FieldIdx, F>,
218+
relocated_upvars: &IndexSlice<LocalIdx, Option<LocalIdx>>,
219219
variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
220220
storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
221221
tag_to_layout: impl Fn(Scalar) -> F,
222222
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
223223
coroutine::layout(
224224
self,
225225
local_layouts,
226-
prefix_layouts,
226+
relocated_upvars,
227227
variant_fields,
228228
storage_conflicts,
229229
tag_to_layout,

Diff for: compiler/rustc_abi/src/layout/coroutine.rs

+50-34
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ pub(super) fn layout<
145145
>(
146146
calc: &super::LayoutCalculator<impl HasDataLayout>,
147147
local_layouts: &IndexSlice<LocalIdx, F>,
148-
mut prefix_layouts: IndexVec<FieldIdx, F>,
148+
relocated_upvars: &IndexSlice<LocalIdx, Option<LocalIdx>>,
149149
variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
150150
storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
151151
tag_to_layout: impl Fn(Scalar) -> F,
@@ -155,10 +155,8 @@ pub(super) fn layout<
155155
let (ineligible_locals, assignments) =
156156
coroutine_saved_local_eligibility(local_layouts.len(), variant_fields, storage_conflicts);
157157

158-
// Build a prefix layout, including "promoting" all ineligible
159-
// locals as part of the prefix. We compute the layout of all of
160-
// these fields at once to get optimal packing.
161-
let tag_index = prefix_layouts.len();
158+
// Build a prefix layout, consisting of only the state tag.
159+
let tag_index = 0;
162160

163161
// `variant_fields` already accounts for the reserved variants, so no need to add them.
164162
let max_discr = (variant_fields.len() - 1) as u128;
@@ -169,17 +167,17 @@ pub(super) fn layout<
169167
};
170168

171169
let promoted_layouts = ineligible_locals.iter().map(|local| local_layouts[local]);
172-
prefix_layouts.push(tag_to_layout(tag));
173-
prefix_layouts.extend(promoted_layouts);
170+
let prefix_layouts: IndexVec<_, _> =
171+
[tag_to_layout(tag)].into_iter().chain(promoted_layouts).collect();
174172
let prefix =
175173
calc.univariant(&prefix_layouts, &ReprOptions::default(), StructKind::AlwaysSized)?;
176174

177175
let (prefix_size, prefix_align) = (prefix.size, prefix.align);
178176

179-
// Split the prefix layout into the "outer" fields (upvars and
180-
// discriminant) and the "promoted" fields. Promoted fields will
181-
// get included in each variant that requested them in
182-
// CoroutineLayout.
177+
// Split the prefix layout into the discriminant and
178+
// the "promoted" fields.
179+
// Promoted fields will get included in each variant
180+
// that requested them in CoroutineLayout.
183181
debug!("prefix = {:#?}", prefix);
184182
let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields {
185183
FieldsShape::Arbitrary { mut offsets, memory_index } => {
@@ -215,22 +213,45 @@ pub(super) fn layout<
215213

216214
let mut size = prefix.size;
217215
let mut align = prefix.align;
218-
let variants = variant_fields
216+
let variants: IndexVec<VariantIdx, _> = variant_fields
219217
.iter_enumerated()
220218
.map(|(index, variant_fields)| {
219+
let is_unresumed = index == VariantIdx::new(0);
220+
let mut is_ineligible = IndexVec::from_elem_n(None, variant_fields.len());
221+
for (field, &local) in variant_fields.iter_enumerated() {
222+
if is_unresumed
223+
&& let Some(inner_local) = relocated_upvars[local]
224+
&& let Ineligible(Some(promoted_field)) = assignments[inner_local]
225+
{
226+
is_ineligible.insert(field, promoted_field);
227+
continue;
228+
}
229+
match assignments[local] {
230+
Assigned(v) if v == index => {}
231+
Ineligible(Some(promoted_field)) => {
232+
is_ineligible.insert(field, promoted_field);
233+
}
234+
Ineligible(None) => {
235+
panic!("an ineligible local should have been promoted into the prefix")
236+
}
237+
Assigned(_) => {
238+
panic!("an eligible local should have been assigned to exactly one variant")
239+
}
240+
Unassigned => {
241+
panic!("each saved local should have been inspected at least once")
242+
}
243+
}
244+
}
221245
// Only include overlap-eligible fields when we compute our variant layout.
222-
let variant_only_tys = variant_fields
223-
.iter()
224-
.filter(|local| match assignments[**local] {
225-
Unassigned => unreachable!(),
226-
Assigned(v) if v == index => true,
227-
Assigned(_) => unreachable!("assignment does not match variant"),
228-
Ineligible(_) => false,
246+
let fields: IndexVec<_, _> = variant_fields
247+
.iter_enumerated()
248+
.filter_map(|(field, &local)| {
249+
if is_ineligible.contains(field) { None } else { Some(local_layouts[local]) }
229250
})
230-
.map(|local| local_layouts[*local]);
251+
.collect();
231252

232253
let mut variant = calc.univariant(
233-
&variant_only_tys.collect::<IndexVec<_, _>>(),
254+
&fields,
234255
&ReprOptions::default(),
235256
StructKind::Prefixed(prefix_size, prefix_align.abi),
236257
)?;
@@ -254,19 +275,14 @@ pub(super) fn layout<
254275
IndexVec::from_elem_n(FieldIdx::new(invalid_field_idx), invalid_field_idx);
255276

256277
let mut offsets_and_memory_index = iter::zip(offsets, memory_index);
257-
let combined_offsets = variant_fields
278+
let combined_offsets = is_ineligible
258279
.iter_enumerated()
259-
.map(|(i, local)| {
260-
let (offset, memory_index) = match assignments[*local] {
261-
Unassigned => unreachable!(),
262-
Assigned(_) => {
263-
let (offset, memory_index) = offsets_and_memory_index.next().unwrap();
264-
(offset, promoted_memory_index.len() as u32 + memory_index)
265-
}
266-
Ineligible(field_idx) => {
267-
let field_idx = field_idx.unwrap();
268-
(promoted_offsets[field_idx], promoted_memory_index[field_idx])
269-
}
280+
.map(|(i, &is_ineligible)| {
281+
let (offset, memory_index) = if let Some(field_idx) = is_ineligible {
282+
(promoted_offsets[field_idx], promoted_memory_index[field_idx])
283+
} else {
284+
let (offset, memory_index) = offsets_and_memory_index.next().unwrap();
285+
(offset, promoted_memory_index.len() as u32 + memory_index)
270286
};
271287
combined_inverse_memory_index[memory_index] = i;
272288
offset
@@ -287,7 +303,7 @@ pub(super) fn layout<
287303
align = align.max(variant.align);
288304
Ok(variant)
289305
})
290-
.collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
306+
.try_collect()?;
291307

292308
size = size.align_to(align.abi);
293309

Diff for: compiler/rustc_abi/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#![cfg_attr(feature = "nightly", allow(internal_features))]
33
#![cfg_attr(feature = "nightly", doc(rust_logo))]
44
#![cfg_attr(feature = "nightly", feature(assert_matches))]
5+
#![cfg_attr(feature = "nightly", feature(iterator_try_collect))]
6+
#![cfg_attr(feature = "nightly", feature(let_chains))]
57
#![cfg_attr(feature = "nightly", feature(rustc_attrs))]
68
#![cfg_attr(feature = "nightly", feature(rustdoc_internals))]
79
#![cfg_attr(feature = "nightly", feature(step_trait))]

Diff for: compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

+63-42
Original file line numberDiff line numberDiff line change
@@ -393,49 +393,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
393393
Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
394394
));
395395

396-
let captured_place = self.upvars[upvar_index.index()];
397-
398-
err.span_label(span, format!("cannot {act}"));
399-
400-
let upvar_hir_id = captured_place.get_root_variable();
401-
402-
if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id)
403-
&& let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) =
404-
pat.kind
405-
{
406-
if upvar_ident.name == kw::SelfLower {
407-
for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) {
408-
if let Some(fn_decl) = node.fn_decl() {
409-
if !matches!(
410-
fn_decl.implicit_self,
411-
hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
412-
) {
413-
err.span_suggestion_verbose(
414-
upvar_ident.span.shrink_to_lo(),
415-
"consider changing this to be mutable",
416-
"mut ",
417-
Applicability::MachineApplicable,
418-
);
419-
break;
420-
}
421-
}
422-
}
423-
} else {
424-
err.span_suggestion_verbose(
425-
upvar_ident.span.shrink_to_lo(),
426-
"consider changing this to be mutable",
427-
"mut ",
428-
Applicability::MachineApplicable,
429-
);
430-
}
431-
}
396+
self.suggest_mutable_upvar(*upvar_index, the_place_err, &mut err, span, act);
397+
}
432398

433-
let tcx = self.infcx.tcx;
434-
if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
435-
&& let ty::Closure(id, _) = *ty.kind()
436-
{
437-
self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
438-
}
399+
PlaceRef { local, projection: [] }
400+
if let Some(upvar_index) = self
401+
.body
402+
.local_upvar_map
403+
.iter_enumerated()
404+
.filter_map(|(field, &local)| local.map(|local| (field, local)))
405+
.find_map(|(field, relocated)| (relocated == local).then_some(field)) =>
406+
{
407+
self.suggest_mutable_upvar(upvar_index, the_place_err, &mut err, span, act);
439408
}
440409

441410
// complete hack to approximate old AST-borrowck
@@ -542,6 +511,58 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
542511
}
543512
}
544513

514+
fn suggest_mutable_upvar(
515+
&self,
516+
upvar_index: FieldIdx,
517+
the_place_err: PlaceRef<'tcx>,
518+
err: &mut Diag<'infcx>,
519+
span: Span,
520+
act: &str,
521+
) {
522+
let captured_place = self.upvars[upvar_index.index()];
523+
524+
err.span_label(span, format!("cannot {act}"));
525+
526+
let upvar_hir_id = captured_place.get_root_variable();
527+
528+
if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id)
529+
&& let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) = pat.kind
530+
{
531+
if upvar_ident.name == kw::SelfLower {
532+
for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) {
533+
if let Some(fn_decl) = node.fn_decl() {
534+
if !matches!(
535+
fn_decl.implicit_self,
536+
hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
537+
) {
538+
err.span_suggestion_verbose(
539+
upvar_ident.span.shrink_to_lo(),
540+
"consider changing this to be mutable",
541+
"mut ",
542+
Applicability::MachineApplicable,
543+
);
544+
break;
545+
}
546+
}
547+
}
548+
} else {
549+
err.span_suggestion_verbose(
550+
upvar_ident.span.shrink_to_lo(),
551+
"consider changing this to be mutable",
552+
"mut ",
553+
Applicability::MachineApplicable,
554+
);
555+
}
556+
}
557+
558+
let tcx = self.infcx.tcx;
559+
if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
560+
&& let ty::Closure(id, _) = *ty.kind()
561+
{
562+
self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, err);
563+
}
564+
}
565+
545566
/// Suggest `map[k] = v` => `map.insert(k, v)` and the like.
546567
fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'infcx>, span: Span) {
547568
let Some(adt) = ty.ty_adt_def() else { return };

Diff for: compiler/rustc_borrowck/src/lib.rs

+22-5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use std::ops::{ControlFlow, Deref};
2323
use rustc_abi::FieldIdx;
2424
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
2525
use rustc_data_structures::graph::dominators::Dominators;
26+
use rustc_data_structures::unord::UnordMap;
2627
use rustc_errors::LintDiagnostic;
2728
use rustc_hir as hir;
2829
use rustc_hir::CRATE_HIR_ID;
@@ -261,6 +262,7 @@ fn do_mir_borrowck<'tcx>(
261262
regioncx: &regioncx,
262263
used_mut: Default::default(),
263264
used_mut_upvars: SmallVec::new(),
265+
local_from_upvars: UnordMap::default(),
264266
borrow_set: &borrow_set,
265267
upvars: &[],
266268
local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
@@ -286,6 +288,11 @@ fn do_mir_borrowck<'tcx>(
286288
promoted_mbcx.report_move_errors();
287289
}
288290

291+
let mut local_from_upvars = UnordMap::default();
292+
for (field, &local) in body.local_upvar_map.iter_enumerated() {
293+
let Some(local) = local else { continue };
294+
local_from_upvars.insert(local, field);
295+
}
289296
let mut mbcx = MirBorrowckCtxt {
290297
infcx: &infcx,
291298
body,
@@ -300,6 +307,7 @@ fn do_mir_borrowck<'tcx>(
300307
regioncx: &regioncx,
301308
used_mut: Default::default(),
302309
used_mut_upvars: SmallVec::new(),
310+
local_from_upvars,
303311
borrow_set: &borrow_set,
304312
upvars: tcx.closure_captures(def),
305313
local_names,
@@ -555,6 +563,9 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
555563
/// If the function we're checking is a closure, then we'll need to report back the list of
556564
/// mutable upvars that have been used. This field keeps track of them.
557565
used_mut_upvars: SmallVec<[FieldIdx; 8]>,
566+
/// Since upvars are moved to real locals, we need to map mutations to the locals back to
567+
/// the upvars, so that used_mut_upvars is up-to-date.
568+
local_from_upvars: UnordMap<Local, FieldIdx>,
558569
/// Region inference context. This contains the results from region inference and lets us e.g.
559570
/// find out which CFG points are contained in each borrow region.
560571
regioncx: &'a RegionInferenceContext<'tcx>,
@@ -2265,7 +2276,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
22652276

22662277
// at this point, we have set up the error reporting state.
22672278
if let Some(init_index) = previously_initialized {
2268-
if let (AccessKind::Mutate, Some(_)) = (error_access, place.as_local()) {
2279+
if let (AccessKind::Mutate, Some(local)) = (error_access, place.as_local())
2280+
&& self.body.local_upvar_map.iter().flatten().all(|upvar| upvar != &local)
2281+
{
22692282
// If this is a mutate access to an immutable local variable with no projections
22702283
// report the error as an illegal reassignment
22712284
let init = &self.move_data.inits[init_index];
@@ -2293,10 +2306,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
22932306
// If the local may have been initialized, and it is now currently being
22942307
// mutated, then it is justified to be annotated with the `mut`
22952308
// keyword, since the mutation may be a possible reassignment.
2296-
if is_local_mutation_allowed != LocalMutationIsAllowed::Yes
2297-
&& self.is_local_ever_initialized(local, state).is_some()
2298-
{
2299-
self.used_mut.insert(local);
2309+
if !matches!(is_local_mutation_allowed, LocalMutationIsAllowed::Yes) {
2310+
if self.is_local_ever_initialized(local, state).is_some() {
2311+
self.used_mut.insert(local);
2312+
} else if let Some(&field) = self.local_from_upvars.get(&local) {
2313+
self.used_mut_upvars.push(field);
2314+
}
23002315
}
23012316
}
23022317
RootPlace {
@@ -2314,6 +2329,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
23142329
projection: place_projection,
23152330
}) {
23162331
self.used_mut_upvars.push(field);
2332+
} else if let Some(&field) = self.local_from_upvars.get(&place_local) {
2333+
self.used_mut_upvars.push(field);
23172334
}
23182335
}
23192336
}

Diff for: compiler/rustc_borrowck/src/type_check/mod.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ use rustc_middle::ty::cast::CastTy;
2727
use rustc_middle::ty::fold::fold_regions;
2828
use rustc_middle::ty::visit::TypeVisitableExt;
2929
use rustc_middle::ty::{
30-
self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt,
31-
Dynamic, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, UserArgs,
30+
self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, Dynamic,
31+
GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, UserArgs,
3232
UserTypeAnnotationIndex,
3333
};
3434
use rustc_middle::{bug, span_bug};
@@ -1544,11 +1544,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
15441544
// It doesn't make sense to look at a field beyond the prefix;
15451545
// these require a variant index, and are not initialized in
15461546
// aggregate rvalues.
1547-
match args.as_coroutine().prefix_tys().get(field_index.as_usize()) {
1548-
Some(ty) => Ok(*ty),
1549-
None => Err(FieldAccessError::OutOfRange {
1550-
field_count: args.as_coroutine().prefix_tys().len(),
1551-
}),
1547+
let upvar_tys = &args.as_coroutine().upvar_tys();
1548+
if let Some(ty) = upvar_tys.get(field_index.as_usize()) {
1549+
Ok(*ty)
1550+
} else {
1551+
Err(FieldAccessError::OutOfRange { field_count: upvar_tys.len() })
15521552
}
15531553
}
15541554
AggregateKind::CoroutineClosure(_, args) => {

0 commit comments

Comments
 (0)