Skip to content

Commit aa1f01a

Browse files
Implement cromulent copy closure captures
Implements RFC 4011.
1 parent db8f076 commit aa1f01a

210 files changed

Lines changed: 4759 additions & 5022 deletions

File tree

Some content is hidden

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

‎compiler/rustc_hir_typeck/src/coercion.rs‎

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -505,26 +505,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
505505
}
506506
};
507507

508-
if coerced_a == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 {
509-
// As a special case, if we would produce `&'a *x`, that's
510-
// a total no-op. We end up with the type `&'a T` just as
511-
// we started with. In that case, just skip it altogether.
512-
//
513-
// Unfortunately, this can actually effect capture analysis
514-
// which in turn means this effects borrow checking. This can
515-
// also effect diagnostics.
516-
// FIXME(BoxyUwU): we should always emit reborrow coercions
517-
//
518-
// Note that for `&mut`, we DO want to reborrow --
519-
// otherwise, this would be a move, which might be an
520-
// error. For example `foo(self.x)` where `self` and
521-
// `self.x` both have `&mut `type would be a move of
522-
// `self.x`, but we auto-coerce it to `foo(&mut *self.x)`,
523-
// which is a borrow.
524-
assert!(mutbl_b.is_not()); // can only coerce &T -> &U
525-
return success(vec![], coerced_a, obligations);
526-
}
527-
528508
let InferOk { value: mut adjustments, obligations: o } =
529509
self.adjust_steps_as_infer_ok(&autoderef);
530510
obligations.extend(o);
@@ -536,10 +516,31 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
536516
coerced_a
537517
);
538518

539-
// Now apply the autoref
540-
let mutbl = AutoBorrowMutability::new(mutbl_b, self.allow_two_phase);
541-
adjustments
542-
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: coerced_a });
519+
if let Some(Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), .. }) =
520+
adjustments.last()
521+
&& let ty::Ref(_, _, ty::Mutability::Not) = if adjustments.len() >= 2 {
522+
*adjustments[adjustments.len() - 2].target.kind()
523+
} else {
524+
ty::Ref(r_a, mt_a.ty, mt_a.mutbl)
525+
}
526+
&& mutbl_b.is_not()
527+
{
528+
// As a special case, if we would produce `&'a *x`, that's
529+
// a total no-op. We end up with the type `&'a T` just as
530+
// we started with. In that case, just skip it altogether.
531+
//
532+
// FIXME: Ideally we would also skip `&mut` -> `&mut` reborrow,
533+
// and handle that in MIR. But unfortunately that would affect
534+
// closure capture inference, which affects borrowcheck
535+
adjustments.pop();
536+
} else {
537+
// Now apply the autoref
538+
let mutbl = AutoBorrowMutability::new(mutbl_b, self.allow_two_phase);
539+
adjustments.push(Adjustment {
540+
kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
541+
target: coerced_a,
542+
});
543+
}
543544

544545
debug!("coerce_to_ref: succeeded coerced_a={:?} adjustments={:?}", coerced_a, adjustments);
545546

‎compiler/rustc_hir_typeck/src/expr_use_visitor.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
10991099
upvar_borrow,
11001100
);
11011101
}
1102+
ty::UpvarCapture::ByCopy => {
1103+
self.delegate.borrow_mut().copy(&place_with_id, place_with_id.hir_id);
1104+
}
11021105
}
11031106
}
11041107
}

‎compiler/rustc_hir_typeck/src/upvar.rs‎

Lines changed: 143 additions & 73 deletions
Large diffs are not rendered by default.

‎compiler/rustc_middle/src/hir/place.rs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ impl<'tcx> Place<'tcx> {
126126
/// Returns the type of this `Place` immediately before `projection_index`th projection
127127
/// is applied.
128128
pub fn ty_before_projection(&self, projection_index: usize) -> Ty<'tcx> {
129-
assert!(projection_index < self.projections.len());
130129
if projection_index == 0 { self.base_ty } else { self.projections[projection_index - 1].ty }
131130
}
132131
}

