From 84a444a1f45ce5a436e19391ea5a1d9312bed1ad Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 22 Jun 2022 15:28:28 +0000 Subject: [PATCH 01/11] Introduce opaque type to hidden type projection --- .../src/diagnostics/conflict_errors.rs | 4 +++- compiler/rustc_borrowck/src/diagnostics/mod.rs | 2 ++ .../src/diagnostics/mutability_errors.rs | 1 + compiler/rustc_borrowck/src/lib.rs | 2 ++ compiler/rustc_borrowck/src/places_conflict.rs | 13 +++++++++++++ compiler/rustc_borrowck/src/prefixes.rs | 1 + compiler/rustc_borrowck/src/type_check/mod.rs | 9 ++++++++- compiler/rustc_codegen_cranelift/src/base.rs | 1 + .../src/value_and_place.rs | 8 ++++++++ compiler/rustc_codegen_ssa/src/mir/place.rs | 16 ++++++++++++++++ .../rustc_const_eval/src/interpret/projection.rs | 10 ++++++++++ .../src/transform/check_consts/check.rs | 1 + .../src/transform/check_consts/qualifs.rs | 1 + .../src/transform/promote_consts.rs | 2 +- compiler/rustc_middle/src/mir/mod.rs | 8 +++++++- compiler/rustc_middle/src/mir/syntax.rs | 7 +++++++ compiler/rustc_middle/src/mir/tcx.rs | 4 +++- compiler/rustc_middle/src/mir/type_foldable.rs | 1 + compiler/rustc_middle/src/mir/visit.rs | 7 ++++++- .../rustc_mir_build/src/build/expr/as_place.rs | 2 ++ .../src/move_paths/abs_domain.rs | 1 + compiler/rustc_mir_transform/src/add_retag.rs | 1 + .../clippy_utils/src/qualify_min_const_fn.rs | 1 + 23 files changed, 97 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 0ceb63477c809..f4d8039eda0e3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2093,7 +2093,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } 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 diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 53c07a3d481e0..fada3d45fbed8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -226,6 +226,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } ProjectionElem::Downcast(..) if including_downcast.0 => 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 { @@ -286,6 +287,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { 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), }, }; diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 8134e1226628f..cb7077fe62171 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -169,6 +169,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .., ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..), ], diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index e8673ecd3a0b7..74321ddcd9919 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1788,6 +1788,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { 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*/) => @@ -2179,6 +2180,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | 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 { diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index 97335fd0dffae..5b67e6aa1cffa 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -255,6 +255,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 @@ -322,6 +323,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. @@ -525,6 +537,7 @@ fn place_projection_conflict<'tcx>( | ProjectionElem::Field(..) | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..), _, diff --git a/compiler/rustc_borrowck/src/prefixes.rs b/compiler/rustc_borrowck/src/prefixes.rs index bdf2becb71126..2b50cbac9a02d 100644 --- a/compiler/rustc_borrowck/src/prefixes.rs +++ b/compiler/rustc_borrowck/src/prefixes.rs @@ -81,6 +81,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { } ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } + | ProjectionElem::OpaqueCast { .. } | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => { cursor = cursor_base; diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index cf2140097e6da..e139a73efc5e9 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -790,6 +790,11 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } PlaceTy::from_ty(fty) } + ProjectionElem::OpaqueCast(ty) => { + let ty = self.sanitize_type(place, ty); + let ty = self.cx.normalize(ty, location); + PlaceTy::from_ty(ty) + } } } @@ -1195,10 +1200,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { 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; } @@ -2493,6 +2499,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } ProjectionElem::Field(..) | ProjectionElem::Downcast(..) + | ProjectionElem::OpaqueCast(..) | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 63cd4d6de4c3e..54652623d9401 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -825,6 +825,7 @@ pub(crate) fn codegen_place<'tcx>( 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); } diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index a68225de58b32..8ff35d2f76dbf 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -615,6 +615,14 @@ impl<'tcx> CPlace<'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>, diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 58cee0c8bb0db..421d6f807ae8e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -411,6 +411,21 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { downcast } + pub fn project_type>( + &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>(&self, bx: &mut Bx) { bx.lifetime_start(self.llval, self.layout.size); } @@ -459,6 +474,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { 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); diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 31fb6a8944df6..eced1e9e6393e 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -351,6 +351,11 @@ where ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { use rustc_middle::mir::ProjectionElem::*; Ok(match proj_elem { + OpaqueCast(ty) => { + let mut place = *base; + 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(), @@ -375,6 +380,11 @@ where ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { use rustc_middle::mir::ProjectionElem::*; Ok(match proj_elem { + OpaqueCast(ty) => { + let mut op = *base; + 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(), diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 628298df47385..0581f4919782d 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -652,6 +652,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { ProjectionElem::ConstantIndex { .. } | ProjectionElem::Downcast(..) + | ProjectionElem::OpaqueCast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {} diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 29464cf8c4e4f..c2b4f6eca5ced 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -316,6 +316,7 @@ where ProjectionElem::Deref | ProjectionElem::Field(_, _) + | ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(_, _) diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index ed4d8c95d1e61..daa154576ae4b 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -361,7 +361,7 @@ impl<'tcx> Validator<'_, 'tcx> { return Err(Unpromotable); } } - ProjectionElem::Downcast(..) => { + ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => { return Err(Unpromotable); } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index f61cb7e8c472e..0b5d23be58d85 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1397,6 +1397,7 @@ impl ProjectionElem { 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 @@ impl Debug for Place<'_> { for elem in self.projection.iter() { match elem { + ProjectionElem::OpaqueCast(ty) => { + write!(fmt, " as {})", ty)?; + } ProjectionElem::Downcast(Some(name), _index) => { write!(fmt, " as {})", name)?; } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 510316c778bc7..263c2ca3c700e 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -754,6 +754,9 @@ pub type AssertMessage<'tcx> = AssertKind>; /// 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 @@ -856,6 +859,10 @@ pub enum ProjectionElem { /// /// The included Symbol is the name of the variant, used for printing MIR. Downcast(Option, 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 diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index fd3359ea80fe5..c6975df45efc2 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -57,7 +57,7 @@ impl<'tcx> PlaceTy<'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, |...| { ... })` @@ -71,6 +71,7 @@ impl<'tcx> PlaceTy<'tcx> { param_env: ty::ParamEnv<'tcx>, elem: &ProjectionElem, 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, @@ -109,6 +110,7 @@ impl<'tcx> PlaceTy<'tcx> { 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 diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index 82a6b0c506f58..a73ef23e28132 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -182,6 +182,7 @@ impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> { Ok(match self { Deref => Deref, Field(f, ty) => Field(f, ty.try_fold_with(folder)?), + OpaqueCast(ty) => OpaqueCast(ty.try_fold_with(folder)?), Index(v) => Index(v.try_fold_with(folder)?), Downcast(symbol, variantidx) => Downcast(symbol, variantidx), ConstantIndex { offset, min_length, from_end } => { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index d285728ec0783..e5599fb15ad31 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1064,6 +1064,11 @@ macro_rules! visit_place_fns { 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 { .. } @@ -1133,7 +1138,7 @@ macro_rules! visit_place_fns { location: Location, ) { match elem { - ProjectionElem::Field(_field, ty) => { + ProjectionElem::OpaqueCast(ty) | ProjectionElem::Field(_, ty) => { self.visit_ty(ty, TyContext::Location(location)); } ProjectionElem::Index(local) => { diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index e88f9dc1f08f5..82debdf7fa1bf 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -105,6 +105,7 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>( continue; } ProjectionElem::Index(..) + | ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { // We don't capture array-access projections. @@ -795,6 +796,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } ProjectionElem::Field(..) | ProjectionElem::Downcast(..) + | ProjectionElem::OpaqueCast(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => (), } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs index 28936274baafa..7806e8f45d3ad 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs @@ -48,6 +48,7 @@ impl<'tcx> Lift for PlaceElem<'tcx> { 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 } diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index b91ae083cf594..86327ade94b8a 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -28,6 +28,7 @@ fn is_stable(place: PlaceRef<'_>) -> bool { ProjectionElem::Field { .. } | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | + ProjectionElem::OpaqueCast { .. } | ProjectionElem::Downcast { .. } => true, } }) diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 3bf75bcbee83e..9690ad2777177 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -252,6 +252,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 From a034446faea140b00e59ada55519ac9bc7b08ee9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 23 Jun 2022 14:32:01 +0000 Subject: [PATCH 02/11] Add some tracing instrumentation --- .../rustc_mir_build/src/build/expr/as_temp.rs | 5 +-- .../rustc_mir_build/src/build/expr/into.rs | 3 +- .../rustc_mir_build/src/build/matches/mod.rs | 35 +++++++++---------- .../src/build/matches/simplify.rs | 3 +- .../rustc_mir_build/src/build/matches/test.rs | 1 + compiler/rustc_mir_build/src/build/scope.rs | 4 +-- compiler/rustc_mir_build/src/thir/cx/expr.rs | 4 +++ .../rustc_mir_build/src/thir/pattern/mod.rs | 1 + 8 files changed, 29 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs index 724b72f8769b8..dac8862647a85 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs @@ -23,6 +23,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr: &Expr<'tcx>, mutability: Mutability, ) -> BlockAnd { - debug!( - "as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})", - block, temp_lifetime, expr, mutability - ); let this = self; let expr_span = expr.span; diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 017d43d10a9a9..6132de6d4862a 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -15,14 +15,13 @@ use std::iter; 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 diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 7067a48b783ec..f2c8b80b1d763 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -654,6 +654,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// 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, @@ -662,7 +663,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { has_guard: ArmHasGuard, opt_match_place: Option<(Option<&Place<'tcx>>, Span)>, ) -> Option { - debug!("declare_bindings: pattern={:?}", pattern); self.visit_primary_bindings( &pattern, UserTypeProjections::none(), @@ -1048,6 +1048,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 +1058,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidates: &mut [&mut Candidate<'pat, 'tcx>], fake_borrows: &mut Option>>, ) { - 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 +1376,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) } + #[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,7 +1389,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { place: PlaceBuilder<'tcx>, fake_borrows: &mut Option>>, ) { - 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)) @@ -1634,9 +1634,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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 @@ -2195,6 +2200,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// 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, @@ -2209,19 +2215,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { opt_match_place: Option<(Option>, 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, @@ -2271,7 +2270,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { LocalsForNode::One(for_arm_body) }; - debug!("declare_binding: vars={:?}", locals); + debug!(?locals); self.var_indices.insert(var_id, locals); } diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index c6298904140c3..878b17ce5a41d 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -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 diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 598da80c574af..1ef2524f136a5 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -144,6 +144,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + #[instrument(skip(self, make_target_blocks, place_builder), level = "debug")] pub(super) fn perform_test( &mut self, match_start_span: Span, diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index b9fd8c50e6a04..ab37c4802852a 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -553,6 +553,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// 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( &mut self, region_scope: (region::Scope, SourceInfo), @@ -562,7 +563,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { where F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd, { - 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 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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) } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 4eb3607e9cca0..05da33caa91bd 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -48,6 +48,8 @@ impl<'tcx> Cx<'tcx> { _ => 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 @@ impl<'tcx> Cx<'tcx> { 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, diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index a13748a2d474a..7d22f7b69d862 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -196,6 +196,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } } + #[instrument(skip(self), level = "debug")] fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { let mut ty = self.typeck_results.node_type(pat.hir_id); From eeb10335ce94d35f3c13f7d14fabc91b3dcd469c Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 23 Jun 2022 16:08:02 +0000 Subject: [PATCH 03/11] Pass one argument instead of fetching two fields of it at every call site --- .../src/build/expr/as_place.rs | 57 +++++++------------ .../src/build/expr/as_rvalue.rs | 12 ++-- .../rustc_mir_build/src/build/expr/into.rs | 4 +- .../rustc_mir_build/src/build/matches/mod.rs | 43 ++++++-------- .../src/build/matches/simplify.rs | 12 ++-- .../rustc_mir_build/src/build/matches/test.rs | 6 +- .../rustc_mir_build/src/build/matches/util.rs | 26 ++++----- 7 files changed, 60 insertions(+), 100 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 82debdf7fa1bf..98502737dc500 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -71,7 +71,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>, } @@ -202,10 +202,9 @@ 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>( +fn to_upvars_resolved_place_builder<'tcx>( from_builder: PlaceBuilder<'tcx>, - tcx: TyCtxt<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, + cx: &Builder<'_, 'tcx>, ) -> Result, PlaceBuilder<'tcx>> { match from_builder.base { PlaceBase::Local(_) => Ok(from_builder), @@ -220,13 +219,13 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( let Some((capture_index, capture)) = find_capture_matching_projections( - typeck_results, + cx.typeck_results, var_hir_id, closure_def_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", @@ -243,8 +242,8 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( }; // We won't be building MIR if the closure wasn't local - let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); - let closure_ty = typeck_results.node_type(closure_hir_id); + let closure_hir_id = cx.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); + let closure_ty = cx.typeck_results.node_type(closure_hir_id); let substs = match closure_ty.kind() { ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), @@ -316,24 +315,16 @@ fn strip_prefix<'tcx>( } impl<'tcx> PlaceBuilder<'tcx> { - pub(crate) fn into_place<'a>( - self, - tcx: TyCtxt<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, - ) -> Place<'tcx> { + pub(crate) 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, typeck_results).into_place(tcx, typeck_results) + self.expect_upvars_resolved(cx).into_place(cx) } } - 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() + fn expect_upvars_resolved(self, cx: &Builder<'_, 'tcx>) -> PlaceBuilder<'tcx> { + to_upvars_resolved_place_builder(self, cx).unwrap() } /// Attempts to resolve the `PlaceBuilder`. @@ -347,12 +338,11 @@ impl<'tcx> PlaceBuilder<'tcx> { /// 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(crate) fn try_upvars_resolved<'a>( + pub(crate) fn try_upvars_resolved( self, - tcx: TyCtxt<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, + cx: &Builder<'_, 'tcx>, ) -> Result, PlaceBuilder<'tcx>> { - to_upvars_resolved_place_builder(self, tcx, typeck_results) + to_upvars_resolved_place_builder(self, cx) } pub(crate) fn base(&self) -> PlaceBase { @@ -412,7 +402,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr: &Expr<'tcx>, ) -> BlockAnd> { let place_builder = unpack!(block = self.as_place_builder(block, expr)); - block.and(place_builder.into_place(self.tcx, self.typeck_results)) + block.and(place_builder.into_place(self)) } /// This is used when constructing a compound `Place`, so that we can avoid creating @@ -436,7 +426,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr: &Expr<'tcx>, ) -> BlockAnd> { let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr)); - block.and(place_builder.into_place(self.tcx, self.typeck_results)) + block.and(place_builder.into_place(self)) } /// This is used when constructing a compound `Place`, so that we can avoid creating @@ -531,7 +521,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { inferred_ty: expr.ty, }); - let place = place_builder.clone().into_place(this.tcx, this.typeck_results); + let place = place_builder.clone().into_place(this); this.cfg.push( block, Statement { @@ -683,7 +673,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.tcx, self.typeck_results); + base_place = base_place.expect_upvars_resolved(self); self.add_fake_borrows_of_base( &base_place, block, @@ -711,12 +701,7 @@ 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.into_place(self.tcx, self.typeck_results)), - ); + self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.into_place(self))); // lt = idx < len self.cfg.push_assign( block, diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index 15f2d17c4e08b..93f76333165a5 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -321,11 +321,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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.typeck_results) - { - let mir_place = - place_builder_resolved.into_place(this.tcx, this.typeck_results); + 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)), @@ -616,8 +613,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // 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.typeck_results); + let enclosing_upvars_resolved = arg_place_builder.clone().into_place(this); match enclosing_upvars_resolved.as_ref() { PlaceRef { @@ -654,7 +650,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false }, }; - let arg_place = arg_place_builder.into_place(this.tcx, this.typeck_results); + let arg_place = arg_place_builder.into_place(this); this.cfg.push_assign( block, diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 6132de6d4862a..d1ef515b3d29b 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -365,9 +365,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None => { let place_builder = place_builder.clone(); this.consume_by_copy_or_move( - place_builder - .field(n, *ty) - .into_place(this.tcx, this.typeck_results), + place_builder.field(n, *ty).into_place(this), ) } }) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index f2c8b80b1d763..7fc28d624da5c 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -220,10 +220,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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.typeck_results) - { - let scrutinee_place = scrutinee_builder.into_place(self.tcx, self.typeck_results); + 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); } @@ -348,12 +346,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // ``` 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.typeck_results) + if let Ok(scrutinee_builder) = + scrutinee_place_builder.clone().try_upvars_resolved(this) { - scrutinee_place = - scrutinee_builder.into_place(this.tcx, this.typeck_results); + scrutinee_place = scrutinee_builder.into_place(this); opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span)); } let scope = this.declare_bindings( @@ -602,12 +598,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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 @@ -622,10 +612,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // let (v1, v2) = foo; // }; // ``` - if let Ok(match_pair_resolved) = - initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results) - { - let place = match_pair_resolved.into_place(self.tcx, self.typeck_results); + 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); } } @@ -1605,9 +1600,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // 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.typeck_results) + match_place.clone().try_upvars_resolved(self) { - let resolved_place = match_place_resolved.into_place(self.tcx, self.typeck_results); + let resolved_place = match_place_resolved.into_place(self); fb.insert(resolved_place); } @@ -1799,10 +1794,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); 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.typeck_results) - { - expr_place = expr_builder.into_place(self.tcx, self.typeck_results); + 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(); diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index 878b17ce5a41d..6fa817da28a3c 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -156,12 +156,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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.typeck_results) - { + 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.typeck_results), + source: place_resolved.into_place(self), variance, }); } @@ -185,12 +183,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ref subpattern, is_primary: _, } => { - if let Ok(place_resolved) = - match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results) - { + 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.typeck_results), + source: place_resolved.into_place(self), var_id: var, binding_mode: mode, }); diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 1ef2524f136a5..92f00560f0139 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -155,10 +155,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { make_target_blocks: impl FnOnce(&mut Self) -> Vec, ) { let place: Place<'tcx>; - if let Ok(test_place_builder) = - place_builder.try_upvars_resolved(self.tcx, self.typeck_results) - { - place = test_place_builder.into_place(self.tcx, self.typeck_results); + if let Ok(test_place_builder) = place_builder.try_upvars_resolved(self) { + place = test_place_builder.into_place(self); } else { return; } diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index 9a1e98d3bb18d..38839e6305964 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -31,21 +31,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { suffix: &'pat [Pat<'tcx>], ) { let tcx = self.tcx; - let (min_length, exact_size) = if let Ok(place_resolved) = - place.clone().try_upvars_resolved(tcx, self.typeck_results) - { - match place_resolved - .into_place(tcx, self.typeck_results) - .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 = @@ -100,7 +94,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { - pub(crate) fn new( + pub(in crate::build) fn new( place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, ) -> MatchPair<'pat, 'tcx> { From 7a8a048b5853c6e44af53d924a7c0feb2140e5af Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 24 Jun 2022 14:53:59 +0000 Subject: [PATCH 04/11] Remove early return that would likely have caused miscompilations if it ever happened --- .../rustc_mir_build/src/build/matches/test.rs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 92f00560f0139..4e2a137c3fca9 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -154,19 +154,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { test: &Test<'tcx>, make_target_blocks: impl FnOnce(&mut Self) -> Vec, ) { - let place: Place<'tcx>; - if let Ok(test_place_builder) = place_builder.try_upvars_resolved(self) { - place = test_place_builder.into_place(self); - } 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 { From 0c6918fe210ebed9639c2f1b1fbfac4eb0fef38a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jun 2022 14:46:36 +0000 Subject: [PATCH 05/11] Reuse a helper method instead of manually rolling it --- compiler/rustc_mir_build/src/build/matches/test.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 4e2a137c3fca9..63acd731db7c9 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -724,9 +724,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // 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); From 12457f814cffc05ece8ad588d83aa87b69d78296 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jun 2022 16:16:11 +0000 Subject: [PATCH 06/11] Some tracing helpers --- compiler/rustc_mir_build/src/build/expr/as_place.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 98502737dc500..4bd7ef943b970 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -202,6 +202,7 @@ 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. +#[instrument(level = "trace", skip(cx))] fn to_upvars_resolved_place_builder<'tcx>( from_builder: PlaceBuilder<'tcx>, cx: &Builder<'_, 'tcx>, @@ -270,12 +271,14 @@ fn to_upvars_resolved_place_builder<'tcx>( // We used some of the projections to build the capture itself, // now we apply the remaining to the upvar resolved place. + trace!(?capture.place, ?from_builder.projection); let remaining_projections = strip_prefix( capture.place.base_ty, from_builder.projection, &capture.place.projections, ); upvar_resolved_place_builder.projection.extend(remaining_projections); + trace!(?upvar_resolved_place_builder); Ok(upvar_resolved_place_builder) } From 728c7e8bda215dc0ef055a817964f199427512e0 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jun 2022 16:26:41 +0000 Subject: [PATCH 07/11] Allow destructuring opaque types, since the patterns constrain the opaque types --- .../src/build/expr/as_place.rs | 33 ++++++++++++++++--- .../rustc_mir_build/src/build/matches/mod.rs | 2 +- .../rustc_mir_build/src/build/matches/util.rs | 4 +++ .../src/thir/pattern/usefulness.rs | 6 +++- .../cross_inference_pattern_bug.rs | 3 +- .../cross_inference_pattern_bug.stderr | 10 ------ .../cross_inference_pattern_bug_no_type.rs | 6 ++-- ...cross_inference_pattern_bug_no_type.stderr | 10 ------ .../issue-96572-unconstrained-mismatch.rs | 10 ++++++ .../issue-96572-unconstrained-mismatch.stderr | 15 +++++++++ .../issue-96572-unconstrained-struct.rs | 11 +++++++ .../issue-96572-unconstrained-upvar-enum.rs | 13 ++++++++ .../issue-96572-unconstrained-upvar.rs | 13 ++++++++ .../issue-96572-unconstrained.rs | 11 +++++++ 14 files changed, 116 insertions(+), 31 deletions(-) delete mode 100644 src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.stderr delete mode 100644 src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.stderr create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.rs create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.stderr create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-struct.rs create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar-enum.rs create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar.rs create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained.rs diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 4bd7ef943b970..6abf419dd4991 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -7,6 +7,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::hir::place::Projection as HirProjection; use rustc_middle::hir::place::ProjectionKind as HirProjectionKind; use rustc_middle::middle::region; +use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::AssertKind::BoundsCheck; use rustc_middle::mir::*; use rustc_middle::thir::*; @@ -104,8 +105,9 @@ 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::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { // We don't capture array-access projections. @@ -297,16 +299,21 @@ fn strip_prefix<'tcx>( prefix_projections: &[HirProjection<'tcx>], ) -> impl Iterator> { 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); @@ -320,7 +327,23 @@ fn strip_prefix<'tcx>( impl<'tcx> PlaceBuilder<'tcx> { pub(crate) fn into_place(self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> { if let PlaceBase::Local(local) = self.base { - Place { local, projection: cx.tcx.intern_place_elems(&self.projection) } + let mut projections = vec![]; + let mut ty = PlaceTy::from_ty(cx.local_decls[local].ty); + for projection in self.projection { + // Only preserve those opaque casts that actually go from an opaque type + // to another type. + if let ProjectionElem::OpaqueCast(t) = projection { + if let ty::Opaque(..) = ty.ty.kind() { + if t != ty.ty { + projections.push(ProjectionElem::OpaqueCast(t)); + } + } + } else { + projections.push(projection); + } + ty = ty.projection_ty(cx.tcx, projection); + } + Place { local, projection: cx.tcx.intern_place_elems(&projections) } } else { self.expect_upvars_resolved(cx).into_place(cx) } diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 7fc28d624da5c..86ef666eac8d2 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -867,7 +867,7 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { Candidate { span: pattern.span, has_guard, - match_pairs: smallvec![MatchPair { place, pattern }], + match_pairs: smallvec![MatchPair::new(place, pattern)], bindings: Vec::new(), ascriptions: Vec::new(), subcandidates: Vec::new(), diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index 38839e6305964..4a7edc517f4e4 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -98,6 +98,10 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, ) -> MatchPair<'pat, 'tcx> { + // Force the place type to the pattern's type. + // FIXME(oli-obk): only do this when we don't already know the place type. + // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? + let place = place.project(ProjectionElem::OpaqueCast(pattern.ty)); MatchPair { place, pattern } } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 9e7a267ecbd7f..3203f801fa03f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -967,7 +967,11 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>( }) .collect(); - let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty)); + // In case we're matching on an opaque type in its defining scope, the patterns define the hidden type. + // The wildcard pattern needs to have the same type, otherwise it will always be deemed useful, even if the + // match is exhaustive for the pattern type. + let wild_ty = arms.first().map_or(scrut_ty, |arm| arm.pat.ty()); + let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(wild_ty)); let v = PatStack::from_pattern(wild_pattern); let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, scrut_hir_id, false, true); let non_exhaustiveness_witnesses = match usefulness { diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs index 811832848d9dd..9a50c0f988a56 100644 --- a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs +++ b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs @@ -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; } diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.stderr b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.stderr deleted file mode 100644 index 03b172e6de570..0000000000000 --- a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.stderr +++ /dev/null @@ -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 - diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs index 328096d44b4b5..b929122a6c23f 100644 --- a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs +++ b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs @@ -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 } diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.stderr b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.stderr deleted file mode 100644 index 8aa1f49563995..0000000000000 --- a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.stderr +++ /dev/null @@ -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 - diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.rs new file mode 100644 index 0000000000000..825710851b01f --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.rs @@ -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 + } +} diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.stderr b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.stderr new file mode 100644 index 0000000000000..728244a1844db --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.stderr @@ -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`. diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-struct.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-struct.rs new file mode 100644 index 0000000000000..3351d9bcff1f8 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-struct.rs @@ -0,0 +1,11 @@ +#![feature(type_alias_impl_trait)] +// check-pass + +#[derive(Copy, Clone)] +struct Foo((u32, u32)); + +fn main() { + type U = impl Copy; + let foo: U = Foo((1u32, 2u32)); + let Foo((a, b)) = foo; +} diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar-enum.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar-enum.rs new file mode 100644 index 0000000000000..ef3279a98d199 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar-enum.rs @@ -0,0 +1,13 @@ +#![feature(type_alias_impl_trait)] +// check-pass + +fn main() { + type T = impl Copy; + let foo: T = Some((1u32, 2u32)); + let x = move || { + match foo { + None => (), + Some((a, b)) => (), + } + }; +} diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar.rs new file mode 100644 index 0000000000000..bb0fc7c7534f5 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar.rs @@ -0,0 +1,13 @@ +#![feature(type_alias_impl_trait)] +// check-pass + +#[derive(Copy, Clone)] +struct Foo((u32, u32)); + +fn main() { + type T = impl Copy; + let foo: T = Foo((1u32, 2u32)); + let x = move || { + let Foo((a, b)) = foo; + }; +} diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained.rs new file mode 100644 index 0000000000000..4b9ed7f28eb3f --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained.rs @@ -0,0 +1,11 @@ +#![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)) => (), + } +} From 6b33d5bfa94f0005c2ae7136f4cda14a396e42bc Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 28 Jun 2022 07:17:43 +0000 Subject: [PATCH 08/11] Make destructuring a defining use --- compiler/rustc_borrowck/src/type_check/mod.rs | 8 ++++++++ .../issue-96572-unconstrained-only-pattern.rs | 12 ++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index e139a73efc5e9..8e763a02af33f 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -793,6 +793,14 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { 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) } } diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs new file mode 100644 index 0000000000000..3f0445f50374d --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs @@ -0,0 +1,12 @@ +#![feature(type_alias_impl_trait)] +// check-pass + +type T = impl Copy; + +fn foo(foo: T) { + let (mut x, mut y) = foo; + x = 42; + y = "foo"; +} + +fn main() {} From ea68ce7fac0a5888e54e5725066b04fcd4290efa Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 28 Jun 2022 08:00:25 +0000 Subject: [PATCH 09/11] Revert a hack that only ever worked on level deep --- compiler/rustc_mir_build/src/thir/pattern/usefulness.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 3203f801fa03f..9e7a267ecbd7f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -967,11 +967,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>( }) .collect(); - // In case we're matching on an opaque type in its defining scope, the patterns define the hidden type. - // The wildcard pattern needs to have the same type, otherwise it will always be deemed useful, even if the - // match is exhaustive for the pattern type. - let wild_ty = arms.first().map_or(scrut_ty, |arm| arm.pat.ty()); - let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(wild_ty)); + let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty)); let v = PatStack::from_pattern(wild_pattern); let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, scrut_hir_id, false, true); let non_exhaustiveness_witnesses = match usefulness { From 92e470a1de4f809af62d38df9a968c41f8887189 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 28 Jun 2022 10:49:55 +0000 Subject: [PATCH 10/11] Move constructor into the branch that actually uses it --- .../rustc_mir_build/src/thir/pattern/usefulness.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 9e7a267ecbd7f..a17d8f336303b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -806,11 +806,6 @@ fn is_useful<'p, 'tcx>( debug_assert!(rows.iter().all(|r| r.len() == v.len())); - let ty = v.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 }; - // If the first pattern is an or-pattern, expand it. let mut ret = Usefulness::new_not_useful(witness_preference); if v.head().is_or_pat() { @@ -832,6 +827,11 @@ fn is_useful<'p, 'tcx>( } } } else { + let ty = v.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 }; + let v_ctor = v.head().ctor(); debug!(?v_ctor); if let Constructor::IntRange(ctor_range) = &v_ctor { From 57e9f7a556ae4873acc14c62bb4d6fc7ed6123ac Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 28 Jun 2022 11:19:38 +0000 Subject: [PATCH 11/11] Infer wildcard type from other patterns at every pattern level --- .../src/thir/pattern/deconstruct_pat.rs | 37 +++++++++---------- .../src/thir/pattern/usefulness.rs | 20 +++++++--- ...e-96572-unconstrained-only-pattern-rpit.rs | 29 +++++++++++++++ .../issue-96572-unconstrained-only-pattern.rs | 12 ++++++ 4 files changed, 72 insertions(+), 26 deletions(-) create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern-rpit.rs diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 60db98073a3b9..c0fd19cf71c31 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -1202,35 +1202,32 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { /// Creates a new list of wildcard fields for a given constructor. The result must have a /// length of `constructor.arity()`. - pub(super) fn wildcards( - cx: &MatchCheckCtxt<'p, 'tcx>, - ty: Ty<'tcx>, - constructor: &Constructor<'tcx>, - ) -> Self { + #[instrument(level = "trace")] + pub(super) fn wildcards(pcx: PatCtxt<'_, 'p, 'tcx>, constructor: &Constructor<'tcx>) -> Self { let ret = match constructor { - Single | Variant(_) => match ty.kind() { - ty::Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter()), - ty::Ref(_, rty, _) => Fields::wildcards_from_tys(cx, once(*rty)), + Single | Variant(_) => match pcx.ty.kind() { + ty::Tuple(fs) => Fields::wildcards_from_tys(pcx.cx, fs.iter()), + ty::Ref(_, rty, _) => Fields::wildcards_from_tys(pcx.cx, once(*rty)), ty::Adt(adt, substs) => { if adt.is_box() { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. - Fields::wildcards_from_tys(cx, once(substs.type_at(0))) + Fields::wildcards_from_tys(pcx.cx, once(substs.type_at(0))) } else { let variant = &adt.variant(constructor.variant_index_for_adt(*adt)); - let tys = Fields::list_variant_nonhidden_fields(cx, ty, variant) + let tys = Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant) .map(|(_, ty)| ty); - Fields::wildcards_from_tys(cx, tys) + Fields::wildcards_from_tys(pcx.cx, tys) } } - _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), + _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx), }, - Slice(slice) => match *ty.kind() { + Slice(slice) => match *pcx.ty.kind() { ty::Slice(ty) | ty::Array(ty, _) => { let arity = slice.arity(); - Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty)) + Fields::wildcards_from_tys(pcx.cx, (0..arity).map(|_| ty)) } - _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), + _ => bug!("bad slice pattern {:?} {:?}", constructor, pcx), }, Str(..) | FloatRange(..) @@ -1243,7 +1240,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { bug!("called `Fields::wildcards` on an `Or` ctor") } }; - debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); + debug!(?ret); ret } @@ -1286,7 +1283,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self { - let fields = Fields::wildcards(pcx.cx, pcx.ty, &ctor); + let fields = Fields::wildcards(pcx, &ctor); DeconstructedPat::new(ctor, fields, pcx.ty, DUMMY_SP) } @@ -1553,13 +1550,13 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { /// `other_ctor` can be different from `self.ctor`, but must be covered by it. pub(super) fn specialize<'a>( &'a self, - cx: &MatchCheckCtxt<'p, 'tcx>, + pcx: PatCtxt<'_, 'p, 'tcx>, other_ctor: &Constructor<'tcx>, ) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> { match (&self.ctor, other_ctor) { (Wildcard, _) => { // We return a wildcard for each field of `other_ctor`. - Fields::wildcards(cx, self.ty, other_ctor).iter_patterns().collect() + Fields::wildcards(pcx, other_ctor).iter_patterns().collect() } (Slice(self_slice), Slice(other_slice)) if self_slice.arity() != other_slice.arity() => @@ -1578,7 +1575,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { let prefix = &self.fields.fields[..prefix]; let suffix = &self.fields.fields[self_slice.arity() - suffix..]; let wildcard: &_ = - cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty)); + pcx.cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty)); let extra_wildcards = other_slice.arity() - self_slice.arity(); let extra_wildcards = (0..extra_wildcards).map(|_| wildcard); prefix.iter().chain(extra_wildcards).chain(suffix).collect() diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index a17d8f336303b..f27ec22a8dee2 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -411,12 +411,12 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { /// This is roughly the inverse of `Constructor::apply`. fn pop_head_constructor( &self, - cx: &MatchCheckCtxt<'p, 'tcx>, + pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>, ) -> PatStack<'p, 'tcx> { // We pop the head pattern and push the new fields extracted from the arguments of // `self.head()`. - let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(cx, ctor); + let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(pcx, ctor); new_fields.extend_from_slice(&self.pats[1..]); PatStack::from_vec(new_fields) } @@ -475,7 +475,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { let mut matrix = Matrix::empty(); for row in &self.patterns { if ctor.is_covered_by(pcx, row.head().ctor()) { - let new_row = row.pop_head_constructor(pcx.cx, ctor); + let new_row = row.pop_head_constructor(pcx, ctor); matrix.push(new_row); } } @@ -786,7 +786,7 @@ fn is_useful<'p, 'tcx>( is_under_guard: bool, is_top_level: bool, ) -> Usefulness<'p, 'tcx> { - debug!("matrix,v={:?}{:?}", matrix, v); + debug!(?matrix, ?v); let Matrix { patterns: rows, .. } = matrix; // The base case. We are pattern-matching on () and the return value is @@ -827,7 +827,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(..) = v.head().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 }; @@ -853,7 +861,7 @@ fn is_useful<'p, 'tcx>( debug!("specialize({:?})", ctor); // We cache the result of `Fields::wildcards` because it is used a lot. let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor); - let v = v.pop_head_constructor(cx, &ctor); + let v = v.pop_head_constructor(pcx, &ctor); let usefulness = ensure_sufficient_stack(|| { is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false) }); diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern-rpit.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern-rpit.rs new file mode 100644 index 0000000000000..c0a371eca1c6f --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern-rpit.rs @@ -0,0 +1,29 @@ +// check-pass + +#[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 { + if b { + return None; + } + match bar(!b) { + Some((mut x, mut y)) => { + x = 42; + y = "foo"; + } + None => {} + } + None +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs index 3f0445f50374d..ec249958590f1 100644 --- a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs @@ -9,4 +9,16 @@ fn foo(foo: T) { y = "foo"; } +type U = impl Copy; + +fn bar(bar: Option) { + match bar { + Some((mut x, mut y)) => { + x = 42; + y = "foo"; + } + None => {} + } +} + fn main() {}