-
Notifications
You must be signed in to change notification settings - Fork 13.4k
2229: Handle patterns within closures correctly when capture_disjoint_fields
is enabled
#82536
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ec10b71
d4f8729
b6cf070
685a4c6
74fc643
fb3b77a
22eaffe
189d206
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,14 +10,15 @@ use rustc_middle::hir::place::ProjectionKind as HirProjectionKind; | |
use rustc_middle::middle::region; | ||
use rustc_middle::mir::AssertKind::BoundsCheck; | ||
use rustc_middle::mir::*; | ||
use rustc_middle::ty::AdtDef; | ||
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)] | ||
#[derive(Copy, Clone, Debug, PartialEq)] | ||
crate enum PlaceBase { | ||
/// Denotes the start of a `Place`. | ||
Local(Local), | ||
|
@@ -67,7 +68,7 @@ crate enum PlaceBase { | |
/// | ||
/// 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)] | ||
#[derive(Clone, Debug, PartialEq)] | ||
crate struct PlaceBuilder<'tcx> { | ||
base: PlaceBase, | ||
projection: Vec<PlaceElem<'tcx>>, | ||
|
@@ -83,20 +84,23 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>( | |
mir_projections: &[PlaceElem<'tcx>], | ||
) -> Vec<HirProjectionKind> { | ||
let mut hir_projections = Vec::new(); | ||
let mut variant = None; | ||
|
||
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)) | ||
let variant = variant.unwrap_or(VariantIdx::new(0)); | ||
HirProjectionKind::Field(field.index() as u32, variant) | ||
} | ||
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::Downcast(.., idx) => { | ||
// We don't expect to see multi-variant enums here, as earlier | ||
// phases will have truncated them already. However, there can | ||
// still be downcasts, thanks to single-variant enums. | ||
// We keep track of VariantIdx so we can use this information | ||
// if the next ProjectionElem is a Field. | ||
variant = Some(*idx); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So-- I'm a bit confused by this change. Are we trying to truncate for multi-variant enums? Do we expect multi-variant enums to show up in this code path? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we capture a multi-variant enum, we will do it in entierty in typchk, i.e. they will be trunacted by the time we get here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Though i think the comment should be something like, "We capture multi-varirant enums completely, but we might see a downcast projection in case of single variant enums, so we need to account for it here." We just happen to be accounting for it more generally. Since we search for ancestors in the list of captures of the place returned by this function, I think its fine to do what we are doing here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the "we capture multi-variant enums completely" is actually sort of confusingly phrased. I would say something like:
If this is correct, can we add an assertion that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dont think that is quite right. We are going from a MIR place -> HIR place. For all the HIR places we have in our capture set, we would stop at the multivairant enum and not capture anything on top of it. However the MIR place might be for something precise, eg: struct Foo(String, String)
x: Option<Foo>
let c = || match x {
Some(Foo(, y)) => // Upvar(x), [downcast(0), field(0), field(1)]
// ... The ideal thing to do here would really be to look at the type before a downcast projection and see if it's a multi-variant enum and break, but that is a little hard to do here -- because we don't know the actual place since we don't know the capture index, and hence can't get the type. We can make it work by checking if we know any anscetors (in our capture set) of the partially converted place. This sort of makes the code hard to follow and somewhat cyclic in logic where to find a valid ancestor we need to convert the mir place into some hir place and to do that we need to find a valid ancestor. On the otherhand, the field projection in HirPlace is defined as Another thing to note here is that a multivariant enum has a variant with index 0, so checking for 0 is not an "if and only if" like check. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we agree on this approach to thinking about the problem, we would need to update the comments to reflect this |
||
continue; | ||
} | ||
ProjectionElem::Index(..) | ||
| ProjectionElem::ConstantIndex { .. } | ||
|
@@ -106,7 +110,7 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>( | |
break; | ||
} | ||
}; | ||
|
||
variant = None; | ||
hir_projections.push(hir_projection); | ||
} | ||
|
||
|
@@ -194,12 +198,12 @@ fn find_capture_matching_projections<'a, 'tcx>( | |
/// 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. | ||
/// Returns a Result with the error being the PlaceBuilder (`from_builder`) 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> { | ||
roxelo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> { | ||
roxelo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
match from_builder.base { | ||
PlaceBase::Local(_) => Ok(from_builder), | ||
PlaceBase::Upvar { var_hir_id, closure_def_id, closure_kind } => { | ||
|
@@ -230,13 +234,12 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( | |
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); | ||
return Err(from_builder); | ||
}; | ||
|
||
let closure_ty = typeck_results | ||
|
@@ -300,6 +303,25 @@ impl<'tcx> PlaceBuilder<'tcx> { | |
to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap() | ||
} | ||
|
||
/// Attempts to resolve the `PlaceBuilder`. | ||
/// On success, it will return the resolved `PlaceBuilder`. | ||
/// On failure, it will return itself. | ||
/// | ||
/// Upvars resolve may fail for a `PlaceBuilder` when attempting to | ||
/// resolve a disjoint field whose root variable is not captured | ||
/// (destructured assignments) or when attempting to resolve a root | ||
/// variable (discriminant matching with only wildcard arm) that is | ||
/// not captured. This can happen because the final mir that will be | ||
/// generated doesn't require a read for this place. Failures will only | ||
/// happen inside closures. | ||
crate fn try_upvars_resolved<'a>( | ||
self, | ||
tcx: TyCtxt<'tcx>, | ||
typeck_results: &'a ty::TypeckResults<'tcx>, | ||
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> { | ||
to_upvars_resolved_place_builder(self, tcx, typeck_results) | ||
} | ||
|
||
crate fn base(&self) -> PlaceBase { | ||
self.base | ||
} | ||
|
@@ -308,15 +330,22 @@ impl<'tcx> PlaceBuilder<'tcx> { | |
self.project(PlaceElem::Field(f, ty)) | ||
} | ||
|
||
fn deref(self) -> Self { | ||
crate fn deref(self) -> Self { | ||
self.project(PlaceElem::Deref) | ||
} | ||
|
||
crate fn downcast(self, adt_def: &'tcx AdtDef, variant_index: VariantIdx) -> Self { | ||
self.project(PlaceElem::Downcast( | ||
Some(adt_def.variants[variant_index].ident.name), | ||
variant_index, | ||
)) | ||
} | ||
|
||
fn index(self, index: Local) -> Self { | ||
self.project(PlaceElem::Index(index)) | ||
} | ||
|
||
fn project(mut self, elem: PlaceElem<'tcx>) -> Self { | ||
crate fn project(mut self, elem: PlaceElem<'tcx>) -> Self { | ||
self.projection.push(elem); | ||
self | ||
} | ||
|
@@ -602,13 +631,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | |
// The "retagging" transformation (for Stacked Borrows) relies on this. | ||
let idx = unpack!(block = self.as_temp(block, temp_lifetime, index, Mutability::Not,)); | ||
|
||
block = self.bounds_check( | ||
block, | ||
base_place.clone().into_place(self.tcx, self.typeck_results), | ||
idx, | ||
expr_span, | ||
source_info, | ||
); | ||
block = self.bounds_check(block, base_place.clone(), idx, expr_span, source_info); | ||
|
||
if is_outermost_index { | ||
self.read_fake_borrows(block, fake_borrow_temps, source_info) | ||
|
@@ -629,7 +652,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | |
fn bounds_check( | ||
&mut self, | ||
block: BasicBlock, | ||
slice: Place<'tcx>, | ||
slice: PlaceBuilder<'tcx>, | ||
index: Local, | ||
expr_span: Span, | ||
source_info: SourceInfo, | ||
|
@@ -641,7 +664,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | |
let lt = self.temp(bool_ty, expr_span); | ||
|
||
// len = len(slice) | ||
self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice)); | ||
self.cfg.push_assign( | ||
block, | ||
source_info, | ||
len, | ||
Rvalue::Len(slice.into_place(self.tcx, self.typeck_results)), | ||
); | ||
// lt = idx < len | ||
self.cfg.push_assign( | ||
block, | ||
|
Uh oh!
There was an error while loading. Please reload this page.