Auto merge of #123795 - matthiaskrgr:rollup-deesd9c, r=matthiaskrgr
Rollup of 4 pull requests Successful merges: - #123660 (Make the computation of `coroutine_captures_by_ref_ty` more sophisticated) - #123738 (Call lower_const_param instead of duplicating the code) - #123774 (Fix typo MaybeUnit -> MaybeUninit) - #123790 (correct the handling of `bootstrap-cache-path` option) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
df7daa815f
15 changed files with 470 additions and 113 deletions
|
@ -143,7 +143,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
|
||||||
};
|
};
|
||||||
assert!(
|
assert!(
|
||||||
ty.is_manually_drop(),
|
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 fields = &ty.non_enum_variant().fields;
|
||||||
let ty = fields[FieldIdx::ZERO].ty(self.tcx, args);
|
let ty = fields[FieldIdx::ZERO].ty(self.tcx, args);
|
||||||
|
|
|
@ -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))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
|
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
|
||||||
use self::hir::TraitItemKind::*;
|
use self::hir::TraitItemKind::*;
|
||||||
|
|
|
@ -2234,11 +2234,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
.type_of(def_id)
|
.type_of(def_id)
|
||||||
.no_bound_vars()
|
.no_bound_vars()
|
||||||
.expect("const parameter types cannot be generic");
|
.expect("const parameter types cannot be generic");
|
||||||
let item_def_id = tcx.parent(def_id);
|
self.lower_const_param(expr.hir_id, ty)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -367,37 +367,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
ty::INNERMOST,
|
ty::INNERMOST,
|
||||||
ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::BrEnv },
|
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(
|
let tupled_upvars_ty_for_borrow = Ty::new_tup_from_iter(
|
||||||
self.tcx,
|
self.tcx,
|
||||||
self.typeck_results
|
ty::analyze_coroutine_closure_captures(
|
||||||
.borrow()
|
typeck_results.closure_min_captures_flattened(closure_def_id),
|
||||||
.closure_min_captures_flattened(
|
typeck_results
|
||||||
self.tcx.coroutine_for_closure(closure_def_id).expect_local(),
|
.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
|
// Skip the captures that are just moving the closure's args
|
||||||
// those later in the `CoroutineClosureSignature` helper functions.
|
// into the coroutine. These are always by move, and we append
|
||||||
.skip(
|
// those later in the `CoroutineClosureSignature` helper functions.
|
||||||
args.as_coroutine_closure()
|
.skip(num_args),
|
||||||
.coroutine_closure_sig()
|
|(_, parent_capture), (_, child_capture)| {
|
||||||
.skip_binder()
|
// This is subtle. See documentation on function.
|
||||||
.tupled_inputs_ty
|
let needs_ref = should_reborrow_from_env_of_parent_coroutine_closure(
|
||||||
.tuple_fields()
|
parent_capture,
|
||||||
.len(),
|
child_capture,
|
||||||
)
|
);
|
||||||
.map(|captured_place| {
|
|
||||||
let upvar_ty = captured_place.place.ty();
|
let upvar_ty = child_capture.place.ty();
|
||||||
let capture = captured_place.info.capture_kind;
|
let capture = child_capture.info.capture_kind;
|
||||||
// Not all upvars are captured by ref, so use
|
// Not all upvars are captured by ref, so use
|
||||||
// `apply_capture_kind_on_capture_ty` to ensure that we
|
// `apply_capture_kind_on_capture_ty` to ensure that we
|
||||||
// compute the right captured type.
|
// compute the right captured type.
|
||||||
apply_capture_kind_on_capture_ty(
|
return apply_capture_kind_on_capture_ty(
|
||||||
self.tcx,
|
self.tcx,
|
||||||
upvar_ty,
|
upvar_ty,
|
||||||
capture,
|
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(
|
let coroutine_captures_by_ref_ty = Ty::new_fn_ptr(
|
||||||
self.tcx,
|
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,
|
/// 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)`.
|
/// which states that it's unsafe to take a reference into a struct marked `repr(packed)`.
|
||||||
fn restrict_repr_packed_field_ref_capture<'tcx>(
|
fn restrict_repr_packed_field_ref_capture<'tcx>(
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::{mir, ty};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use crate::query::Providers;
|
use crate::query::Providers;
|
||||||
|
use rustc_data_structures::captures::Captures;
|
||||||
use rustc_data_structures::fx::FxIndexMap;
|
use rustc_data_structures::fx::FxIndexMap;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::LocalDefId;
|
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<Item = &'a CapturedPlace<'tcx>>,
|
||||||
|
child_captures: impl IntoIterator<Item = &'a CapturedPlace<'tcx>>,
|
||||||
|
mut for_each: impl FnMut((usize, &'a CapturedPlace<'tcx>), (usize, &'a CapturedPlace<'tcx>)) -> T,
|
||||||
|
) -> impl Iterator<Item = T> + 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) {
|
pub fn provide(providers: &mut Providers) {
|
||||||
*providers = Providers { closure_typeinfo, ..*providers }
|
*providers = Providers { closure_typeinfo, ..*providers }
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,9 +77,10 @@ pub use rustc_type_ir::ConstKind::{
|
||||||
pub use rustc_type_ir::*;
|
pub use rustc_type_ir::*;
|
||||||
|
|
||||||
pub use self::closure::{
|
pub use self::closure::{
|
||||||
is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo,
|
analyze_coroutine_closure_captures, is_ancestor_or_same_capture, place_to_string_for_capture,
|
||||||
CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList,
|
BorrowKind, CaptureInfo, CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap,
|
||||||
RootVariableMinCaptureList, UpvarCapture, UpvarId, UpvarPath, CAPTURE_STRUCT_LOCAL,
|
MinCaptureList, RootVariableMinCaptureList, UpvarCapture, UpvarId, UpvarPath,
|
||||||
|
CAPTURE_STRUCT_LOCAL,
|
||||||
};
|
};
|
||||||
pub use self::consts::{
|
pub use self::consts::{
|
||||||
Const, ConstData, ConstInt, ConstKind, Expr, ScalarInt, UnevaluatedConst, ValTree,
|
Const, ConstData, ConstInt, ConstKind, Expr, ScalarInt, UnevaluatedConst, ValTree,
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
|
|
||||||
use rustc_data_structures::unord::UnordMap;
|
use rustc_data_structures::unord::UnordMap;
|
||||||
use rustc_hir as hir;
|
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::visit::MutVisitor;
|
||||||
use rustc_middle::mir::{self, dump_mir, MirPass};
|
use rustc_middle::mir::{self, dump_mir, MirPass};
|
||||||
use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt, TypeVisitableExt};
|
use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt, TypeVisitableExt};
|
||||||
|
@ -124,44 +124,10 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
|
||||||
.tuple_fields()
|
.tuple_fields()
|
||||||
.len();
|
.len();
|
||||||
|
|
||||||
let mut field_remapping = UnordMap::default();
|
let field_remapping: UnordMap<_, _> = ty::analyze_coroutine_closure_captures(
|
||||||
|
tcx.closure_captures(parent_def_id).iter().copied(),
|
||||||
let mut child_captures = tcx
|
tcx.closure_captures(coroutine_def_id).iter().skip(num_args).copied(),
|
||||||
.closure_captures(coroutine_def_id)
|
|(parent_field_idx, parent_capture), (child_field_idx, child_capture)| {
|
||||||
.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:#?})"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Store this set of additional projections (fields and derefs).
|
// Store this set of additional projections (fields and derefs).
|
||||||
// We need to re-apply them later.
|
// We need to re-apply them later.
|
||||||
let child_precise_captures =
|
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(child_field_idx + num_args),
|
||||||
(
|
(
|
||||||
FieldIdx::from_usize(parent_field_idx + num_args),
|
FieldIdx::from_usize(parent_field_idx + num_args),
|
||||||
|
@ -200,18 +166,10 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
|
||||||
needs_deref,
|
needs_deref,
|
||||||
child_precise_captures,
|
child_precise_captures,
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
|
},
|
||||||
field_used_at_least_once = true;
|
)
|
||||||
}
|
.collect();
|
||||||
|
|
||||||
// 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?");
|
|
||||||
|
|
||||||
if coroutine_kind == ty::ClosureKind::FnOnce {
|
if coroutine_kind == ty::ClosureKind::FnOnce {
|
||||||
assert_eq!(field_remapping.len(), tcx.closure_captures(parent_def_id).len());
|
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> {
|
struct MakeByMoveBody<'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
field_remapping: UnordMap<FieldIdx, (FieldIdx, Ty<'tcx>, bool, &'tcx [Projection<'tcx>])>,
|
field_remapping: UnordMap<FieldIdx, (FieldIdx, Ty<'tcx>, bool, &'tcx [Projection<'tcx>])>,
|
||||||
|
|
|
@ -302,7 +302,7 @@
|
||||||
|
|
||||||
# Set the bootstrap/download cache path. It is useful when building rust
|
# Set the bootstrap/download cache path. It is useful when building rust
|
||||||
# repeatedly in a CI invironment.
|
# 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
|
# 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"
|
# but also tools such as Cargo. This will also produce "combined installers"
|
||||||
|
|
|
@ -152,9 +152,9 @@ v("default-linker", "rust.default-linker", "the default linker")
|
||||||
# (others are conditionally saved).
|
# (others are conditionally saved).
|
||||||
o("manage-submodules", "build.submodules", "let the build manage the git submodules")
|
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("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")
|
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("tools", None, "List of extended tools will be installed")
|
||||||
v("codegen-backends", None, "List of codegen backends to build")
|
v("codegen-backends", None, "List of codegen backends to build")
|
||||||
v("build", "build.build", "GNUs ./configure syntax LLVM build triple")
|
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)
|
set('target.{}.llvm-filecheck'.format(build_triple), value, config)
|
||||||
elif option.name == 'tools':
|
elif option.name == 'tools':
|
||||||
set('build.tools', value.split(','), config)
|
set('build.tools', value.split(','), config)
|
||||||
|
elif option.name == 'bootstrap-cache-path':
|
||||||
|
set('build.bootstrap-cache-path', value, config)
|
||||||
elif option.name == 'codegen-backends':
|
elif option.name == 'codegen-backends':
|
||||||
set('rust.codegen-backends', value.split(','), config)
|
set('rust.codegen-backends', value.split(','), config)
|
||||||
elif option.name == 'host':
|
elif option.name == 'host':
|
||||||
|
|
|
@ -5,7 +5,6 @@ fn foo() -> Box<dyn std::future::Future<Output = u32>> {
|
||||||
let x = 0u32;
|
let x = 0u32;
|
||||||
Box::new((async || x)())
|
Box::new((async || x)())
|
||||||
//~^ ERROR cannot return value referencing local variable `x`
|
//~^ ERROR cannot return value referencing local variable `x`
|
||||||
//~| ERROR cannot return value referencing temporary value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -7,15 +7,6 @@ LL | Box::new((async || x)())
|
||||||
| | `x` is borrowed here
|
| | `x` is borrowed here
|
||||||
| returns a value referencing data owned by the current function
|
| returns a value referencing data owned by the current function
|
||||||
|
|
||||||
error[E0515]: cannot return value referencing temporary value
|
error: aborting due to 1 previous error
|
||||||
--> $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
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0515`.
|
For more information about this error, try `rustc --explain E0515`.
|
||||||
|
|
43
tests/ui/async-await/async-closures/moro-example.rs
Normal file
43
tests/ui/async-await/async-closures/moro-example.rs
Normal file
|
@ -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<Box<dyn Future<Output = T> + Send + 'a>>;
|
||||||
|
|
||||||
|
pub struct Scope<'scope, 'env: 'scope> {
|
||||||
|
enqueued: Mutex<Vec<BoxFuture<'scope, ()>>>,
|
||||||
|
phantom: PhantomData<&'env ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||||
|
pub fn spawn(&'scope self, future: impl Future<Output = ()> + 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() {}
|
44
tests/ui/async-await/async-closures/no-borrow-from-env.rs
Normal file
44
tests/ui/async-await/async-closures/no-borrow-from-env.rs
Normal file
|
@ -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() {}
|
|
@ -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() {}
|
|
@ -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`.
|
Loading…
Add table
Add a link
Reference in a new issue