‎compiler/rustc_middle/src/ty/closure.rs‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ impl UpvarId {
4747
#[derive(Eq, PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, StableHash, Hash)]
4848
#[derive(TypeFoldable, TypeVisitable)]
4949
pub enum UpvarCapture {
50+
/// Like `ByValue`, but dominated by all the other modes instead of dominating them.
51+
ByCopy,
52+
5053
/// Upvar is captured by reference.
5154
ByRef(BorrowKind),
5255

@@ -64,11 +67,15 @@ impl PartialOrd for UpvarCapture {
6467
#[inline]
6568
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
6669
match (self, other) {
67-
(Self::ByValue, Self::ByValue) | (Self::ByUse, Self::ByUse) => {
68-
Some(cmp::Ordering::Equal)
70+
(Self::ByCopy, Self::ByCopy)
71+
| (Self::ByValue, Self::ByValue)
72+
| (Self::ByUse, Self::ByUse) => Some(cmp::Ordering::Equal),
73+
(_, Self::ByCopy) | (Self::ByValue | Self::ByUse, Self::ByRef(_)) => {
74+
Some(cmp::Ordering::Greater)
75+
}
76+
(Self::ByCopy, _) | (Self::ByRef(_), Self::ByValue | Self::ByUse) => {
77+
Some(cmp::Ordering::Less)
6978
}
70-
(Self::ByValue | Self::ByUse, Self::ByRef(_)) => Some(cmp::Ordering::Greater),
71-
(Self::ByRef(_), Self::ByValue | Self::ByUse) => Some(cmp::Ordering::Less),
7279
(Self::ByRef(left), Self::ByRef(right)) => Some(left.cmp(&right)),
7380
(Self::ByUse, Self::ByValue) | (Self::ByValue, Self::ByUse) => None,
7481
}
@@ -202,7 +209,7 @@ impl<'tcx> CapturedPlace<'tcx> {
202209

203210
pub fn is_by_ref(&self) -> bool {
204211
match self.info.capture_kind {
205-
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => false,
212+
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse | ty::UpvarCapture::ByCopy => false,
206213
ty::UpvarCapture::ByRef(..) => true,
207214
}
208215
}

‎compiler/rustc_mir_build/src/builder/mod.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
5353
.map(|captured_place| {
5454
let name = captured_place.to_symbol();
5555
match captured_place.info.capture_kind {
56-
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => name,
56+
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse | ty::UpvarCapture::ByCopy => {
57+
name
58+
}
5759
ty::UpvarCapture::ByRef(..) => Symbol::intern(&format!("_ref__{name}")),
5860
}
5961
})
@@ -916,7 +918,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
916918
let mut projs = closure_env_projs.clone();
917919
projs.push(ProjectionElem::Field(FieldIdx::new(i), ty));
918920
match capture {
919-
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {}
921+
ty::UpvarCapture::ByValue
922+
| ty::UpvarCapture::ByUse
923+
| ty::UpvarCapture::ByCopy => {}
920924
ty::UpvarCapture::ByRef(..) => {
921925
projs.push(ProjectionElem::Deref);
922926
}

‎compiler/rustc_mir_build/src/thir/cx/expr.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1647,7 +1647,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
16471647
let temp_scope_id = closure_expr.hir_id.local_id;
16481648

16491649
match upvar_capture {
1650-
ty::UpvarCapture::ByValue => captured_place_expr,
1650+
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByCopy => captured_place_expr,
16511651
ty::UpvarCapture::ByUse => {
16521652
let span = captured_place_expr.span;
16531653
let expr_id = self.thir.exprs.push(captured_place_expr);

‎compiler/rustc_mir_transform/src/coroutine/by_move_body.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,9 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>(
170170
// this when building the field projection in the MIR body later on.
171171
let mut parent_capture_ty = parent_capture.place.ty();
172172
parent_capture_ty = match parent_capture.info.capture_kind {
173-
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => parent_capture_ty,
173+
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse | ty::UpvarCapture::ByCopy => {
174+
parent_capture_ty
175+
}
174176
ty::UpvarCapture::ByRef(kind) => Ty::new_ref(
175177
tcx,
176178
tcx.lifetimes.re_erased,

‎compiler/rustc_mir_transform/src/shim.rs‎

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,18 +1244,6 @@ fn build_construct_coroutine_by_move_shim<'tcx>(
12441244

12451245
for (idx, ty) in args.as_coroutine_closure().upvar_tys().iter().enumerate() {
12461246
if receiver_by_ref {
1247-
// The only situation where it's possible is when we capture immuatable references,
1248-
// since those don't need to be reborrowed with the closure's env lifetime. Since
1249-
// references are always `Copy`, just emit a copy.
1250-
if !matches!(ty.kind(), ty::Ref(_, _, hir::Mutability::Not)) {
1251-
// This copy is only sound if it's a `&T`. This may be
1252-
// reachable e.g. when eagerly computing the `Fn` instance
1253-
// of an async closure that doesn't borrowck.
1254-
tcx.dcx().delayed_bug(format!(
1255-
"field should be captured by immutable ref if we have \
1256-
an `Fn` instance, but it was: {ty}"
1257-
));
1258-
}
12591247
fields.push(Operand::Copy(tcx.mk_place_field(
12601248
self_local,
12611249
FieldIdx::from_usize(idx),

‎src/bootstrap/src/core/builder/mod.rs‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,10 +1120,9 @@ impl<'a> Builder<'a> {
11201120
Subcommand::Run { .. } => (Kind::Run, &paths[..]),
11211121
Subcommand::Clean { .. } => (Kind::Clean, &paths[..]),
11221122
Subcommand::Format { .. } => (Kind::Format, &[][..]),
1123-
Subcommand::Setup { profile: ref path } => (
1124-
Kind::Setup,
1125-
path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)),
1126-
),
1123+
Subcommand::Setup { profile: ref path } => {
1124+
(Kind::Setup, path.as_ref().map_or([].as_slice(), std::slice::from_ref))
1125+
}
11271126
Subcommand::Vendor { .. } => (Kind::Vendor, &paths[..]),
11281127
Subcommand::Perf { .. } => (Kind::Perf, &paths[..]),
11291128
};

0 commit comments

Comments
 (0)