From 568703c4bd5102c2d596e75db492c37d0102d16b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 9 Apr 2024 11:11:17 -0400 Subject: [PATCH 1/5] Use a helper to zip together parent and child captures for coroutine-closures --- compiler/rustc_middle/src/ty/closure.rs | 67 ++++++++++++++++ compiler/rustc_middle/src/ty/mod.rs | 7 +- .../src/coroutine/by_move_body.rs | 78 +++---------------- 3 files changed, 81 insertions(+), 71 deletions(-) diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 95d1e08b58b51..211d403998f88 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -6,6 +6,7 @@ use crate::{mir, ty}; use std::fmt::Write; use crate::query::Providers; +use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; @@ -415,6 +416,72 @@ impl BorrowKind { } } +pub fn analyze_coroutine_closure_captures<'a, 'tcx: 'a, T>( + parent_captures: impl IntoIterator>, + child_captures: impl IntoIterator>, + mut for_each: impl FnMut((usize, &'a CapturedPlace<'tcx>), (usize, &'a CapturedPlace<'tcx>)) -> T, +) -> impl Iterator + Captures<'a> + Captures<'tcx> { + std::iter::from_coroutine(move || { + let mut child_captures = child_captures.into_iter().enumerate().peekable(); + + // One parent capture may correspond to several child captures if we end up + // refining the set of captures via edition-2021 precise captures. We want to + // match up any number of child captures with one parent capture, so we keep + // peeking off this `Peekable` until the child doesn't match anymore. + for (parent_field_idx, parent_capture) in parent_captures.into_iter().enumerate() { + // Make sure we use every field at least once, b/c why are we capturing something + // if it's not used in the inner coroutine. + let mut field_used_at_least_once = false; + + // A parent matches a child if they share the same prefix of projections. + // The child may have more, if it is capturing sub-fields out of + // something that is captured by-move in the parent closure. + while child_captures.peek().map_or(false, |(_, child_capture)| { + child_prefix_matches_parent_projections(parent_capture, child_capture) + }) { + let (child_field_idx, child_capture) = child_captures.next().unwrap(); + // This analysis only makes sense if the parent capture is a + // prefix of the child capture. + assert!( + child_capture.place.projections.len() >= parent_capture.place.projections.len(), + "parent capture ({parent_capture:#?}) expected to be prefix of \ + child capture ({child_capture:#?})" + ); + + yield for_each( + (parent_field_idx, parent_capture), + (child_field_idx, child_capture), + ); + + field_used_at_least_once = true; + } + + // Make sure the field was used at least once. + assert!( + field_used_at_least_once, + "we captured {parent_capture:#?} but it was not used in the child coroutine?" + ); + } + assert_eq!(child_captures.next(), None, "leftover child captures?"); + }) +} + +fn child_prefix_matches_parent_projections( + parent_capture: &ty::CapturedPlace<'_>, + child_capture: &ty::CapturedPlace<'_>, +) -> bool { + let HirPlaceBase::Upvar(parent_base) = parent_capture.place.base else { + bug!("expected capture to be an upvar"); + }; + let HirPlaceBase::Upvar(child_base) = child_capture.place.base else { + bug!("expected capture to be an upvar"); + }; + + parent_base.var_path.hir_id == child_base.var_path.hir_id + && std::iter::zip(&child_capture.place.projections, &parent_capture.place.projections) + .all(|(child, parent)| child.kind == parent.kind) +} + pub fn provide(providers: &mut Providers) { *providers = Providers { closure_typeinfo, ..*providers } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ee4dc9744acb4..e6b773ae512d1 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -77,9 +77,10 @@ pub use rustc_type_ir::ConstKind::{ pub use rustc_type_ir::*; pub use self::closure::{ - is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo, - CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList, - RootVariableMinCaptureList, UpvarCapture, UpvarId, UpvarPath, CAPTURE_STRUCT_LOCAL, + analyze_coroutine_closure_captures, is_ancestor_or_same_capture, place_to_string_for_capture, + BorrowKind, CaptureInfo, CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap, + MinCaptureList, RootVariableMinCaptureList, UpvarCapture, UpvarId, UpvarPath, + CAPTURE_STRUCT_LOCAL, }; pub use self::consts::{ Const, ConstData, ConstInt, ConstKind, Expr, ScalarInt, UnevaluatedConst, ValTree, diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index b26f968bf5e8a..3d6c1a952041c 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -71,7 +71,7 @@ use rustc_data_structures::unord::UnordMap; use rustc_hir as hir; -use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind}; +use rustc_middle::hir::place::{Projection, ProjectionKind}; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::{self, dump_mir, MirPass}; use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt, TypeVisitableExt}; @@ -124,44 +124,10 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { .tuple_fields() .len(); - let mut field_remapping = UnordMap::default(); - - let mut child_captures = tcx - .closure_captures(coroutine_def_id) - .iter() - .copied() - // By construction we capture all the args first. - .skip(num_args) - .enumerate() - .peekable(); - - // One parent capture may correspond to several child captures if we end up - // refining the set of captures via edition-2021 precise captures. We want to - // match up any number of child captures with one parent capture, so we keep - // peeking off this `Peekable` until the child doesn't match anymore. - for (parent_field_idx, parent_capture) in - tcx.closure_captures(parent_def_id).iter().copied().enumerate() - { - // Make sure we use every field at least once, b/c why are we capturing something - // if it's not used in the inner coroutine. - let mut field_used_at_least_once = false; - - // A parent matches a child if they share the same prefix of projections. - // The child may have more, if it is capturing sub-fields out of - // something that is captured by-move in the parent closure. - while child_captures.peek().map_or(false, |(_, child_capture)| { - child_prefix_matches_parent_projections(parent_capture, child_capture) - }) { - let (child_field_idx, child_capture) = child_captures.next().unwrap(); - - // This analysis only makes sense if the parent capture is a - // prefix of the child capture. - assert!( - child_capture.place.projections.len() >= parent_capture.place.projections.len(), - "parent capture ({parent_capture:#?}) expected to be prefix of \ - child capture ({child_capture:#?})" - ); - + let field_remapping: UnordMap<_, _> = ty::analyze_coroutine_closure_captures( + tcx.closure_captures(parent_def_id).iter().copied(), + tcx.closure_captures(coroutine_def_id).iter().skip(num_args).copied(), + |(parent_field_idx, parent_capture), (child_field_idx, child_capture)| { // Store this set of additional projections (fields and derefs). // We need to re-apply them later. let child_precise_captures = @@ -192,7 +158,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { ), }; - field_remapping.insert( + ( FieldIdx::from_usize(child_field_idx + num_args), ( FieldIdx::from_usize(parent_field_idx + num_args), @@ -200,18 +166,10 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { needs_deref, child_precise_captures, ), - ); - - field_used_at_least_once = true; - } - - // Make sure the field was used at least once. - assert!( - field_used_at_least_once, - "we captured {parent_capture:#?} but it was not used in the child coroutine?" - ); - } - assert_eq!(child_captures.next(), None, "leftover child captures?"); + ) + }, + ) + .collect(); if coroutine_kind == ty::ClosureKind::FnOnce { assert_eq!(field_remapping.len(), tcx.closure_captures(parent_def_id).len()); @@ -241,22 +199,6 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { } } -fn child_prefix_matches_parent_projections( - parent_capture: &ty::CapturedPlace<'_>, - child_capture: &ty::CapturedPlace<'_>, -) -> bool { - let PlaceBase::Upvar(parent_base) = parent_capture.place.base else { - bug!("expected capture to be an upvar"); - }; - let PlaceBase::Upvar(child_base) = child_capture.place.base else { - bug!("expected capture to be an upvar"); - }; - - parent_base.var_path.hir_id == child_base.var_path.hir_id - && std::iter::zip(&child_capture.place.projections, &parent_capture.place.projections) - .all(|(child, parent)| child.kind == parent.kind) -} - struct MakeByMoveBody<'tcx> { tcx: TyCtxt<'tcx>, field_remapping: UnordMap, bool, &'tcx [Projection<'tcx>])>, From 599d456a7532aa5de020b42fd1d9d936fa647573 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 8 Apr 2024 21:09:13 -0400 Subject: [PATCH 2/5] Make the computation of coroutine_captures_by_ref_ty more sophisticated --- compiler/rustc_hir_typeck/src/upvar.rs | 114 ++++++++++--- .../async-borrowck-escaping-closure-error.rs | 1 - ...ync-borrowck-escaping-closure-error.stderr | 11 +- .../async-closures/moro-example.rs | 43 +++++ .../async-closures/no-borrow-from-env.rs | 44 +++++ ...thout-precise-captures-we-are-powerless.rs | 47 ++++++ ...t-precise-captures-we-are-powerless.stderr | 152 ++++++++++++++++++ 7 files changed, 378 insertions(+), 34 deletions(-) create mode 100644 tests/ui/async-await/async-closures/moro-example.rs create mode 100644 tests/ui/async-await/async-closures/no-borrow-from-env.rs create mode 100644 tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs create mode 100644 tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index c987bfb9a0e88..ef569b4bef3b3 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -367,37 +367,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::INNERMOST, ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::BrEnv }, ); + + let num_args = args + .as_coroutine_closure() + .coroutine_closure_sig() + .skip_binder() + .tupled_inputs_ty + .tuple_fields() + .len(); + let typeck_results = self.typeck_results.borrow(); + let tupled_upvars_ty_for_borrow = Ty::new_tup_from_iter( self.tcx, - self.typeck_results - .borrow() - .closure_min_captures_flattened( - self.tcx.coroutine_for_closure(closure_def_id).expect_local(), - ) - // Skip the captures that are just moving the closure's args - // into the coroutine. These are always by move, and we append - // those later in the `CoroutineClosureSignature` helper functions. - .skip( - args.as_coroutine_closure() - .coroutine_closure_sig() - .skip_binder() - .tupled_inputs_ty - .tuple_fields() - .len(), - ) - .map(|captured_place| { - let upvar_ty = captured_place.place.ty(); - let capture = captured_place.info.capture_kind; + ty::analyze_coroutine_closure_captures( + typeck_results.closure_min_captures_flattened(closure_def_id), + typeck_results + .closure_min_captures_flattened( + self.tcx.coroutine_for_closure(closure_def_id).expect_local(), + ) + // Skip the captures that are just moving the closure's args + // into the coroutine. These are always by move, and we append + // those later in the `CoroutineClosureSignature` helper functions. + .skip(num_args), + |(_, parent_capture), (_, child_capture)| { + // This is subtle. See documentation on function. + let needs_ref = should_reborrow_from_env_of_parent_coroutine_closure( + parent_capture, + child_capture, + ); + + let upvar_ty = child_capture.place.ty(); + let capture = child_capture.info.capture_kind; // Not all upvars are captured by ref, so use // `apply_capture_kind_on_capture_ty` to ensure that we // compute the right captured type. - apply_capture_kind_on_capture_ty( + return apply_capture_kind_on_capture_ty( self.tcx, upvar_ty, capture, - Some(closure_env_region), - ) - }), + if needs_ref { Some(closure_env_region) } else { child_capture.region }, + ); + }, + ), ); let coroutine_captures_by_ref_ty = Ty::new_fn_ptr( self.tcx, @@ -1761,6 +1772,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } +/// Determines whether a child capture that is derived from a parent capture +/// should be borrowed with the lifetime of the parent coroutine-closure's env. +/// +/// There are two cases when this needs to happen: +/// +/// (1.) Are we borrowing data owned by the parent closure? We can determine if +/// that is the case by checking if the parent capture is by move, EXCEPT if we +/// apply a deref projection, which means we're reborrowing a reference that we +/// captured by move. +/// +/// ```rust +/// #![feature(async_closure)] +/// let x = &1i32; // Let's call this lifetime `'1`. +/// let c = async move || { +/// println!("{:?}", *x); +/// // Even though the inner coroutine borrows by ref, we're only capturing `*x`, +/// // not `x`, so the inner closure is allowed to reborrow the data for `'1`. +/// }; +/// ``` +/// +/// (2.) If a coroutine is mutably borrowing from a parent capture, then that +/// mutable borrow cannot live for longer than either the parent *or* the borrow +/// that we have on the original upvar. Therefore we always need to borrow the +/// child capture with the lifetime of the parent coroutine-closure's env. +/// +/// ```rust +/// #![feature(async_closure)] +/// let mut x = 1i32; +/// let c = async || { +/// x = 1; +/// // The parent borrows `x` for some `&'1 mut i32`. +/// // However, when we call `c()`, we implicitly autoref for the signature of +/// // `AsyncFnMut::async_call_mut`. Let's call that lifetime `'call`. Since +/// // the maximum that `&'call mut &'1 mut i32` can be reborrowed is `&'call mut i32`, +/// // the inner coroutine should capture w/ the lifetime of the coroutine-closure. +/// }; +/// ``` +/// +/// If either of these cases apply, then we should capture the borrow with the +/// lifetime of the parent coroutine-closure's env. Luckily, if this function is +/// not correct, then the program is not unsound, since we still borrowck and validate +/// the choices made from this function -- the only side-effect is that the user +/// may receive unnecessary borrowck errors. +fn should_reborrow_from_env_of_parent_coroutine_closure<'tcx>( + parent_capture: &ty::CapturedPlace<'tcx>, + child_capture: &ty::CapturedPlace<'tcx>, +) -> bool { + // (1.) + (!parent_capture.is_by_ref() + && !matches!( + child_capture.place.projections.get(parent_capture.place.projections.len()), + Some(Projection { kind: ProjectionKind::Deref, .. }) + )) + // (2.) + || matches!(child_capture.info.capture_kind, UpvarCapture::ByRef(ty::BorrowKind::MutBorrow)) +} + /// Truncate the capture so that the place being borrowed is in accordance with RFC 1240, /// which states that it's unsafe to take a reference into a struct marked `repr(packed)`. fn restrict_repr_packed_field_ref_capture<'tcx>( diff --git a/tests/ui/async-await/async-borrowck-escaping-closure-error.rs b/tests/ui/async-await/async-borrowck-escaping-closure-error.rs index 1990d0ffe2a3f..ffb97ca04ac50 100644 --- a/tests/ui/async-await/async-borrowck-escaping-closure-error.rs +++ b/tests/ui/async-await/async-borrowck-escaping-closure-error.rs @@ -5,7 +5,6 @@ fn foo() -> Box> { let x = 0u32; Box::new((async || x)()) //~^ ERROR cannot return value referencing local variable `x` - //~| ERROR cannot return value referencing temporary value } fn main() { diff --git a/tests/ui/async-await/async-borrowck-escaping-closure-error.stderr b/tests/ui/async-await/async-borrowck-escaping-closure-error.stderr index be67c78221a7c..4b1ce300b5692 100644 --- a/tests/ui/async-await/async-borrowck-escaping-closure-error.stderr +++ b/tests/ui/async-await/async-borrowck-escaping-closure-error.stderr @@ -7,15 +7,6 @@ LL | Box::new((async || x)()) | | `x` is borrowed here | returns a value referencing data owned by the current function -error[E0515]: cannot return value referencing temporary value - --> $DIR/async-borrowck-escaping-closure-error.rs:6:5 - | -LL | Box::new((async || x)()) - | ^^^^^^^^^------------^^^ - | | | - | | temporary value created here - | returns a value referencing data owned by the current function - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/async-await/async-closures/moro-example.rs b/tests/ui/async-await/async-closures/moro-example.rs new file mode 100644 index 0000000000000..5a8f42c7ca5a4 --- /dev/null +++ b/tests/ui/async-await/async-closures/moro-example.rs @@ -0,0 +1,43 @@ +//@ check-pass +//@ edition: 2021 + +#![feature(async_closure)] + +use std::future::Future; +use std::pin::Pin; +use std::{marker::PhantomData, sync::Mutex}; + +type BoxFuture<'a, T> = Pin + Send + 'a>>; + +pub struct Scope<'scope, 'env: 'scope> { + enqueued: Mutex>>, + phantom: PhantomData<&'env ()>, +} + +impl<'scope, 'env: 'scope> Scope<'scope, 'env> { + pub fn spawn(&'scope self, future: impl Future + Send + 'scope) { + self.enqueued.lock().unwrap().push(Box::pin(future)); + } +} + +fn scope_with_closure<'env, B>(_body: B) -> BoxFuture<'env, ()> +where + for<'scope> B: async FnOnce(&'scope Scope<'scope, 'env>), +{ + todo!() +} + +type ScopeRef<'scope, 'env> = &'scope Scope<'scope, 'env>; + +async fn go<'a>(value: &'a i32) { + let closure = async |scope: ScopeRef<'_, 'a>| { + let _future1 = scope.spawn(async { + // Make sure that `*value` is immutably borrowed with lifetime of + // `'a` and not with the lifetime of the containing coroutine-closure. + let _v = *value; + }); + }; + scope_with_closure(closure).await; +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/no-borrow-from-env.rs b/tests/ui/async-await/async-closures/no-borrow-from-env.rs new file mode 100644 index 0000000000000..fe84aeeb32f7f --- /dev/null +++ b/tests/ui/async-await/async-closures/no-borrow-from-env.rs @@ -0,0 +1,44 @@ +//@ edition: 2021 +//@ check-pass + +#![feature(async_closure)] + +fn outlives<'a>(_: impl Sized + 'a) {} + +async fn call_once(f: impl async FnOnce()) { + f().await; +} + +fn simple<'a>(x: &'a i32) { + let c = async || { println!("{}", *x); }; + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); + + let c = async move || { println!("{}", *x); }; + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); +} + +struct S<'a>(&'a i32); + +fn through_field<'a>(x: S<'a>) { + let c = async || { println!("{}", *x.0); }; + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); + + let c = async move || { println!("{}", *x.0); }; + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); +} + +fn through_field_and_ref<'a>(x: &S<'a>) { + let c = async || { println!("{}", *x.0); }; + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); + + let c = async move || { println!("{}", *x.0); }; + outlives::<'a>(c()); + // outlives::<'a>(call_once(c)); // FIXME(async_closures): Figure out why this fails +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs new file mode 100644 index 0000000000000..17681161e20ed --- /dev/null +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs @@ -0,0 +1,47 @@ +//@ edition: 2018 + +// This is `no-borrow-from-env.rs`, but under edition 2018 we still want to make +// sure that we don't ICE or anything, even if precise closure captures means +// that we can't actually borrowck successfully. + +#![feature(async_closure)] + +fn outlives<'a>(_: impl Sized + 'a) {} + +async fn call_once(f: impl async FnOnce()) { + f().await; +} + +fn simple<'a>(x: &'a i32) { + let c = async || { println!("{}", *x); }; //~ ERROR `x` does not live long enough + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); + + let c = async move || { println!("{}", *x); }; + outlives::<'a>(c()); //~ ERROR `c` does not live long enough + outlives::<'a>(call_once(c)); //~ ERROR cannot move out of `c` +} + +struct S<'a>(&'a i32); + +fn through_field<'a>(x: S<'a>) { + let c = async || { println!("{}", *x.0); }; //~ ERROR `x` does not live long enough + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); + + let c = async move || { println!("{}", *x.0); }; //~ ERROR cannot move out of `x` + outlives::<'a>(c()); //~ ERROR `c` does not live long enough + outlives::<'a>(call_once(c)); //~ ERROR cannot move out of `c` +} + +fn through_field_and_ref<'a>(x: &S<'a>) { + let c = async || { println!("{}", *x.0); }; //~ ERROR `x` does not live long enough + outlives::<'a>(c()); + outlives::<'a>(call_once(c)); //~ ERROR explicit lifetime required in the type of `x` + + let c = async move || { println!("{}", *x.0); }; + outlives::<'a>(c()); //~ ERROR `c` does not live long enough + // outlives::<'a>(call_once(c)); // FIXME(async_closures): Figure out why this fails +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr new file mode 100644 index 0000000000000..569028934cbac --- /dev/null +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr @@ -0,0 +1,152 @@ +error[E0597]: `x` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:16:13 + | +LL | fn simple<'a>(x: &'a i32) { + | -- lifetime `'a` defined here +LL | let c = async || { println!("{}", *x); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough +LL | outlives::<'a>(c()); +LL | outlives::<'a>(call_once(c)); + | ------------ argument requires that `x` is borrowed for `'a` +... +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:21:20 + | +LL | fn simple<'a>(x: &'a i32) { + | -- lifetime `'a` defined here +... +LL | let c = async move || { println!("{}", *x); }; + | - binding `c` declared here +LL | outlives::<'a>(c()); + | ^-- + | | + | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +LL | outlives::<'a>(call_once(c)); +LL | } + | - `c` dropped here while still borrowed + +error[E0505]: cannot move out of `c` because it is borrowed + --> $DIR/without-precise-captures-we-are-powerless.rs:22:30 + | +LL | fn simple<'a>(x: &'a i32) { + | -- lifetime `'a` defined here +... +LL | let c = async move || { println!("{}", *x); }; + | - binding `c` declared here +LL | outlives::<'a>(c()); + | --- + | | + | borrow of `c` occurs here + | argument requires that `c` is borrowed for `'a` +LL | outlives::<'a>(call_once(c)); + | ^ move out of `c` occurs here + +error[E0597]: `x` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:28:13 + | +LL | fn through_field<'a>(x: S<'a>) { + | -- lifetime `'a` defined here +LL | let c = async || { println!("{}", *x.0); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough +LL | outlives::<'a>(c()); +LL | outlives::<'a>(call_once(c)); + | ------------ argument requires that `x` is borrowed for `'a` +... +LL | } + | - `x` dropped here while still borrowed + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/without-precise-captures-we-are-powerless.rs:32:13 + | +LL | fn through_field<'a>(x: S<'a>) { + | -- lifetime `'a` defined here +LL | let c = async || { println!("{}", *x.0); }; + | ---------------------------------- borrow of `x` occurs here +LL | outlives::<'a>(c()); +LL | outlives::<'a>(call_once(c)); + | ------------ argument requires that `x` is borrowed for `'a` +LL | +LL | let c = async move || { println!("{}", *x.0); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `x` occurs here + +error[E0597]: `c` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:33:20 + | +LL | fn through_field<'a>(x: S<'a>) { + | -- lifetime `'a` defined here +... +LL | let c = async move || { println!("{}", *x.0); }; + | - binding `c` declared here +LL | outlives::<'a>(c()); + | ^-- + | | + | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +LL | outlives::<'a>(call_once(c)); +LL | } + | - `c` dropped here while still borrowed + +error[E0505]: cannot move out of `c` because it is borrowed + --> $DIR/without-precise-captures-we-are-powerless.rs:34:30 + | +LL | fn through_field<'a>(x: S<'a>) { + | -- lifetime `'a` defined here +... +LL | let c = async move || { println!("{}", *x.0); }; + | - binding `c` declared here +LL | outlives::<'a>(c()); + | --- + | | + | borrow of `c` occurs here + | argument requires that `c` is borrowed for `'a` +LL | outlives::<'a>(call_once(c)); + | ^ move out of `c` occurs here + +error[E0597]: `x` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:38:13 + | +LL | fn through_field_and_ref<'a>(x: &S<'a>) { + | -- lifetime `'a` defined here +LL | let c = async || { println!("{}", *x.0); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough +LL | outlives::<'a>(c()); +LL | outlives::<'a>(call_once(c)); + | ------------ argument requires that `x` is borrowed for `'a` +... +LL | } + | - `x` dropped here while still borrowed + +error[E0621]: explicit lifetime required in the type of `x` + --> $DIR/without-precise-captures-we-are-powerless.rs:40:20 + | +LL | fn through_field_and_ref<'a>(x: &S<'a>) { + | ------ help: add explicit lifetime `'a` to the type of `x`: `&'a S<'a>` +... +LL | outlives::<'a>(call_once(c)); + | ^^^^^^^^^^^^ lifetime `'a` required + +error[E0597]: `c` does not live long enough + --> $DIR/without-precise-captures-we-are-powerless.rs:43:20 + | +LL | fn through_field_and_ref<'a>(x: &S<'a>) { + | -- lifetime `'a` defined here +... +LL | let c = async move || { println!("{}", *x.0); }; + | - binding `c` declared here +LL | outlives::<'a>(c()); + | ^-- + | | + | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +LL | // outlives::<'a>(call_once(c)); // FIXME(async_closures): Figure out why this fails +LL | } + | - `c` dropped here while still borrowed + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0505, E0597, E0621. +For more information about an error, try `rustc --explain E0505`. From 9984c5bbc1e7eb2f965a0bc05fa5344169490861 Mon Sep 17 00:00:00 2001 From: Janggun Lee Date: Thu, 11 Apr 2024 15:38:24 +0900 Subject: [PATCH 3/5] Fix small typo --- compiler/rustc_hir_analysis/src/check/intrinsicck.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index 1958a80d47c18..45ccd0fa2e068 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -143,7 +143,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { }; assert!( ty.is_manually_drop(), - "expected first field of `MaybeUnit` to be `ManuallyDrop`" + "expected first field of `MaybeUninit` to be `ManuallyDrop`" ); let fields = &ty.non_enum_variant().fields; let ty = fields[FieldIdx::ZERO].ty(self.tcx, args); From bd479113d38aa453cbad9d9f5ca9c5fc8903b0cf Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 11 Apr 2024 14:57:10 +0300 Subject: [PATCH 4/5] correct the handling of `bootstrap-cache-path` option This change makes `build.bootstrap-cache-path` option to be configurable with `./configure` script, so it can be used like `./configure --bootstrap-cache-path=demo`. Signed-off-by: onur-ozkan --- config.example.toml | 2 +- src/bootstrap/configure.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/config.example.toml b/config.example.toml index b8cdc2ec84804..0e8fda9a69c57 100644 --- a/config.example.toml +++ b/config.example.toml @@ -302,7 +302,7 @@ # Set the bootstrap/download cache path. It is useful when building rust # repeatedly in a CI invironment. -# bootstrap-cache-path = /shared/cache +#bootstrap-cache-path = /path/to/shared/cache # Enable a build of the extended Rust tool set which is not only the compiler # but also tools such as Cargo. This will also produce "combined installers" diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 818a7daadcab8..768aac912ce47 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -152,9 +152,9 @@ def v(*args): # (others are conditionally saved). o("manage-submodules", "build.submodules", "let the build manage the git submodules") o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two (not recommended except for testing reproducible builds)") -o("bootstrap-cache-path", "build.bootstrap-cache-path", "use provided path for the bootstrap cache") o("extended", "build.extended", "build an extended rust tool set") +v("bootstrap-cache-path", None, "use provided path for the bootstrap cache") v("tools", None, "List of extended tools will be installed") v("codegen-backends", None, "List of codegen backends to build") v("build", "build.build", "GNUs ./configure syntax LLVM build triple") @@ -359,6 +359,8 @@ def apply_args(known_args, option_checking, config): set('target.{}.llvm-filecheck'.format(build_triple), value, config) elif option.name == 'tools': set('build.tools', value.split(','), config) + elif option.name == 'bootstrap-cache-path': + set('build.bootstrap-cache-path', value, config) elif option.name == 'codegen-backends': set('rust.codegen-backends', value.split(','), config) elif option.name == 'host': From d51fda8bece46d07dec9696afc629095583c14a6 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 10 Apr 2024 16:26:00 -0300 Subject: [PATCH 5/5] Call lower_const_param instead of duplicating the code --- .../rustc_hir_analysis/src/collect/resolve_bound_vars.rs | 5 +++++ compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 6 +----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index affd678fc6cc7..3d16f1420d997 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -717,6 +717,11 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self))] + fn visit_pattern_type_pattern(&mut self, p: &'tcx hir::Pat<'tcx>) { + intravisit::walk_pat(self, p) + } + #[instrument(level = "debug", skip(self))] fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { use self::hir::TraitItemKind::*; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 9fb8b4ac40e5a..63aeb165a4808 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2234,11 +2234,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .type_of(def_id) .no_bound_vars() .expect("const parameter types cannot be generic"); - let item_def_id = tcx.parent(def_id); - let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id]; - let name = tcx.item_name(def_id); - ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty) + self.lower_const_param(expr.hir_id, ty) } _ => {