1
Fork 0

Auto merge of #79553 - sexxi-goose:mir_min_cap_writeback, r=nikomatsakis

Capture precise paths in THIR and MIR

This PR allows THIR and MIR to use the result of the new capture analysis to actually capture precise paths

To achieve we:
- Writeback min capture results to TypeckResults
- Move handling upvars to PlaceBuilder in mir_build
- Lower precise paths in THIR build by reading min_captures
- Search for ancestors in min_capture when trying to build a MIR place which starts off of an upvar

Closes: https://github.com/rust-lang/project-rfc-2229/issues/10

Partly implements: rust-lang/project-rfc-2229#18

Work that remains (not in this PR):
- [ ] [Known bugs when feature gate is enabled](https://github.com/rust-lang/project-rfc-2229/projects/1?card_filter_query=label%3Abug)
- [ ] Use min_capure_map for
  - [ ] Liveness analysis
  - [ ] rustc_mir/interpret/validity.rs
  - [ ] regionck
- [ ] rust-lang/project-rfc-2229#8
- [ ] remove closure_captures and upvar_capture_map

r? `@ghost`
This commit is contained in:
bors 2020-12-12 00:23:29 +00:00
commit 5bd9b60333
41 changed files with 1479 additions and 229 deletions

View file

@ -4,14 +4,62 @@ use crate::build::expr::category::Category;
use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::thir::*;
use rustc_hir::def_id::DefId;
use rustc_hir::HirId;
use rustc_middle::middle::region;
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
use rustc_middle::mir::AssertKind::BoundsCheck;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
use rustc_span::Span;
use rustc_target::abi::VariantIdx;
use rustc_index::vec::Idx;
/// The "outermost" place that holds this value.
#[derive(Copy, Clone)]
crate enum PlaceBase {
/// Denotes the start of a `Place`.
Local(Local),
/// When building place for an expression within a closure, the place might start off a
/// captured path. When `capture_disjoint_fields` is enabled, we might not know the capture
/// index (within the desugared closure) of the captured path until most of the projections
/// are applied. We use `PlaceBase::Upvar` to keep track of the root variable off of which the
/// captured path starts, the closure the capture belongs to and the trait the closure
/// implements.
///
/// Once we have figured out the capture index, we can convert the place builder to start from
/// `PlaceBase::Local`.
///
/// Consider the following example
/// ```rust
/// let t = (10, (10, (10, 10)));
///
/// let c = || {
/// println!("{}", t.0.0.0);
/// };
/// ```
/// Here the THIR expression for `t.0.0.0` will be something like
///
/// ```
/// * Field(0)
/// * Field(0)
/// * Field(0)
/// * UpvarRef(t)
/// ```
///
/// When `capture_disjoint_fields` is enabled, `t.0.0.0` is captured and we won't be able to
/// figure out that it is captured until all the `Field` projections are applied.
Upvar {
/// HirId of the upvar
var_hir_id: HirId,
/// DefId of the closure
closure_def_id: DefId,
/// The trait closure implements, `Fn`, `FnMut`, `FnOnce`
closure_kind: ty::ClosureKind },
}
/// `PlaceBuilder` is used to create places during MIR construction. It allows you to "build up" a
/// place by pushing more and more projections onto the end, and then convert the final set into a
/// place using the `into_place` method.
@ -19,14 +67,240 @@ use rustc_index::vec::Idx;
/// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
#[derive(Clone)]
struct PlaceBuilder<'tcx> {
local: Local,
crate struct PlaceBuilder<'tcx> {
base: PlaceBase,
projection: Vec<PlaceElem<'tcx>>,
}
/// Given a list of MIR projections, convert them to list of HIR ProjectionKind.
/// The projections are truncated to represent a path that might be captured by a
/// closure/generator. This implies the vector returned from this function doesn't contain
/// ProjectionElems `Downcast`, `ConstantIndex`, `Index`, or `Subslice` because those will never be
/// part of a path that is captued by a closure. We stop applying projections once we see the first
/// projection that isn't captured by a closure.
fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
mir_projections: &Vec<PlaceElem<'tcx>>,
) -> Vec<HirProjectionKind> {
let mut hir_projections = Vec::new();
for mir_projection in mir_projections {
let hir_projection = match mir_projection {
ProjectionElem::Deref => HirProjectionKind::Deref,
ProjectionElem::Field(field, _) => {
// We will never encouter this for multivariant enums,
// read the comment for `Downcast`.
HirProjectionKind::Field(field.index() as u32, VariantIdx::new(0))
},
ProjectionElem::Downcast(..) => {
// This projections exist only for enums that have
// multiple variants. Since such enums that are captured
// completely, we can stop here.
break
},
ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {
// We don't capture array-access projections.
// We can stop here as arrays are captured completely.
break
},
};
hir_projections.push(hir_projection);
}
hir_projections
}
/// Return true if the `proj_possible_ancestor` represents an ancestor path
/// to `proj_capture` or `proj_possible_ancestor` is same as `proj_capture`,
/// assuming they both start off of the same root variable.
///
/// **Note:** It's the caller's responsibility to ensure that both lists of projections
/// start off of the same root variable.
///
/// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of
/// `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`.
/// Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`.
/// 2. Since we only look at the projections here function will return `bar.x` as an a valid
/// ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections
/// list are being applied to the same root variable.
fn is_ancestor_or_same_capture(
proj_possible_ancestor: &Vec<HirProjectionKind>,
proj_capture: &Vec<HirProjectionKind>,
) -> bool {
// We want to make sure `is_ancestor_or_same_capture("x.0.0", "x.0")` to return false.
// Therefore we can't just check if all projections are same in the zipped iterator below.
if proj_possible_ancestor.len() > proj_capture.len() {
return false;
}
proj_possible_ancestor.iter().zip(proj_capture).all(|(a, b)| a == b)
}
/// Computes the index of a capture within the desugared closure provided the closure's
/// `closure_min_captures` and the capture's index of the capture in the
/// `ty::MinCaptureList` of the root variable `var_hir_id`.
fn compute_capture_idx<'tcx>(
closure_min_captures: &ty::RootVariableMinCaptureList<'tcx>,
var_hir_id: HirId,
root_var_idx: usize,
) -> usize {
let mut res = 0;
for (var_id, capture_list) in closure_min_captures {
if *var_id == var_hir_id {
res += root_var_idx;
break;
} else {
res += capture_list.len();
}
}
res
}
/// Given a closure, returns the index of a capture within the desugared closure struct and the
/// `ty::CapturedPlace` which is the ancestor of the Place represented using the `var_hir_id`
/// and `projection`.
///
/// Note there will be at most one ancestor for any given Place.
///
/// Returns None, when the ancestor is not found.
fn find_capture_matching_projections<'a, 'tcx>(
typeck_results: &'a ty::TypeckResults<'tcx>,
var_hir_id: HirId,
closure_def_id: DefId,
projections: &Vec<PlaceElem<'tcx>>,
) -> Option<(usize, &'a ty::CapturedPlace<'tcx>)> {
let closure_min_captures = typeck_results.closure_min_captures.get(&closure_def_id)?;
let root_variable_min_captures = closure_min_captures.get(&var_hir_id)?;
let hir_projections = convert_to_hir_projections_and_truncate_for_capture(projections);
// If an ancestor is found, `idx` is the index within the list of captured places
// for root variable `var_hir_id` and `capture` is the `ty::CapturedPlace` itself.
let (idx, capture) = root_variable_min_captures.iter().enumerate().find(|(_, capture)| {
let possible_ancestor_proj_kinds =
capture.place.projections.iter().map(|proj| proj.kind).collect();
is_ancestor_or_same_capture(&possible_ancestor_proj_kinds, &hir_projections)
})?;
// Convert index to be from the presepective of the entire closure_min_captures map
// instead of just the root variable capture list
Some((compute_capture_idx(closure_min_captures, var_hir_id, idx), capture))
}
/// Takes a PlaceBuilder and resolves the upvar (if any) within it, so that the
/// `PlaceBuilder` now starts from `PlaceBase::Local`.
///
/// Returns a Result with the error being the HirId of the Upvar that was not found.
fn to_upvars_resolved_place_builder<'a, 'tcx>(
from_builder: PlaceBuilder<'tcx>,
tcx: TyCtxt<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
) -> Result<PlaceBuilder<'tcx>, HirId> {
match from_builder.base {
PlaceBase::Local(_) => Ok(from_builder),
PlaceBase::Upvar { var_hir_id, closure_def_id, closure_kind } => {
// Captures are represented using fields inside a structure.
// This represents accessing self in the closure structure
let mut upvar_resolved_place_builder = PlaceBuilder::from(Local::new(1));
match closure_kind {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
upvar_resolved_place_builder = upvar_resolved_place_builder.deref();
}
ty::ClosureKind::FnOnce => {}
}
let (capture_index, capture) =
if let Some(capture_details) = find_capture_matching_projections(
typeck_results,
var_hir_id,
closure_def_id,
&from_builder.projection,
) {
capture_details
} else {
if !tcx.features().capture_disjoint_fields {
bug!(
"No associated capture found for {:?}[{:#?}] even though \
capture_disjoint_fields isn't enabled",
var_hir_id,
from_builder.projection
)
} else {
// FIXME(project-rfc-2229#24): Handle this case properly
debug!(
"No associated capture found for {:?}[{:#?}]",
var_hir_id,
from_builder.projection,
);
}
return Err(var_hir_id);
};
let closure_ty =
typeck_results.node_type(tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()));
let substs = match closure_ty.kind() {
ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
_ => bug!("Lowering capture for non-closure type {:?}", closure_ty),
};
// Access the capture by accessing the field within the Closure struct.
//
// We must have inferred the capture types since we are building MIR, therefore
// it's safe to call `tuple_element_ty` and we can unwrap here because
// we know that the capture exists and is the `capture_index`-th capture.
let var_ty = substs.tupled_upvars_ty().tuple_element_ty(capture_index).unwrap();
upvar_resolved_place_builder = upvar_resolved_place_builder.field(Field::new(capture_index), var_ty);
// If the variable is captured via ByRef(Immutable/Mutable) Borrow,
// we need to deref it
upvar_resolved_place_builder = match capture.info.capture_kind {
ty::UpvarCapture::ByRef(_) => upvar_resolved_place_builder.deref(),
ty::UpvarCapture::ByValue(_) => upvar_resolved_place_builder,
};
let next_projection = capture.place.projections.len();
let mut curr_projections = from_builder.projection;
// We used some of the projections to build the capture itself,
// now we apply the remaining to the upvar resolved place.
upvar_resolved_place_builder.projection.extend(
curr_projections.drain(next_projection..));
Ok(upvar_resolved_place_builder)
}
}
}
impl<'tcx> PlaceBuilder<'tcx> {
fn into_place(self, tcx: TyCtxt<'tcx>) -> Place<'tcx> {
Place { local: self.local, projection: tcx.intern_place_elems(&self.projection) }
crate fn into_place<'a>(
self,
tcx: TyCtxt<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
) -> Place<'tcx> {
if let PlaceBase::Local(local) = self.base {
Place { local, projection: tcx.intern_place_elems(&self.projection) }
} else {
self.expect_upvars_resolved(tcx, typeck_results).into_place(tcx, typeck_results)
}
}
fn expect_upvars_resolved<'a>(
self,
tcx: TyCtxt<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
) -> PlaceBuilder<'tcx> {
to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap()
}
crate fn base(&self) -> PlaceBase {
self.base
}
fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
@ -49,7 +323,13 @@ impl<'tcx> PlaceBuilder<'tcx> {
impl<'tcx> From<Local> for PlaceBuilder<'tcx> {
fn from(local: Local) -> Self {
Self { local, projection: Vec::new() }
Self { base: PlaceBase::Local(local), projection: Vec::new() }
}
}
impl<'tcx> From<PlaceBase> for PlaceBuilder<'tcx> {
fn from(base: PlaceBase) -> Self {
Self { base, projection: Vec::new() }
}
}
@ -71,12 +351,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
M: Mirror<'tcx, Output = Expr<'tcx>>,
{
let place_builder = unpack!(block = self.as_place_builder(block, expr));
block.and(place_builder.into_place(self.hir.tcx()))
block.and(place_builder.into_place(self.hir.tcx(), self.hir.typeck_results()))
}
/// This is used when constructing a compound `Place`, so that we can avoid creating
/// intermediate `Place` values until we know the full set of projections.
fn as_place_builder<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<PlaceBuilder<'tcx>>
crate fn as_place_builder<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<PlaceBuilder<'tcx>>
where
M: Mirror<'tcx, Output = Expr<'tcx>>,
{
@ -98,7 +378,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
M: Mirror<'tcx, Output = Expr<'tcx>>,
{
let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
block.and(place_builder.into_place(self.hir.tcx()))
block.and(place_builder.into_place(self.hir.tcx(), self.hir.typeck_results()))
}
/// This is used when constructing a compound `Place`, so that we can avoid creating
@ -161,27 +441,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
source_info,
),
ExprKind::UpvarRef { closure_def_id, var_hir_id } => {
let capture = this
.hir
.typeck_results
.closure_captures
.get(&closure_def_id)
.and_then(|captures| captures.get_full(&var_hir_id));
if capture.is_none() {
if !this.hir.tcx().features().capture_disjoint_fields {
bug!(
"No associated capture found for {:?} even though \
capture_disjoint_fields isn't enabled",
expr.kind
)
}
// FIXME(project-rfc-2229#24): Handle this case properly
}
// Unwrap until the FIXME has been resolved
let (capture_index, _, upvar_id) = capture.unwrap();
this.lower_closure_capture(block, capture_index, *upvar_id)
let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id.expect_local());
this.lower_captured_upvar(block, upvar_id)
}
ExprKind::VarRef { id } => {
@ -208,7 +469,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
inferred_ty: expr.ty,
});
let place = place_builder.clone().into_place(this.hir.tcx());
let place =
place_builder.clone().into_place(this.hir.tcx(), this.hir.typeck_results());
this.cfg.push(
block,
Statement {
@ -293,59 +555,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
/// Lower a closure/generator capture by representing it as a field
/// access within the desugared closure/generator.
///
/// `capture_index` is the index of the capture within the desugared
/// closure/generator.
fn lower_closure_capture(
/// Lower a captured upvar. Note we might not know the actual capture index,
/// so we create a place starting from `PlaceBase::Upvar`, which will be resolved
/// once all projections that allow us to indentify a capture have been applied.
fn lower_captured_upvar(
&mut self,
block: BasicBlock,
capture_index: usize,
upvar_id: ty::UpvarId,
) -> BlockAnd<PlaceBuilder<'tcx>> {
) -> BlockAnd<PlaceBuilder<'tcx>> {
let closure_ty = self
.hir
.typeck_results()
.node_type(self.hir.tcx().hir().local_def_id_to_hir_id(upvar_id.closure_expr_id));
// Captures are represented using fields inside a structure.
// This represents accessing self in the closure structure
let mut place_builder = PlaceBuilder::from(Local::new(1));
// In case of Fn/FnMut closures we must deref to access the fields
// Generators are considered FnOnce, so we ignore this step for them.
if let ty::Closure(_, closure_substs) = closure_ty.kind() {
match self.hir.infcx().closure_kind(closure_substs).unwrap() {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
place_builder = place_builder.deref();
}
ty::ClosureKind::FnOnce => {}
}
}
let substs = match closure_ty.kind() {
ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
_ => bug!("Lowering capture for non-closure type {:?}", closure_ty)
let closure_kind = if let ty::Closure(_, closure_substs) = closure_ty.kind() {
self.hir.infcx().closure_kind(closure_substs).unwrap()
} else {
// Generators are considered FnOnce.
ty::ClosureKind::FnOnce
};
// Access the capture by accessing the field within the Closure struct.
//
// We must have inferred the capture types since we are building MIR, therefore
// it's safe to call `upvar_tys` and we can unwrap here because
// we know that the capture exists and is the `capture_index`-th capture.
let var_ty = substs.upvar_tys().nth(capture_index).unwrap();
place_builder = place_builder.field(Field::new(capture_index), var_ty);
// If the variable is captured via ByRef(Immutable/Mutable) Borrow,
// we need to deref it
match self.hir.typeck_results.upvar_capture(upvar_id) {
ty::UpvarCapture::ByRef(_) => {
block.and(place_builder.deref())
}
ty::UpvarCapture::ByValue(_) => block.and(place_builder),
}
block.and(PlaceBuilder::from(PlaceBase::Upvar {
var_hir_id: upvar_id.var_path.hir_id,
closure_def_id: upvar_id.closure_expr_id.to_def_id(),
closure_kind,
}))
}
/// Lower an index expression
@ -373,7 +607,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let is_outermost_index = fake_borrow_temps.is_none();
let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps);
let base_place =
let mut base_place =
unpack!(block = self.expr_as_place(block, lhs, mutability, Some(fake_borrow_temps),));
// Making this a *fresh* temporary means we do not have to worry about
@ -383,7 +617,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block = self.bounds_check(
block,
base_place.clone().into_place(self.hir.tcx()),
base_place.clone().into_place(self.hir.tcx(), self.hir.typeck_results()),
idx,
expr_span,
source_info,
@ -392,6 +626,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if is_outermost_index {
self.read_fake_borrows(block, fake_borrow_temps, source_info)
} else {
base_place = base_place.expect_upvars_resolved(self.hir.tcx(), self.hir.typeck_results());
self.add_fake_borrows_of_base(
&base_place,
block,
@ -441,8 +676,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
source_info: SourceInfo,
) {
let tcx = self.hir.tcx();
let place_ty =
Place::ty_from(base_place.local, &base_place.projection, &self.local_decls, tcx);
let local = match base_place.base {
PlaceBase::Local(local) => local,
PlaceBase::Upvar { .. } => bug!("Expected PlacseBase::Local found Upvar")
};
let place_ty = Place::ty_from(local, &base_place.projection, &self.local_decls, tcx);
if let ty::Slice(_) = place_ty.ty.kind() {
// We need to create fake borrows to ensure that the bounds
// check that we just did stays valid. Since we can't assign to
@ -452,7 +691,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match elem {
ProjectionElem::Deref => {
let fake_borrow_deref_ty = Place::ty_from(
base_place.local,
local,
&base_place.projection[..idx],
&self.local_decls,
tcx,
@ -470,14 +709,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Rvalue::Ref(
tcx.lifetimes.re_erased,
BorrowKind::Shallow,
Place { local: base_place.local, projection },
Place { local, projection },
),
);
fake_borrow_temps.push(fake_borrow_temp);
}
ProjectionElem::Index(_) => {
let index_ty = Place::ty_from(
base_place.local,
local,
&base_place.projection[..idx],
&self.local_decls,
tcx,

View file

@ -4,6 +4,7 @@ use rustc_index::vec::Idx;
use crate::build::expr::category::{Category, RvalueFunc};
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::build::expr::as_place::PlaceBase;
use crate::thir::*;
use rustc_middle::middle::region;
use rustc_middle::mir::AssertKind;
@ -393,44 +394,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
let arg_place = unpack!(block = this.as_place(block, arg));
let arg_place_builder = unpack!(block = this.as_place_builder(block, arg));
let mutability = match arg_place.as_ref() {
PlaceRef { local, projection: &[] } => this.local_decls[local].mutability,
PlaceRef { local, projection: &[ProjectionElem::Deref] } => {
debug_assert!(
this.local_decls[local].is_ref_for_guard(),
"Unexpected capture place",
);
this.local_decls[local].mutability
}
PlaceRef {
local,
projection: &[ref proj_base @ .., ProjectionElem::Field(upvar_index, _)],
}
| PlaceRef {
local,
projection:
&[ref proj_base @ .., ProjectionElem::Field(upvar_index, _), ProjectionElem::Deref],
} => {
let place = PlaceRef { local, projection: proj_base };
let mutability = match arg_place_builder.base() {
// We are capturing a path that starts off a local variable in the parent.
// The mutability of the current capture is same as the mutability
// of the local declaration in the parent.
PlaceBase::Local(local) => this.local_decls[local].mutability,
// Parent is a closure and we are capturing a path that is captured
// by the parent itself. The mutability of the current capture
// is same as that of the capture in the parent closure.
PlaceBase::Upvar { .. } => {
let enclosing_upvars_resolved = arg_place_builder.clone().into_place(
this.hir.tcx(),
this.hir.typeck_results());
// Not projected from the implicit `self` in a closure.
debug_assert!(
match place.local_or_deref_local() {
Some(local) => local == Local::new(1),
None => false,
},
"Unexpected capture place"
);
// Not in a closure
debug_assert!(
this.upvar_mutbls.len() > upvar_index.index(),
"Unexpected capture place"
);
this.upvar_mutbls[upvar_index.index()]
match enclosing_upvars_resolved.as_ref() {
PlaceRef { local, projection: &[ProjectionElem::Field(upvar_index, _), ..] }
| PlaceRef {
local,
projection: &[ProjectionElem::Deref, ProjectionElem::Field(upvar_index, _), ..] } => {
// Not in a closure
debug_assert!(
local == Local::new(1),
"Expected local to be Local(1), found {:?}",
local
);
// Not in a closure
debug_assert!(
this.upvar_mutbls.len() > upvar_index.index(),
"Unexpected capture place, upvar_mutbls={:#?}, upvar_index={:?}",
this.upvar_mutbls, upvar_index
);
this.upvar_mutbls[upvar_index.index()]
}
_ => bug!("Unexpected capture place"),
}
}
_ => bug!("Unexpected capture place"),
};
let borrow_kind = match mutability {
@ -438,6 +438,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
};
let arg_place = arg_place_builder.into_place(
this.hir.tcx(),
this.hir.typeck_results());
this.cfg.push_assign(
block,
source_info,

View file

@ -10,6 +10,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_hir::{GeneratorKind, HirIdMap, Node};
use rustc_index::vec::{Idx, IndexVec};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
use rustc_middle::middle::region;
use rustc_middle::mir::*;
use rustc_middle::ty::subst::Subst;
@ -823,7 +824,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// with the closure's DefId. Here, we run through that vec of UpvarIds for
// the given closure and use the necessary information to create upvar
// debuginfo and to fill `self.upvar_mutbls`.
if let Some(upvars) = hir_typeck_results.closure_captures.get(&fn_def_id) {
if hir_typeck_results.closure_min_captures.get(&fn_def_id).is_some() {
let closure_env_arg = Local::new(1);
let mut closure_env_projs = vec![];
let mut closure_ty = self.local_decls[closure_env_arg].ty;
@ -836,14 +837,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
_ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty),
};
let upvar_tys = upvar_substs.upvar_tys();
let upvars_with_tys = upvars.iter().zip(upvar_tys);
self.upvar_mutbls = upvars_with_tys
let capture_tys = upvar_substs.upvar_tys();
let captures_with_tys = hir_typeck_results
.closure_min_captures_flattened(fn_def_id)
.zip(capture_tys);
self.upvar_mutbls = captures_with_tys
.enumerate()
.map(|(i, ((&var_id, &upvar_id), ty))| {
let capture = hir_typeck_results.upvar_capture(upvar_id);
.map(|(i, (captured_place, ty))| {
let capture = captured_place.info.capture_kind;
let var_id = match captured_place.place.base {
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
_ => bug!("Expected an upvar")
};
let mut mutability = Mutability::Not;
// FIXME(project-rfc-2229#8): Store more precise information
let mut name = kw::Invalid;
if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {

View file

@ -6,6 +6,8 @@ use crate::thir::*;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_index::vec::Idx;
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::BorrowKind;
use rustc_middle::ty::adjustment::{
@ -386,14 +388,12 @@ fn make_mirror_unadjusted<'a, 'tcx>(
span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty);
}
};
let upvars = cx
.typeck_results()
.closure_captures
.get(&def_id)
.iter()
.flat_map(|upvars| upvars.iter())
.closure_min_captures_flattened(def_id)
.zip(substs.upvar_tys())
.map(|((&var_hir_id, _), ty)| capture_upvar(cx, expr, var_hir_id, ty))
.map(|(captured_place, ty)| capture_upvar(cx, expr, captured_place, ty))
.collect();
ExprKind::Closure { closure_id: def_id, substs, upvars, movability }
}
@ -981,27 +981,55 @@ fn overloaded_place<'a, 'tcx>(
ExprKind::Deref { arg: ref_expr.to_ref() }
}
fn capture_upvar<'tcx>(
fn capture_upvar<'a, 'tcx>(
cx: &mut Cx<'_, 'tcx>,
closure_expr: &'tcx hir::Expr<'tcx>,
var_hir_id: hir::HirId,
captured_place: &'a ty::CapturedPlace<'tcx>,
upvar_ty: Ty<'tcx>,
) -> ExprRef<'tcx> {
let upvar_id = ty::UpvarId {
var_path: ty::UpvarPath { hir_id: var_hir_id },
closure_expr_id: cx.tcx.hir().local_def_id(closure_expr.hir_id),
};
let upvar_capture = cx.typeck_results().upvar_capture(upvar_id);
let upvar_capture = captured_place.info.capture_kind;
let temp_lifetime = cx.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id);
let var_ty = cx.typeck_results().node_type(var_hir_id);
let captured_var = Expr {
let var_ty = captured_place.place.base_ty;
// The result of capture analysis in `rustc_typeck/check/upvar.rs`represents a captured path
// as it's seen for use within the closure and not at the time of closure creation.
//
// That is we see expect to see it start from a captured upvar and not something that is local
// to the closure's parent.
let var_hir_id = match captured_place.place.base {
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
base => bug!("Expected an upvar, found {:?}", base),
};
let mut captured_place_expr = Expr {
temp_lifetime,
ty: var_ty,
span: closure_expr.span,
kind: convert_var(cx, var_hir_id),
};
for proj in captured_place.place.projections.iter() {
let kind = match proj.kind {
HirProjectionKind::Deref => ExprKind::Deref { arg: captured_place_expr.to_ref() },
HirProjectionKind::Field(field, ..) => {
// Variant index will always be 0, because for multi-variant
// enums, we capture the enum entirely.
ExprKind::Field {
lhs: captured_place_expr.to_ref(),
name: Field::new(field as usize),
}
}
HirProjectionKind::Index | HirProjectionKind::Subslice => {
// We don't capture these projections, so we can ignore them here
continue;
}
};
captured_place_expr = Expr { temp_lifetime, ty: proj.ty, span: closure_expr.span, kind };
}
match upvar_capture {
ty::UpvarCapture::ByValue(_) => captured_var.to_ref(),
ty::UpvarCapture::ByValue(_) => captured_place_expr.to_ref(),
ty::UpvarCapture::ByRef(upvar_borrow) => {
let borrow_kind = match upvar_borrow.kind {
ty::BorrowKind::ImmBorrow => BorrowKind::Shared,
@ -1012,7 +1040,7 @@ fn capture_upvar<'tcx>(
temp_lifetime,
ty: upvar_ty,
span: closure_expr.span,
kind: ExprKind::Borrow { borrow_kind, arg: captured_var.to_ref() },
kind: ExprKind::Borrow { borrow_kind, arg: captured_place_expr.to_ref() },
}
.to_ref()
}