Auto merge of #99806 - oli-obk:unconstrained_opaque_type, r=estebank

Allow patterns to constrain the hidden type of opaque types

fixes #96572

reverts a revert as original PR was a perf regression that was fixed by reverting it: https://github.com/rust-lang/rust/pull/99368#issuecomment-1186587864)

TODO:

* check if https://github.com/rust-lang/rust/issues/99685 is avoided
This commit is contained in:
bors 2022-09-20 12:09:52 +00:00
commit 4136b59b7d
40 changed files with 384 additions and 179 deletions

View File

@ -2146,7 +2146,9 @@ fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<
}
StorageDeadOrDrop::Destructor(_) => kind,
},
ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Field(..)
| ProjectionElem::Downcast(..) => {
match place_ty.ty.kind() {
ty::Adt(def, _) if def.has_dtor(tcx) => {
// Report the outermost adt with a destructor

View File

@ -237,6 +237,7 @@ pub(super) fn describe_place_with_options(
}
ProjectionElem::Downcast(..) if opt.including_downcast => return None,
ProjectionElem::Downcast(..) => (),
ProjectionElem::OpaqueCast(..) => (),
ProjectionElem::Field(field, _ty) => {
// FIXME(project-rfc_2229#36): print capture precisely here.
if let Some(field) = self.is_upvar_field_projection(PlaceRef {
@ -317,6 +318,7 @@ fn describe_field(
PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
}
ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(*ty),
ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
},
};

View File

@ -169,6 +169,7 @@ pub(crate) fn report_mutability_error(
..,
ProjectionElem::Index(_)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Downcast(..),
],

View File

@ -1781,6 +1781,7 @@ fn check_if_assigned_path_is_moved(
for (place_base, elem) in place.iter_projections().rev() {
match elem {
ProjectionElem::Index(_/*operand*/) |
ProjectionElem::OpaqueCast(_) |
ProjectionElem::ConstantIndex { .. } |
// assigning to P[i] requires P to be valid.
ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
@ -2172,6 +2173,7 @@ fn is_mutable(
| ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Downcast(..) => {
let upvar_field_projection = self.is_upvar_field_projection(place);
if let Some(field) = upvar_field_projection {

View File

@ -250,6 +250,7 @@ fn place_components_conflict<'tcx>(
| (ProjectionElem::Index { .. }, _, _)
| (ProjectionElem::ConstantIndex { .. }, _, _)
| (ProjectionElem::Subslice { .. }, _, _)
| (ProjectionElem::OpaqueCast { .. }, _, _)
| (ProjectionElem::Downcast { .. }, _, _) => {
// Recursive case. This can still be disjoint on a
// further iteration if this a shallow access and
@ -317,6 +318,17 @@ fn place_projection_conflict<'tcx>(
debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
Overlap::EqualOrDisjoint
}
(ProjectionElem::OpaqueCast(v1), ProjectionElem::OpaqueCast(v2)) => {
if v1 == v2 {
// same type - recur.
debug!("place_element_conflict: DISJOINT-OR-EQ-OPAQUE");
Overlap::EqualOrDisjoint
} else {
// Different types. Disjoint!
debug!("place_element_conflict: DISJOINT-OPAQUE");
Overlap::Disjoint
}
}
(ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
if f1 == f2 {
// same field (e.g., `a.y` vs. `a.y`) - recur.
@ -520,6 +532,7 @@ fn place_projection_conflict<'tcx>(
| ProjectionElem::Field(..)
| ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Downcast(..),
_,

View File

@ -81,6 +81,7 @@ fn next(&mut self) -> Option<Self::Item> {
}
ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. }
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Index(_) => {
cursor = cursor_base;

View File

@ -764,6 +764,19 @@ fn sanitize_projection(
}
PlaceTy::from_ty(fty)
}
ProjectionElem::OpaqueCast(ty) => {
let ty = self.sanitize_type(place, ty);
let ty = self.cx.normalize(ty, location);
self.cx
.eq_types(
base.ty,
ty,
location.to_locations(),
ConstraintCategory::TypeAnnotation,
)
.unwrap();
PlaceTy::from_ty(ty)
}
}
}
@ -1170,10 +1183,11 @@ fn relate_type_and_user_type(
tcx,
self.param_env,
proj,
|this, field, ()| {
|this, field, _| {
let ty = this.field_ty(tcx, field);
self.normalize(ty, locations)
},
|_, _| unreachable!(),
);
curr_projected_ty = projected_ty;
}
@ -2503,6 +2517,7 @@ fn add_reborrow_constraint(
}
ProjectionElem::Field(..)
| ProjectionElem::Downcast(..)
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {

View File

@ -850,6 +850,7 @@ pub(crate) fn codegen_place<'tcx>(
PlaceElem::Deref => {
cplace = cplace.place_deref(fx);
}
PlaceElem::OpaqueCast(ty) => cplace = cplace.place_opaque_cast(fx, ty),
PlaceElem::Field(field, _ty) => {
cplace = cplace.place_field(fx, field);
}

View File

@ -621,6 +621,14 @@ fn transmute_value<'tcx>(
}
}
pub(crate) fn place_opaque_cast(
self,
fx: &mut FunctionCx<'_, '_, 'tcx>,
ty: Ty<'tcx>,
) -> CPlace<'tcx> {
CPlace { inner: self.inner, layout: fx.layout_of(ty) }
}
pub(crate) fn place_field(
self,
fx: &mut FunctionCx<'_, '_, 'tcx>,

View File

@ -400,6 +400,21 @@ pub fn project_downcast<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
downcast
}
pub fn project_type<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
&self,
bx: &mut Bx,
ty: Ty<'tcx>,
) -> Self {
let mut downcast = *self;
downcast.layout = bx.cx().layout_of(ty);
// Cast to the appropriate type.
let variant_ty = bx.cx().backend_type(downcast.layout);
downcast.llval = bx.pointercast(downcast.llval, bx.cx().type_ptr_to(variant_ty));
downcast
}
pub fn storage_live<Bx: BuilderMethods<'a, 'tcx, Value = V>>(&self, bx: &mut Bx) {
bx.lifetime_start(self.llval, self.layout.size);
}
@ -442,6 +457,7 @@ pub fn codegen_place(
mir::ProjectionElem::Field(ref field, _) => {
cg_base.project_field(bx, field.index())
}
mir::ProjectionElem::OpaqueCast(ty) => cg_base.project_type(bx, ty),
mir::ProjectionElem::Index(index) => {
let index = &mir::Operand::Copy(mir::Place::from(index));
let index = self.codegen_operand(bx, index);

View File

@ -350,6 +350,11 @@ pub fn place_projection(
) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
use rustc_middle::mir::ProjectionElem::*;
Ok(match proj_elem {
OpaqueCast(ty) => {
let mut place = base.clone();
place.layout = self.layout_of(ty)?;
place
}
Field(field, _) => self.place_field(base, field.index())?,
Downcast(_, variant) => self.place_downcast(base, variant)?,
Deref => self.deref_operand(&self.place_to_op(base)?)?.into(),
@ -374,6 +379,11 @@ pub fn operand_projection(
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
use rustc_middle::mir::ProjectionElem::*;
Ok(match proj_elem {
OpaqueCast(ty) => {
let mut op = base.clone();
op.layout = self.layout_of(ty)?;
op
}
Field(field, _) => self.operand_field(base, field.index())?,
Downcast(_, variant) => self.operand_downcast(base, variant)?,
Deref => self.deref_operand(base)?.into(),

View File

@ -656,6 +656,7 @@ fn visit_projection_elem(
ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Downcast(..)
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::Subslice { .. }
| ProjectionElem::Field(..)
| ProjectionElem::Index(_) => {}

View File

@ -308,6 +308,7 @@ pub fn in_place<'tcx, Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, place: Pla
ProjectionElem::Deref
| ProjectionElem::Field(_, _)
| ProjectionElem::OpaqueCast(_)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Downcast(_, _)

View File

@ -361,7 +361,7 @@ fn validate_place(&mut self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable>
return Err(Unpromotable);
}
}
ProjectionElem::Downcast(..) => {
ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => {
return Err(Unpromotable);
}

View File

@ -1380,6 +1380,7 @@ fn is_indirect(&self) -> bool {
Self::Field(_, _)
| Self::Index(_)
| Self::OpaqueCast(_)
| Self::ConstantIndex { .. }
| Self::Subslice { .. }
| Self::Downcast(_, _) => false,
@ -1574,7 +1575,9 @@ impl Debug for Place<'_> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
for elem in self.projection.iter().rev() {
match elem {
ProjectionElem::Downcast(_, _) | ProjectionElem::Field(_, _) => {
ProjectionElem::OpaqueCast(_)
| ProjectionElem::Downcast(_, _)
| ProjectionElem::Field(_, _) => {
write!(fmt, "(").unwrap();
}
ProjectionElem::Deref => {
@ -1590,6 +1593,9 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
for elem in self.projection.iter() {
match elem {
ProjectionElem::OpaqueCast(ty) => {
write!(fmt, " as {})", ty)?;
}
ProjectionElem::Downcast(Some(name), _index) => {
write!(fmt, " as {})", name)?;
}

View File

@ -829,6 +829,9 @@ pub enum InlineAsmOperand<'tcx> {
/// generator has more than one variant, the parent place's variant index must be set, indicating
/// which variant is being used. If it has just one variant, the variant index may or may not be
/// included - the single possible variant is inferred if it is not included.
/// - [`OpaqueCast`](ProjectionElem::OpaqueCast): This projection changes the place's type to the
/// given one, and makes no other changes. A `OpaqueCast` projection on any type other than an
/// opaque type from the current crate is not well-formed.
/// - [`ConstantIndex`](ProjectionElem::ConstantIndex): Computes an offset in units of `T` into the
/// place as described in the documentation for the `ProjectionElem`. The resulting address is
/// the parent's address plus that offset, and the type is `T`. This is only legal if the parent
@ -928,6 +931,10 @@ pub enum ProjectionElem<V, T> {
///
/// The included Symbol is the name of the variant, used for printing MIR.
Downcast(Option<Symbol>, VariantIdx),
/// Like an explicit cast from an opaque type to a concrete type, but without
/// requiring an intermediate variable.
OpaqueCast(T),
}
/// Alias for projections as they appear in places, where the base is a place

View File

@ -56,7 +56,7 @@ pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: Field) -> Ty<'tcx> {
/// `PlaceElem`, where we can just use the `Ty` that is already
/// stored inline on field projection elems.
pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> {
self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty)
self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty, |_, ty| ty)
}
/// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
@ -70,6 +70,7 @@ pub fn projection_ty_core<V, T>(
param_env: ty::ParamEnv<'tcx>,
elem: &ProjectionElem<V, T>,
mut handle_field: impl FnMut(&Self, Field, T) -> Ty<'tcx>,
mut handle_opaque_cast: impl FnMut(&Self, T) -> Ty<'tcx>,
) -> PlaceTy<'tcx>
where
V: ::std::fmt::Debug,
@ -108,6 +109,7 @@ pub fn projection_ty_core<V, T>(
PlaceTy { ty: self.ty, variant_index: Some(index) }
}
ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast(&self, ty)),
};
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
answer

View File

@ -1084,6 +1084,11 @@ fn process_projection_elem(
self.visit_ty(&mut new_ty, TyContext::Location(location));
if ty != new_ty { Some(PlaceElem::Field(field, new_ty)) } else { None }
}
PlaceElem::OpaqueCast(ty) => {
let mut new_ty = ty;
self.visit_ty(&mut new_ty, TyContext::Location(location));
if ty != new_ty { Some(PlaceElem::OpaqueCast(new_ty)) } else { None }
}
PlaceElem::Deref
| PlaceElem::ConstantIndex { .. }
| PlaceElem::Subslice { .. }
@ -1153,7 +1158,7 @@ fn super_projection_elem(
location: Location,
) {
match elem {
ProjectionElem::Field(_field, ty) => {
ProjectionElem::OpaqueCast(ty) | ProjectionElem::Field(_, ty) => {
self.visit_ty(ty, TyContext::Location(location));
}
ProjectionElem::Index(local) => {

View File

@ -69,7 +69,7 @@ pub(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, Debug, PartialEq)]
pub(crate) struct PlaceBuilder<'tcx> {
pub(in crate::build) struct PlaceBuilder<'tcx> {
base: PlaceBase,
projection: Vec<PlaceElem<'tcx>>,
}
@ -102,6 +102,8 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
variant = Some(*idx);
continue;
}
// These do not affect anything, they just make sure we know the right type.
ProjectionElem::OpaqueCast(_) => continue,
ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {
@ -168,22 +170,22 @@ fn find_capture_matching_projections<'a, 'tcx>(
/// `PlaceBuilder` now starts from `PlaceBase::Local`.
///
/// Returns a Result with the error being the PlaceBuilder (`from_builder`) that was not found.
fn to_upvars_resolved_place_builder<'a, 'tcx>(
#[instrument(level = "trace", skip(cx), ret)]
fn to_upvars_resolved_place_builder<'tcx>(
from_builder: PlaceBuilder<'tcx>,
tcx: TyCtxt<'tcx>,
upvars: &'a CaptureMap<'tcx>,
cx: &Builder<'_, 'tcx>,
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
match from_builder.base {
PlaceBase::Local(_) => Ok(from_builder),
PlaceBase::Upvar { var_hir_id, closure_def_id } => {
let Some((capture_index, capture)) =
find_capture_matching_projections(
upvars,
&cx.upvars,
var_hir_id,
&from_builder.projection,
) else {
let closure_span = tcx.def_span(closure_def_id);
if !enable_precise_capture(tcx, closure_span) {
let closure_span = cx.tcx.def_span(closure_def_id);
if !enable_precise_capture(cx.tcx, closure_span) {
bug!(
"No associated capture found for {:?}[{:#?}] even though \
capture_disjoint_fields isn't enabled",
@ -200,12 +202,13 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
};
// Access the capture by accessing the field within the Closure struct.
let capture_info = &upvars[capture_index];
let capture_info = &cx.upvars[capture_index];
let mut upvar_resolved_place_builder = PlaceBuilder::from(capture_info.use_place);
// We used some of the projections to build the capture itself,
// now we apply the remaining to the upvar resolved place.
trace!(?capture.captured_place, ?from_builder.projection);
let remaining_projections = strip_prefix(
capture.captured_place.place.base_ty,
from_builder.projection,
@ -230,16 +233,21 @@ fn strip_prefix<'tcx>(
prefix_projections: &[HirProjection<'tcx>],
) -> impl Iterator<Item = PlaceElem<'tcx>> {
let mut iter = projections.into_iter();
let mut next = || match iter.next()? {
// Filter out opaque casts, they are unnecessary in the prefix.
ProjectionElem::OpaqueCast(..) => iter.next(),
other => Some(other),
};
for projection in prefix_projections {
match projection.kind {
HirProjectionKind::Deref => {
assert!(matches!(iter.next(), Some(ProjectionElem::Deref)));
assert!(matches!(next(), Some(ProjectionElem::Deref)));
}
HirProjectionKind::Field(..) => {
if base_ty.is_enum() {
assert!(matches!(iter.next(), Some(ProjectionElem::Downcast(..))));
assert!(matches!(next(), Some(ProjectionElem::Downcast(..))));
}
assert!(matches!(iter.next(), Some(ProjectionElem::Field(..))));
assert!(matches!(next(), Some(ProjectionElem::Field(..))));
}
HirProjectionKind::Index | HirProjectionKind::Subslice => {
bug!("unexpected projection kind: {:?}", projection);
@ -251,24 +259,16 @@ fn strip_prefix<'tcx>(
}
impl<'tcx> PlaceBuilder<'tcx> {
pub(in crate::build) fn into_place<'a>(
self,
tcx: TyCtxt<'tcx>,
upvars: &'a CaptureMap<'tcx>,
) -> Place<'tcx> {
pub(in crate::build) fn into_place(self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> {
if let PlaceBase::Local(local) = self.base {
Place { local, projection: tcx.intern_place_elems(&self.projection) }
Place { local, projection: cx.tcx.intern_place_elems(&self.projection) }
} else {
self.expect_upvars_resolved(tcx, upvars).into_place(tcx, upvars)
self.expect_upvars_resolved(cx).into_place(cx)
}
}
fn expect_upvars_resolved<'a>(
self,
tcx: TyCtxt<'tcx>,
upvars: &'a CaptureMap<'tcx>,
) -> PlaceBuilder<'tcx> {
to_upvars_resolved_place_builder(self, tcx, upvars).unwrap()
fn expect_upvars_resolved(self, cx: &Builder<'_, 'tcx>) -> PlaceBuilder<'tcx> {
to_upvars_resolved_place_builder(self, cx).unwrap()
}
/// Attempts to resolve the `PlaceBuilder`.
@ -282,18 +282,21 @@ fn expect_upvars_resolved<'a>(
/// 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.
pub(in crate::build) fn try_upvars_resolved<'a>(
pub(in crate::build) fn try_upvars_resolved(
self,
tcx: TyCtxt<'tcx>,
upvars: &'a CaptureMap<'tcx>,
cx: &Builder<'_, 'tcx>,
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
to_upvars_resolved_place_builder(self, tcx, upvars)
to_upvars_resolved_place_builder(self, cx)
}
pub(crate) fn base(&self) -> PlaceBase {
self.base
}
pub(crate) fn projection(&self) -> &[PlaceElem<'tcx>] {
&self.projection
}
pub(crate) fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
self.project(PlaceElem::Field(f, ty))
}
@ -353,7 +356,7 @@ pub(crate) fn as_place(
expr: &Expr<'tcx>,
) -> BlockAnd<Place<'tcx>> {
let place_builder = unpack!(block = self.as_place_builder(block, expr));
block.and(place_builder.into_place(self.tcx, &self.upvars))
block.and(place_builder.into_place(self))
}
/// This is used when constructing a compound `Place`, so that we can avoid creating
@ -377,7 +380,7 @@ pub(crate) fn as_read_only_place(
expr: &Expr<'tcx>,
) -> BlockAnd<Place<'tcx>> {
let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
block.and(place_builder.into_place(self.tcx, &self.upvars))
block.and(place_builder.into_place(self))
}
/// This is used when constructing a compound `Place`, so that we can avoid creating
@ -472,7 +475,7 @@ fn expr_as_place(
inferred_ty: expr.ty,
});
let place = place_builder.clone().into_place(this.tcx, &this.upvars);
let place = place_builder.clone().into_place(this);
this.cfg.push(
block,
Statement {
@ -610,7 +613,7 @@ fn lower_index_expression(
if is_outermost_index {
self.read_fake_borrows(block, fake_borrow_temps, source_info)
} else {
base_place = base_place.expect_upvars_resolved(self.tcx, &self.upvars);
base_place = base_place.expect_upvars_resolved(self);
self.add_fake_borrows_of_base(
&base_place,
block,
@ -638,12 +641,7 @@ fn bounds_check(
let lt = self.temp(bool_ty, expr_span);
// len = len(slice)
self.cfg.push_assign(
block,
source_info,
len,
Rvalue::Len(slice.into_place(self.tcx, &self.upvars)),
);
self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.into_place(self)));
// lt = idx < len
self.cfg.push_assign(
block,
@ -723,6 +721,7 @@ fn add_fake_borrows_of_base(
}
ProjectionElem::Field(..)
| ProjectionElem::Downcast(..)
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => (),
}

View File

@ -329,10 +329,8 @@ pub(crate) fn as_rvalue(
let place_builder =
unpack!(block = this.as_place_builder(block, &this.thir[*thir_place]));
if let Ok(place_builder_resolved) =
place_builder.try_upvars_resolved(this.tcx, &this.upvars)
{
let mir_place = place_builder_resolved.into_place(this.tcx, &this.upvars);
if let Ok(place_builder_resolved) = place_builder.try_upvars_resolved(this) {
let mir_place = place_builder_resolved.into_place(this);
this.cfg.push_fake_read(
block,
this.source_info(this.tcx.hir().span(*hir_id)),
@ -623,8 +621,7 @@ fn limit_capture_mutability(
// 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.tcx, &this.upvars);
let enclosing_upvars_resolved = arg_place_builder.clone().into_place(this);
match enclosing_upvars_resolved.as_ref() {
PlaceRef {
@ -661,7 +658,7 @@ fn limit_capture_mutability(
Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
};
let arg_place = arg_place_builder.into_place(this.tcx, &this.upvars);
let arg_place = arg_place_builder.into_place(this);
this.cfg.push_assign(
block,

View File

@ -23,6 +23,7 @@ pub(crate) fn as_temp(
ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr, mutability))
}
#[instrument(skip(self), level = "debug")]
fn as_temp_inner(
&mut self,
mut block: BasicBlock,
@ -30,10 +31,6 @@ fn as_temp_inner(
expr: &Expr<'tcx>,
mutability: Mutability,
) -> BlockAnd<Local> {
debug!(
"as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
block, temp_lifetime, expr, mutability
);
let this = self;
let expr_span = expr.span;

View File

@ -15,14 +15,13 @@
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Compile `expr`, storing the result into `destination`, which
/// is assumed to be uninitialized.
#[instrument(level = "debug", skip(self))]
pub(crate) fn expr_into_dest(
&mut self,
destination: Place<'tcx>,
mut block: BasicBlock,
expr: &Expr<'tcx>,
) -> BlockAnd<()> {
debug!("expr_into_dest(destination={:?}, block={:?}, expr={:?})", destination, block, expr);
// since we frequently have to reference `self` from within a
// closure, where `self` would be shadowed, it's easier to
// just use the name `this` uniformly
@ -366,7 +365,7 @@ pub(crate) fn expr_into_dest(
None => {
let place_builder = place_builder.clone();
this.consume_by_copy_or_move(
place_builder.field(n, *ty).into_place(this.tcx, &this.upvars),
place_builder.field(n, *ty).into_place(this),
)
}
})

View File

@ -220,10 +220,8 @@ fn lower_scrutinee(
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
let source_info = self.source_info(scrutinee_span);
if let Ok(scrutinee_builder) =
scrutinee_place_builder.clone().try_upvars_resolved(self.tcx, &self.upvars)
{
let scrutinee_place = scrutinee_builder.into_place(self.tcx, &self.upvars);
if let Ok(scrutinee_builder) = scrutinee_place_builder.clone().try_upvars_resolved(self) {
let scrutinee_place = scrutinee_builder.into_place(self);
self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
}
@ -246,7 +244,8 @@ fn create_match_candidates<'pat>(
.map(|arm| {
let arm = &self.thir[arm];
let arm_has_guard = arm.guard.is_some();
let arm_candidate = Candidate::new(scrutinee.clone(), &arm.pattern, arm_has_guard);
let arm_candidate =
Candidate::new(scrutinee.clone(), &arm.pattern, arm_has_guard, self);
(arm, arm_candidate)
})
.collect()
@ -349,9 +348,9 @@ fn lower_match_arms(
let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None;
let scrutinee_place: Place<'tcx>;
if let Ok(scrutinee_builder) =
scrutinee_place_builder.clone().try_upvars_resolved(this.tcx, &this.upvars)
scrutinee_place_builder.clone().try_upvars_resolved(this)
{
scrutinee_place = scrutinee_builder.into_place(this.tcx, &this.upvars);
scrutinee_place = scrutinee_builder.into_place(this);
opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span));
}
let scope = this.declare_bindings(
@ -584,7 +583,7 @@ pub(crate) fn place_into_pattern(
initializer: PlaceBuilder<'tcx>,
set_match_place: bool,
) -> BlockAnd<()> {
let mut candidate = Candidate::new(initializer.clone(), &irrefutable_pat, false);
let mut candidate = Candidate::new(initializer.clone(), &irrefutable_pat, false, self);
let fake_borrow_temps = self.lower_match_tree(
block,
irrefutable_pat.span,
@ -601,12 +600,6 @@ pub(crate) fn place_into_pattern(
while let Some(next) = {
for binding in &candidate_ref.bindings {
let local = self.var_local_id(binding.var_id, OutsideGuard);
let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
)))) = self.local_decls[local].local_info else {
bug!("Let binding to non-user variable.")
};
// `try_upvars_resolved` may fail if it is unable to resolve the given
// `PlaceBuilder` inside a closure. In this case, we don't want to include
// a scrutinee place. `scrutinee_place_builder` will fail for destructured
@ -621,10 +614,14 @@ pub(crate) fn place_into_pattern(
// let (v1, v2) = foo;
// };
// ```
if let Ok(match_pair_resolved) =
initializer.clone().try_upvars_resolved(self.tcx, &self.upvars)
{
let place = match_pair_resolved.into_place(self.tcx, &self.upvars);
if let Ok(match_pair_resolved) = initializer.clone().try_upvars_resolved(self) {
let place = match_pair_resolved.into_place(self);
let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
)))) = self.local_decls[local].local_info else {
bug!("Let binding to non-user variable.")
};
*match_place = Some(place);
}
}
@ -654,6 +651,7 @@ pub(crate) fn place_into_pattern(
/// scope for the bindings in these patterns, if such a scope had to be
/// created. NOTE: Declaring the bindings should always be done in their
/// drop scope.
#[instrument(skip(self), level = "debug")]
pub(crate) fn declare_bindings(
&mut self,
mut visibility_scope: Option<SourceScope>,
@ -662,7 +660,6 @@ pub(crate) fn declare_bindings(
has_guard: ArmHasGuard,
opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,
) -> Option<SourceScope> {
debug!("declare_bindings: pattern={:?}", pattern);
self.visit_primary_bindings(
&pattern,
UserTypeProjections::none(),
@ -868,11 +865,16 @@ struct Candidate<'pat, 'tcx> {
}
impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
fn new(place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, has_guard: bool) -> Self {
fn new(
place: PlaceBuilder<'tcx>,
pattern: &'pat Pat<'tcx>,
has_guard: bool,
cx: &Builder<'_, 'tcx>,
) -> Self {
Candidate {
span: pattern.span,
has_guard,
match_pairs: smallvec![MatchPair { place, pattern }],
match_pairs: smallvec![MatchPair::new(place, pattern, cx)],
bindings: Vec::new(),
ascriptions: Vec::new(),
subcandidates: Vec::new(),
@ -1048,6 +1050,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// if `x.0` matches `false` (for the third arm). In the (impossible at
/// runtime) case when `x.0` is now `true`, we branch to
/// `otherwise_block`.
#[instrument(skip(self, fake_borrows), level = "debug")]
fn match_candidates<'pat>(
&mut self,
span: Span,
@ -1057,11 +1060,6 @@ fn match_candidates<'pat>(
candidates: &mut [&mut Candidate<'pat, 'tcx>],
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
) {
debug!(
"matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})",
span, candidates, start_block, otherwise_block,
);
// Start by simplifying candidates. Once this process is complete, all
// the match pairs which remain require some form of test, whether it
// be a switch or pattern comparison.
@ -1380,6 +1378,10 @@ fn test_candidates_with_or(
)
}
#[instrument(
skip(self, otherwise, or_span, place, fake_borrows, candidate, pats),
level = "debug"
)]
fn test_or_pattern<'pat>(
&mut self,
candidate: &mut Candidate<'pat, 'tcx>,
@ -1389,10 +1391,10 @@ fn test_or_pattern<'pat>(
place: PlaceBuilder<'tcx>,
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
) {
debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats);
debug!("candidate={:#?}\npats={:#?}", candidate, pats);
let mut or_candidates: Vec<_> = pats
.iter()
.map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard))
.map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard, self))
.collect();
let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect();
let otherwise = if candidate.otherwise_block.is_some() {
@ -1605,9 +1607,9 @@ fn test_candidates<'pat, 'b, 'c>(
// Insert a Shallow borrow of any places that is switched on.
if let Some(fb) = fake_borrows && let Ok(match_place_resolved) =
match_place.clone().try_upvars_resolved(self.tcx, &self.upvars)
match_place.clone().try_upvars_resolved(self)
{
let resolved_place = match_place_resolved.into_place(self.tcx, &self.upvars);
let resolved_place = match_place_resolved.into_place(self);
fb.insert(resolved_place);
}
@ -1634,9 +1636,14 @@ fn test_candidates<'pat, 'b, 'c>(
candidates = rest;
}
// at least the first candidate ought to be tested
assert!(total_candidate_count > candidates.len());
debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len());
debug!("test_candidates: untested_candidates: {}", candidates.len());
assert!(
total_candidate_count > candidates.len(),
"{}, {:#?}",
total_candidate_count,
candidates
);
debug!("tested_candidates: {}", total_candidate_count - candidates.len());
debug!("untested_candidates: {}", candidates.len());
// HACK(matthewjasper) This is a closure so that we can let the test
// create its blocks before the rest of the match. This currently
@ -1783,8 +1790,9 @@ pub(crate) fn lower_let_expr(
let expr_span = expr.span;
let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr, expr_span));
let wildcard = Pat::wildcard_from_ty(pat.ty);
let mut guard_candidate = Candidate::new(expr_place_builder.clone(), &pat, false);
let mut otherwise_candidate = Candidate::new(expr_place_builder.clone(), &wildcard, false);
let mut guard_candidate = Candidate::new(expr_place_builder.clone(), &pat, false, self);
let mut otherwise_candidate =
Candidate::new(expr_place_builder.clone(), &wildcard, false, self);
let fake_borrow_temps = self.lower_match_tree(
block,
pat.span,
@ -1794,8 +1802,8 @@ pub(crate) fn lower_let_expr(
);
let mut opt_expr_place: Option<(Option<&Place<'tcx>>, Span)> = None;
let expr_place: Place<'tcx>;
if let Ok(expr_builder) = expr_place_builder.try_upvars_resolved(self.tcx, &self.upvars) {
expr_place = expr_builder.into_place(self.tcx, &self.upvars);
if let Ok(expr_builder) = expr_place_builder.try_upvars_resolved(self) {
expr_place = expr_builder.into_place(self);
opt_expr_place = Some((Some(&expr_place), expr_span));
}
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
@ -2209,6 +2217,7 @@ fn bind_matched_candidate_for_arm_body<'b>(
/// first local is a binding for occurrences of `var` in the guard, which
/// will have type `&T`. The second local is a binding for occurrences of
/// `var` in the arm body, which will have type `T`.
#[instrument(skip(self), level = "debug")]
fn declare_binding(
&mut self,
source_info: SourceInfo,
@ -2223,19 +2232,12 @@ fn declare_binding(
opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
pat_span: Span,
) {
debug!(
"declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \
visibility_scope={:?}, source_info={:?})",
var_id, name, mode, var_ty, visibility_scope, source_info
);
let tcx = self.tcx;
let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope };
let binding_mode = match mode {
BindingMode::ByValue => ty::BindingMode::BindByValue(mutability),
BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability),
};
debug!("declare_binding: user_ty={:?}", user_ty);
let local = LocalDecl::<'tcx> {
mutability,
ty: var_ty,
@ -2285,7 +2287,7 @@ fn declare_binding(
} else {
LocalsForNode::One(for_arm_body)
};
debug!("declare_binding: vars={:?}", locals);
debug!(?locals);
self.var_indices.insert(var_id, locals);
}
@ -2302,8 +2304,8 @@ pub(crate) fn ast_let_else(
let (matching, failure) = self.in_if_then_scope(*let_else_scope, |this| {
let scrutinee = unpack!(block = this.lower_scrutinee(block, init, initializer_span));
let pat = Pat { ty: init.ty, span: else_block_span, kind: PatKind::Wild };
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false);
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false);
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false, this);
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false, this);
let fake_borrow_temps = this.lower_match_tree(
block,
initializer_span,

View File

@ -37,12 +37,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
///
/// only generates a single switch. If this happens this method returns
/// `true`.
#[instrument(skip(self, candidate), level = "debug")]
pub(super) fn simplify_candidate<'pat>(
&mut self,
candidate: &mut Candidate<'pat, 'tcx>,
) -> bool {
// repeatedly simplify match pairs until fixed point is reached
debug!(?candidate, "simplify_candidate");
debug!("{candidate:#?}");
// existing_bindings and new_bindings exists to keep the semantics in order.
// Reversing the binding order for bindings after `@` changes the binding order in places
@ -131,7 +132,7 @@ fn create_or_subcandidates<'pat>(
) -> Vec<Candidate<'pat, 'tcx>> {
pats.iter()
.map(|box pat| {
let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard);
let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard, self);
self.simplify_candidate(&mut candidate);
candidate
})
@ -155,17 +156,15 @@ fn simplify_match_pair<'pat>(
ascription: thir::Ascription { ref annotation, variance },
} => {
// Apply the type ascription to the value at `match_pair.place`, which is the
if let Ok(place_resolved) =
match_pair.place.clone().try_upvars_resolved(self.tcx, &self.upvars)
{
if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self) {
candidate.ascriptions.push(Ascription {
annotation: annotation.clone(),
source: place_resolved.into_place(self.tcx, &self.upvars),
source: place_resolved.into_place(self),
variance,
});
}
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
Ok(())
}
@ -184,12 +183,10 @@ fn simplify_match_pair<'pat>(
ref subpattern,
is_primary: _,
} => {
if let Ok(place_resolved) =
match_pair.place.clone().try_upvars_resolved(self.tcx, &self.upvars)
{
if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self) {
candidate.bindings.push(Binding {
span: match_pair.pattern.span,
source: place_resolved.into_place(self.tcx, &self.upvars),
source: place_resolved.into_place(self),
var_id: var,
binding_mode: mode,
});
@ -197,7 +194,7 @@ fn simplify_match_pair<'pat>(
if let Some(subpattern) = subpattern.as_ref() {
// this is the `x @ P` case; have to keep matching against `P` now
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
}
Ok(())
@ -308,7 +305,7 @@ fn simplify_match_pair<'pat>(
PatKind::Deref { ref subpattern } => {
let place_builder = match_pair.place.deref();
candidate.match_pairs.push(MatchPair::new(place_builder, subpattern));
candidate.match_pairs.push(MatchPair::new(place_builder, subpattern, self));
Ok(())
}

View File

@ -144,6 +144,7 @@ pub(super) fn add_variants_to_switch<'pat>(
}
}
#[instrument(skip(self, make_target_blocks, place_builder), level = "debug")]
pub(super) fn perform_test(
&mut self,
match_start_span: Span,
@ -153,19 +154,9 @@ pub(super) fn perform_test(
test: &Test<'tcx>,
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
) {
let place: Place<'tcx>;
if let Ok(test_place_builder) = place_builder.try_upvars_resolved(self.tcx, &self.upvars) {
place = test_place_builder.into_place(self.tcx, &self.upvars);
} else {
return;
}
debug!(
"perform_test({:?}, {:?}: {:?}, {:?})",
block,
place,
place.ty(&self.local_decls, self.tcx),
test
);
let place = place_builder.into_place(self);
let place_ty = place.ty(&self.local_decls, self.tcx);
debug!(?place, ?place_ty,);
let source_info = self.source_info(test.span);
match test.kind {
@ -733,14 +724,12 @@ fn candidate_after_variant_switch<'pat>(
// So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
// we want to create a set of derived match-patterns like
// `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
let elem =
ProjectionElem::Downcast(Some(adt_def.variant(variant_index).name), variant_index);
let downcast_place = match_pair.place.project(elem); // `(x as Variant)`
let downcast_place = match_pair.place.downcast(adt_def, variant_index); // `(x as Variant)`
let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
// e.g., `(x as Variant).0`
let place = downcast_place.clone().field(subpattern.field, subpattern.pattern.ty);
// e.g., `(x as Variant).0 @ P1`
MatchPair::new(place, &subpattern.pattern)
MatchPair::new(place, &subpattern.pattern, self)
});
candidate.match_pairs.extend(consequent_match_pairs);

View File

@ -1,9 +1,11 @@
use crate::build::expr::as_place::PlaceBase;
use crate::build::expr::as_place::PlaceBuilder;
use crate::build::matches::MatchPair;
use crate::build::Builder;
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty;
use rustc_middle::ty::TypeVisitable;
use smallvec::SmallVec;
use std::convert::TryInto;
@ -17,7 +19,7 @@ pub(crate) fn field_match_pairs<'pat>(
.iter()
.map(|fieldpat| {
let place = place.clone().field(fieldpat.field, fieldpat.pattern.ty);
MatchPair::new(place, &fieldpat.pattern)
MatchPair::new(place, &fieldpat.pattern, self)
})
.collect()
}
@ -31,23 +33,21 @@ pub(crate) fn prefix_slice_suffix<'pat>(
suffix: &'pat [Box<Pat<'tcx>>],
) {
let tcx = self.tcx;
let (min_length, exact_size) = if let Ok(place_resolved) =
place.clone().try_upvars_resolved(tcx, &self.upvars)
{
match place_resolved.into_place(tcx, &self.upvars).ty(&self.local_decls, tcx).ty.kind()
{
ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true),
_ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
}
} else {
((prefix.len() + suffix.len()).try_into().unwrap(), false)
};
let (min_length, exact_size) =
if let Ok(place_resolved) = place.clone().try_upvars_resolved(self) {
match place_resolved.into_place(self).ty(&self.local_decls, tcx).ty.kind() {
ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true),
_ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
}
} else {
((prefix.len() + suffix.len()).try_into().unwrap(), false)
};
match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
let elem =
ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
let place = place.clone().project(elem);
MatchPair::new(place, subpattern)
MatchPair::new(place, subpattern, self)
}));
if let Some(subslice_pat) = opt_slice {
@ -57,7 +57,7 @@ pub(crate) fn prefix_slice_suffix<'pat>(
to: if exact_size { min_length - suffix_len } else { suffix_len },
from_end: !exact_size,
});
match_pairs.push(MatchPair::new(subslice, subslice_pat));
match_pairs.push(MatchPair::new(subslice, subslice_pat, self));
}
match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
@ -68,7 +68,7 @@ pub(crate) fn prefix_slice_suffix<'pat>(
from_end: !exact_size,
};
let place = place.clone().project(elem);
MatchPair::new(place, subpattern)
MatchPair::new(place, subpattern, self)
}));
}
@ -96,10 +96,29 @@ pub(crate) fn false_edges(
}
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
pub(crate) fn new(
pub(in crate::build) fn new(
place: PlaceBuilder<'tcx>,
pattern: &'pat Pat<'tcx>,
cx: &Builder<'_, 'tcx>,
) -> MatchPair<'pat, 'tcx> {
// Force the place type to the pattern's type.
// FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
let mut place = match place.try_upvars_resolved(cx) {
Ok(val) | Err(val) => val,
};
// Only add the OpaqueCast projection if the given place is an opaque type and the
// expected type from the pattern is not.
let may_need_cast = match place.base() {
PlaceBase::Local(local) => {
let ty = Place::ty_from(local, place.projection(), &cx.local_decls, cx.tcx).ty;
ty != pattern.ty && ty.has_opaque_types()
}
_ => true,
};
if may_need_cast {
place = place.project(ProjectionElem::OpaqueCast(pattern.ty));
}
MatchPair { place, pattern }
}
}

View File

@ -553,6 +553,7 @@ pub(crate) fn in_opt_scope<F, R>(
/// Convenience wrapper that pushes a scope and then executes `f`
/// to build its contents, popping the scope afterwards.
#[instrument(skip(self, f), level = "debug")]
pub(crate) fn in_scope<F, R>(
&mut self,
region_scope: (region::Scope, SourceInfo),
@ -562,7 +563,6 @@ pub(crate) fn in_scope<F, R>(
where
F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
{
debug!("in_scope(region_scope={:?})", region_scope);
let source_scope = self.source_scope;
let tcx = self.tcx;
if let LintLevel::Explicit(current_hir_id) = lint_level {
@ -589,7 +589,7 @@ pub(crate) fn in_scope<F, R>(
let rv = unpack!(block = f(self));
unpack!(block = self.pop_scope(region_scope, block));
self.source_scope = source_scope;
debug!("in_scope: exiting region_scope={:?} block={:?}", region_scope, block);
debug!(?block);
block.and(rv)
}

View File

@ -48,6 +48,8 @@ pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> E
_ => None,
};
trace!(?expr.ty);
// Now apply adjustments, if any.
for adjustment in self.typeck_results.expr_adjustments(hir_expr) {
trace!(?expr, ?adjustment);
@ -56,6 +58,8 @@ pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> E
self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span));
}
trace!(?expr.ty, "after adjustments");
// Next, wrap this up in the expr's scope.
expr = Expr {
temp_lifetime,

View File

@ -200,6 +200,7 @@ fn normalize_range_pattern_ends(
}
}
#[instrument(skip(self), level = "debug")]
fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> {
let mut ty = self.typeck_results.node_type(pat.hir_id);
let mut span = pat.span;

View File

@ -842,7 +842,15 @@ fn is_useful<'p, 'tcx>(
}
}
} else {
let ty = v.head().ty();
let mut ty = v.head().ty();
// Opaque types can't get destructured/split, but the patterns can
// actually hint at hidden types, so we use the patterns' types instead.
if let ty::Opaque(..) = ty.kind() {
if let Some(row) = rows.first() {
ty = row.head().ty();
}
}
let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span());
let pcx = &PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive };

View File

@ -893,7 +893,7 @@ fn open_drop(&mut self) -> BasicBlock {
}
ty::Slice(ety) => self.open_drop_for_array(*ety, None),
_ => bug!("open drop from non-ADT `{:?}`", ty),
_ => span_bug!(self.source_info.span, "open drop from non-ADT `{:?}`", ty),
}
}

View File

@ -48,6 +48,7 @@ fn lift(&self) -> Self::Abstract {
match *self {
ProjectionElem::Deref => ProjectionElem::Deref,
ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty.lift()),
ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty.lift()),
ProjectionElem::Index(ref i) => ProjectionElem::Index(i.lift()),
ProjectionElem::Subslice { from, to, from_end } => {
ProjectionElem::Subslice { from, to, from_end }

View File

@ -1,8 +1,9 @@
// compile-flags: --edition=2021
// check-pass
#![feature(type_alias_impl_trait)]
fn main() {
type T = impl Copy; //~ ERROR unconstrained opaque type
type T = impl Copy;
let foo: T = (1u32, 2u32);
let (a, b): (u32, u32) = foo;
}

View File

@ -1,10 +0,0 @@
error: unconstrained opaque type
--> $DIR/cross_inference_pattern_bug.rs:5:14
|
LL | type T = impl Copy;
| ^^^^^^^^^
|
= note: `T` must be used in combination with a concrete type within the same module
error: aborting due to previous error

View File

@ -1,13 +1,13 @@
// known-bug: #96572
// compile-flags: --edition=2021 --crate-type=lib
// rustc-env:RUST_BACKTRACE=0
// check-pass
// tracked in https://github.com/rust-lang/rust/issues/96572
#![feature(type_alias_impl_trait)]
fn main() {
type T = impl Copy; // error: unconstrained opaque type
type T = impl Copy;
let foo: T = (1u32, 2u32);
let (a, b) = foo; // removing this line makes the code compile
let (a, b) = foo; // this line used to make the code fail
}

View File

@ -1,10 +0,0 @@
error: unconstrained opaque type
--> $DIR/cross_inference_pattern_bug_no_type.rs:10:14
|
LL | type T = impl Copy; // error: unconstrained opaque type
| ^^^^^^^^^
|
= note: `T` must be used in combination with a concrete type within the same module
error: aborting due to previous error

View File

@ -0,0 +1,10 @@
#![feature(type_alias_impl_trait)]
fn main() {
type T = impl Copy;
let foo: T = Some((1u32, 2u32));
match foo {
None => (),
Some((a, b, c)) => (), //~ ERROR mismatched types
}
}

View File

@ -0,0 +1,15 @@
error[E0308]: mismatched types
--> $DIR/issue-96572-unconstrained-mismatch.rs:8:14
|
LL | match foo {
| --- this expression has type `T`
LL | None => (),
LL | Some((a, b, c)) => (),
| ^^^^^^^^^ expected a tuple with 2 elements, found one with 3 elements
|
= note: expected tuple `(u32, u32)`
found tuple `(_, _, _)`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.

View File

@ -0,0 +1,92 @@
#![feature(type_alias_impl_trait)]
// check-pass
fn main() {
type T = impl Copy;
let foo: T = Some((1u32, 2u32));
match foo {
None => (),
Some((a, b)) => (),
}
}
fn upvar() {
#[derive(Copy, Clone)]
struct Foo((u32, u32));
type T = impl Copy;
let foo: T = Foo((1u32, 2u32));
let x = move || {
let Foo((a, b)) = foo;
};
}
fn enum_upvar() {
type T = impl Copy;
let foo: T = Some((1u32, 2u32));
let x = move || {
match foo {
None => (),
Some((a, b)) => (),
}
};
}
fn r#struct() {
#[derive(Copy, Clone)]
struct Foo((u32, u32));
type U = impl Copy;
let foo: U = Foo((1u32, 2u32));
let Foo((a, b)) = foo;
}
mod only_pattern {
type T = impl Copy;
fn foo(foo: T) {
let (mut x, mut y) = foo;
x = 42;
y = "foo";
}
type U = impl Copy;
fn bar(bar: Option<U>) {
match bar {
Some((mut x, mut y)) => {
x = 42;
y = "foo";
}
None => {}
}
}
}
mod only_pattern_rpit {
#[allow(unconditional_recursion)]
fn foo(b: bool) -> impl Copy {
let (mut x, mut y) = foo(false);
x = 42;
y = "foo";
if b {
panic!()
} else {
foo(true)
}
}
fn bar(b: bool) -> Option<impl Copy> {
if b {
return None;
}
match bar(!b) {
Some((mut x, mut y)) => {
x = 42;
y = "foo";
}
None => {}
}
None
}
}

View File

@ -260,6 +260,7 @@ fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &B
}
},
ProjectionElem::ConstantIndex { .. }
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. }
| ProjectionElem::Deref