Auto merge of #115025 - ouz-a:ouz_testing, r=lcnr
Make subtyping explicit in MIR This adds new mir-opt that pushes new `ProjectionElem` called `ProjectionElem::Subtype(T)` to `Rvalue` of a subtyped assignment so we can unsoundness issues like https://github.com/rust-lang/rust/issues/107205 Addresses https://github.com/rust-lang/rust/issues/112651 r? `@lcnr`
This commit is contained in:
commit
eb0f3ed59c
34 changed files with 282 additions and 65 deletions
|
@ -2828,6 +2828,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
}
|
}
|
||||||
ProjectionElem::ConstantIndex { .. }
|
ProjectionElem::ConstantIndex { .. }
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
|
| ProjectionElem::Subtype(_)
|
||||||
| ProjectionElem::Index(_) => kind,
|
| ProjectionElem::Index(_) => kind,
|
||||||
},
|
},
|
||||||
place_ty.projection_ty(tcx, elem),
|
place_ty.projection_ty(tcx, elem),
|
||||||
|
|
|
@ -242,6 +242,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
ProjectionElem::Downcast(..) if opt.including_downcast => return None,
|
ProjectionElem::Downcast(..) if opt.including_downcast => return None,
|
||||||
ProjectionElem::Downcast(..) => (),
|
ProjectionElem::Downcast(..) => (),
|
||||||
ProjectionElem::OpaqueCast(..) => (),
|
ProjectionElem::OpaqueCast(..) => (),
|
||||||
|
ProjectionElem::Subtype(..) => (),
|
||||||
ProjectionElem::Field(field, _ty) => {
|
ProjectionElem::Field(field, _ty) => {
|
||||||
// FIXME(project-rfc_2229#36): print capture precisely here.
|
// FIXME(project-rfc_2229#36): print capture precisely here.
|
||||||
if let Some(field) = self.is_upvar_field_projection(PlaceRef {
|
if let Some(field) = self.is_upvar_field_projection(PlaceRef {
|
||||||
|
@ -322,7 +323,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
|
PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
|
||||||
}
|
}
|
||||||
ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
|
ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
|
||||||
ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(*ty),
|
ProjectionElem::Subtype(ty) | ProjectionElem::OpaqueCast(ty) => {
|
||||||
|
PlaceTy::from_ty(*ty)
|
||||||
|
}
|
||||||
ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
|
ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -159,6 +159,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||||
[
|
[
|
||||||
..,
|
..,
|
||||||
ProjectionElem::Index(_)
|
ProjectionElem::Index(_)
|
||||||
|
| ProjectionElem::Subtype(_)
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
| ProjectionElem::OpaqueCast { .. }
|
| ProjectionElem::OpaqueCast { .. }
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
|
|
|
@ -1803,6 +1803,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
for (place_base, elem) in place.iter_projections().rev() {
|
for (place_base, elem) in place.iter_projections().rev() {
|
||||||
match elem {
|
match elem {
|
||||||
ProjectionElem::Index(_/*operand*/) |
|
ProjectionElem::Index(_/*operand*/) |
|
||||||
|
ProjectionElem::Subtype(_) |
|
||||||
ProjectionElem::OpaqueCast(_) |
|
ProjectionElem::OpaqueCast(_) |
|
||||||
ProjectionElem::ConstantIndex { .. } |
|
ProjectionElem::ConstantIndex { .. } |
|
||||||
// assigning to P[i] requires P to be valid.
|
// assigning to P[i] requires P to be valid.
|
||||||
|
@ -2191,6 +2192,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
| ProjectionElem::Index(..)
|
| ProjectionElem::Index(..)
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
|
| ProjectionElem::Subtype(..)
|
||||||
| ProjectionElem::OpaqueCast { .. }
|
| ProjectionElem::OpaqueCast { .. }
|
||||||
| ProjectionElem::Downcast(..) => {
|
| ProjectionElem::Downcast(..) => {
|
||||||
let upvar_field_projection = self.is_upvar_field_projection(place);
|
let upvar_field_projection = self.is_upvar_field_projection(place);
|
||||||
|
|
|
@ -249,6 +249,7 @@ fn place_components_conflict<'tcx>(
|
||||||
| (ProjectionElem::ConstantIndex { .. }, _, _)
|
| (ProjectionElem::ConstantIndex { .. }, _, _)
|
||||||
| (ProjectionElem::Subslice { .. }, _, _)
|
| (ProjectionElem::Subslice { .. }, _, _)
|
||||||
| (ProjectionElem::OpaqueCast { .. }, _, _)
|
| (ProjectionElem::OpaqueCast { .. }, _, _)
|
||||||
|
| (ProjectionElem::Subtype(_), _, _)
|
||||||
| (ProjectionElem::Downcast { .. }, _, _) => {
|
| (ProjectionElem::Downcast { .. }, _, _) => {
|
||||||
// Recursive case. This can still be disjoint on a
|
// Recursive case. This can still be disjoint on a
|
||||||
// further iteration if this a shallow access and
|
// further iteration if this a shallow access and
|
||||||
|
@ -508,6 +509,7 @@ fn place_projection_conflict<'tcx>(
|
||||||
| ProjectionElem::Field(..)
|
| ProjectionElem::Field(..)
|
||||||
| ProjectionElem::Index(..)
|
| ProjectionElem::Index(..)
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
|
| ProjectionElem::Subtype(_)
|
||||||
| ProjectionElem::OpaqueCast { .. }
|
| ProjectionElem::OpaqueCast { .. }
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
| ProjectionElem::Downcast(..),
|
| ProjectionElem::Downcast(..),
|
||||||
|
|
|
@ -89,6 +89,9 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
|
||||||
cursor = cursor_base;
|
cursor = cursor_base;
|
||||||
continue 'cursor;
|
continue 'cursor;
|
||||||
}
|
}
|
||||||
|
ProjectionElem::Subtype(..) => {
|
||||||
|
panic!("Subtype projection is not allowed before borrow check")
|
||||||
|
}
|
||||||
ProjectionElem::Deref => {
|
ProjectionElem::Deref => {
|
||||||
// (handled below)
|
// (handled below)
|
||||||
}
|
}
|
||||||
|
|
|
@ -716,6 +716,9 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||||
}
|
}
|
||||||
PlaceTy::from_ty(fty)
|
PlaceTy::from_ty(fty)
|
||||||
}
|
}
|
||||||
|
ProjectionElem::Subtype(_) => {
|
||||||
|
bug!("ProjectionElem::Subtype shouldn't exist in borrowck")
|
||||||
|
}
|
||||||
ProjectionElem::OpaqueCast(ty) => {
|
ProjectionElem::OpaqueCast(ty) => {
|
||||||
let ty = self.sanitize_type(place, ty);
|
let ty = self.sanitize_type(place, ty);
|
||||||
let ty = self.cx.normalize(ty, location);
|
let ty = self.cx.normalize(ty, location);
|
||||||
|
@ -2563,6 +2566,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
| ProjectionElem::Subslice { .. } => {
|
| ProjectionElem::Subslice { .. } => {
|
||||||
// other field access
|
// other field access
|
||||||
}
|
}
|
||||||
|
ProjectionElem::Subtype(_) => {
|
||||||
|
bug!("ProjectionElem::Subtype shouldn't exist in borrowck")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -876,6 +876,7 @@ pub(crate) fn codegen_place<'tcx>(
|
||||||
cplace = cplace.place_deref(fx);
|
cplace = cplace.place_deref(fx);
|
||||||
}
|
}
|
||||||
PlaceElem::OpaqueCast(ty) => bug!("encountered OpaqueCast({ty}) in codegen"),
|
PlaceElem::OpaqueCast(ty) => bug!("encountered OpaqueCast({ty}) in codegen"),
|
||||||
|
PlaceElem::Subtype(ty) => cplace = cplace.place_transmute_type(fx, fx.monomorphize(ty)),
|
||||||
PlaceElem::Field(field, _ty) => {
|
PlaceElem::Field(field, _ty) => {
|
||||||
cplace = cplace.place_field(fx, field);
|
cplace = cplace.place_field(fx, field);
|
||||||
}
|
}
|
||||||
|
|
|
@ -674,6 +674,16 @@ impl<'tcx> CPlace<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used for `ProjectionElem::Subtype`, `ty` has to be monomorphized before
|
||||||
|
/// passed on.
|
||||||
|
pub(crate) fn place_transmute_type(
|
||||||
|
self,
|
||||||
|
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||||
|
ty: Ty<'tcx>,
|
||||||
|
) -> CPlace<'tcx> {
|
||||||
|
CPlace { inner: self.inner, layout: fx.layout_of(ty) }
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn place_field(
|
pub(crate) fn place_field(
|
||||||
self,
|
self,
|
||||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||||
|
|
|
@ -466,6 +466,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
mir::ProjectionElem::OpaqueCast(ty) => {
|
mir::ProjectionElem::OpaqueCast(ty) => {
|
||||||
bug!("encountered OpaqueCast({ty}) in codegen")
|
bug!("encountered OpaqueCast({ty}) in codegen")
|
||||||
}
|
}
|
||||||
|
mir::ProjectionElem::Subtype(ty) => cg_base.project_type(bx, self.monomorphize(ty)),
|
||||||
mir::ProjectionElem::Index(index) => {
|
mir::ProjectionElem::Index(index) => {
|
||||||
let index = &mir::Operand::Copy(mir::Place::from(index));
|
let index = &mir::Operand::Copy(mir::Place::from(index));
|
||||||
let index = self.codegen_operand(bx, index);
|
let index = self.codegen_operand(bx, index);
|
||||||
|
|
|
@ -13,7 +13,7 @@ use rustc_middle::ty::layout::{
|
||||||
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
|
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
|
||||||
TyAndLayout,
|
TyAndLayout,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::{self, GenericArgsRef, ParamEnv, Ty, TyCtxt, TypeFoldable};
|
use rustc_middle::ty::{self, GenericArgsRef, ParamEnv, Ty, TyCtxt, TypeFoldable, Variance};
|
||||||
use rustc_mir_dataflow::storage::always_storage_live_locals;
|
use rustc_mir_dataflow::storage::always_storage_live_locals;
|
||||||
use rustc_session::Limit;
|
use rustc_session::Limit;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
@ -384,7 +384,7 @@ pub(super) fn mir_assign_valid_types<'tcx>(
|
||||||
// all normal lifetimes are erased, higher-ranked types with their
|
// all normal lifetimes are erased, higher-ranked types with their
|
||||||
// late-bound lifetimes are still around and can lead to type
|
// late-bound lifetimes are still around and can lead to type
|
||||||
// differences.
|
// differences.
|
||||||
if util::is_subtype(tcx, param_env, src.ty, dest.ty) {
|
if util::relate_types(tcx, param_env, Variance::Covariant, src.ty, dest.ty) {
|
||||||
// Make sure the layout is equal, too -- just to be safe. Miri really
|
// Make sure the layout is equal, too -- just to be safe. Miri really
|
||||||
// needs layout equality. For performance reason we skip this check when
|
// needs layout equality. For performance reason we skip this check when
|
||||||
// the types are equal. Equal types *can* have different layouts when
|
// the types are equal. Equal types *can* have different layouts when
|
||||||
|
|
|
@ -319,6 +319,8 @@ where
|
||||||
OpaqueCast(ty) => {
|
OpaqueCast(ty) => {
|
||||||
span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck")
|
span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck")
|
||||||
}
|
}
|
||||||
|
// We don't want anything happening here, this is here as a dummy.
|
||||||
|
Subtype(_) => base.transmute(base.layout(), self)?,
|
||||||
Field(field, _) => self.project_field(base, field.index())?,
|
Field(field, _) => self.project_field(base, field.index())?,
|
||||||
Downcast(_, variant) => self.project_downcast(base, variant)?,
|
Downcast(_, variant) => self.project_downcast(base, variant)?,
|
||||||
Deref => self.deref_pointer(&base.to_op(self)?)?.into(),
|
Deref => self.deref_pointer(&base.to_op(self)?)?.into(),
|
||||||
|
|
|
@ -664,6 +664,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||||
| ProjectionElem::Downcast(..)
|
| ProjectionElem::Downcast(..)
|
||||||
| ProjectionElem::OpaqueCast(..)
|
| ProjectionElem::OpaqueCast(..)
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
|
| ProjectionElem::Subtype(..)
|
||||||
| ProjectionElem::Field(..)
|
| ProjectionElem::Field(..)
|
||||||
| ProjectionElem::Index(_) => {}
|
| ProjectionElem::Index(_) => {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,6 +306,7 @@ where
|
||||||
ProjectionElem::Index(index) if in_local(index) => return true,
|
ProjectionElem::Index(index) if in_local(index) => return true,
|
||||||
|
|
||||||
ProjectionElem::Deref
|
ProjectionElem::Deref
|
||||||
|
| ProjectionElem::Subtype(_)
|
||||||
| ProjectionElem::Field(_, _)
|
| ProjectionElem::Field(_, _)
|
||||||
| ProjectionElem::OpaqueCast(_)
|
| ProjectionElem::OpaqueCast(_)
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
|
|
|
@ -357,7 +357,9 @@ impl<'tcx> Validator<'_, 'tcx> {
|
||||||
return Err(Unpromotable);
|
return Err(Unpromotable);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {}
|
ProjectionElem::ConstantIndex { .. }
|
||||||
|
| ProjectionElem::Subtype(_)
|
||||||
|
| ProjectionElem::Subslice { .. } => {}
|
||||||
|
|
||||||
ProjectionElem::Index(local) => {
|
ProjectionElem::Index(local) => {
|
||||||
let mut promotable = false;
|
let mut promotable = false;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use rustc_infer::traits::Reveal;
|
||||||
use rustc_middle::mir::interpret::Scalar;
|
use rustc_middle::mir::interpret::Scalar;
|
||||||
use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
|
use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
|
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt, Variance};
|
||||||
use rustc_mir_dataflow::impls::MaybeStorageLive;
|
use rustc_mir_dataflow::impls::MaybeStorageLive;
|
||||||
use rustc_mir_dataflow::storage::always_storage_live_locals;
|
use rustc_mir_dataflow::storage::always_storage_live_locals;
|
||||||
use rustc_mir_dataflow::{Analysis, ResultsCursor};
|
use rustc_mir_dataflow::{Analysis, ResultsCursor};
|
||||||
|
@ -16,6 +16,8 @@ use rustc_target::spec::abi::Abi;
|
||||||
|
|
||||||
use crate::util::is_within_packed;
|
use crate::util::is_within_packed;
|
||||||
|
|
||||||
|
use crate::util::relate_types;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
enum EdgeKind {
|
enum EdgeKind {
|
||||||
Unwind,
|
Unwind,
|
||||||
|
@ -602,7 +604,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::util::is_subtype(self.tcx, self.param_env, src, dest)
|
// After borrowck subtyping should be fully explicit via
|
||||||
|
// `Subtype` projections.
|
||||||
|
let variance = if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||||
|
Variance::Invariant
|
||||||
|
} else {
|
||||||
|
Variance::Covariant
|
||||||
|
};
|
||||||
|
|
||||||
|
crate::util::relate_types(self.tcx, self.param_env, variance, src, dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,6 +763,23 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ProjectionElem::Subtype(ty) => {
|
||||||
|
if !relate_types(
|
||||||
|
self.tcx,
|
||||||
|
self.param_env,
|
||||||
|
Variance::Covariant,
|
||||||
|
ty,
|
||||||
|
place_ref.ty(&self.body.local_decls, self.tcx).ty,
|
||||||
|
) {
|
||||||
|
self.fail(
|
||||||
|
location,
|
||||||
|
format!(
|
||||||
|
"Failed subtyping {ty:#?} and {:#?}",
|
||||||
|
place_ref.ty(&self.body.local_decls, self.tcx).ty
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.super_projection_elem(place_ref, elem, context, location);
|
self.super_projection_elem(place_ref, elem, context, location);
|
||||||
|
@ -1088,6 +1115,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
// LHS and RHS of the assignment must have the same type.
|
// LHS and RHS of the assignment must have the same type.
|
||||||
let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
|
let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
|
||||||
let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);
|
let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);
|
||||||
|
|
||||||
if !self.mir_assign_valid_types(right_ty, left_ty) {
|
if !self.mir_assign_valid_types(right_ty, left_ty) {
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_middle::traits::{DefiningAnchor, ObligationCause};
|
use rustc_middle::traits::{DefiningAnchor, ObligationCause};
|
||||||
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt, Variance};
|
||||||
use rustc_trait_selection::traits::ObligationCtxt;
|
use rustc_trait_selection::traits::ObligationCtxt;
|
||||||
|
|
||||||
/// Returns whether the two types are equal up to subtyping.
|
/// Returns whether the two types are equal up to subtyping.
|
||||||
|
@ -24,16 +24,22 @@ pub fn is_equal_up_to_subtyping<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for subtyping in either direction.
|
// Check for subtyping in either direction.
|
||||||
is_subtype(tcx, param_env, src, dest) || is_subtype(tcx, param_env, dest, src)
|
relate_types(tcx, param_env, Variance::Covariant, src, dest)
|
||||||
|
|| relate_types(tcx, param_env, Variance::Covariant, dest, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether `src` is a subtype of `dest`, i.e. `src <: dest`.
|
/// Returns whether `src` is a subtype of `dest`, i.e. `src <: dest`.
|
||||||
///
|
///
|
||||||
|
/// When validating assignments, the variance should be `Covariant`. When checking
|
||||||
|
/// during `MirPhase` >= `MirPhase::Runtime(RuntimePhase::Initial)` variance should be `Invariant`
|
||||||
|
/// because we want to check for type equality.
|
||||||
|
///
|
||||||
/// This mostly ignores opaque types as it can be used in constraining contexts
|
/// This mostly ignores opaque types as it can be used in constraining contexts
|
||||||
/// while still computing the final underlying type.
|
/// while still computing the final underlying type.
|
||||||
pub fn is_subtype<'tcx>(
|
pub fn relate_types<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
param_env: ParamEnv<'tcx>,
|
param_env: ParamEnv<'tcx>,
|
||||||
|
variance: Variance,
|
||||||
src: Ty<'tcx>,
|
src: Ty<'tcx>,
|
||||||
dest: Ty<'tcx>,
|
dest: Ty<'tcx>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
@ -48,7 +54,7 @@ pub fn is_subtype<'tcx>(
|
||||||
let cause = ObligationCause::dummy();
|
let cause = ObligationCause::dummy();
|
||||||
let src = ocx.normalize(&cause, param_env, src);
|
let src = ocx.normalize(&cause, param_env, src);
|
||||||
let dest = ocx.normalize(&cause, param_env, dest);
|
let dest = ocx.normalize(&cause, param_env, dest);
|
||||||
match ocx.sub(&cause, param_env, src, dest) {
|
match ocx.relate(&cause, param_env, variance, src, dest) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@ mod type_name;
|
||||||
|
|
||||||
pub use self::alignment::{is_disaligned, is_within_packed};
|
pub use self::alignment::{is_disaligned, is_within_packed};
|
||||||
pub use self::check_validity_requirement::check_validity_requirement;
|
pub use self::check_validity_requirement::check_validity_requirement;
|
||||||
pub use self::compare_types::{is_equal_up_to_subtyping, is_subtype};
|
pub use self::compare_types::{is_equal_up_to_subtyping, relate_types};
|
||||||
pub use self::type_name::type_name;
|
pub use self::type_name::type_name;
|
||||||
|
|
||||||
/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
|
/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
|
||||||
|
|
|
@ -1103,6 +1103,7 @@ fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
|
||||||
for &elem in projection.iter().rev() {
|
for &elem in projection.iter().rev() {
|
||||||
match elem {
|
match elem {
|
||||||
ProjectionElem::OpaqueCast(_)
|
ProjectionElem::OpaqueCast(_)
|
||||||
|
| ProjectionElem::Subtype(_)
|
||||||
| ProjectionElem::Downcast(_, _)
|
| ProjectionElem::Downcast(_, _)
|
||||||
| ProjectionElem::Field(_, _) => {
|
| ProjectionElem::Field(_, _) => {
|
||||||
write!(fmt, "(").unwrap();
|
write!(fmt, "(").unwrap();
|
||||||
|
@ -1125,6 +1126,9 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
|
||||||
ProjectionElem::OpaqueCast(ty) => {
|
ProjectionElem::OpaqueCast(ty) => {
|
||||||
write!(fmt, " as {ty})")?;
|
write!(fmt, " as {ty})")?;
|
||||||
}
|
}
|
||||||
|
ProjectionElem::Subtype(ty) => {
|
||||||
|
write!(fmt, " as subtype {ty})")?;
|
||||||
|
}
|
||||||
ProjectionElem::Downcast(Some(name), _index) => {
|
ProjectionElem::Downcast(Some(name), _index) => {
|
||||||
write!(fmt, " as {name})")?;
|
write!(fmt, " as {name})")?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ impl<V, T> ProjectionElem<V, T> {
|
||||||
Self::Field(_, _)
|
Self::Field(_, _)
|
||||||
| Self::Index(_)
|
| Self::Index(_)
|
||||||
| Self::OpaqueCast(_)
|
| Self::OpaqueCast(_)
|
||||||
|
| Self::Subtype(_)
|
||||||
| Self::ConstantIndex { .. }
|
| Self::ConstantIndex { .. }
|
||||||
| Self::Subslice { .. }
|
| Self::Subslice { .. }
|
||||||
| Self::Downcast(_, _) => false,
|
| Self::Downcast(_, _) => false,
|
||||||
|
@ -70,6 +71,7 @@ impl<V, T> ProjectionElem<V, T> {
|
||||||
Self::Deref | Self::Index(_) => false,
|
Self::Deref | Self::Index(_) => false,
|
||||||
Self::Field(_, _)
|
Self::Field(_, _)
|
||||||
| Self::OpaqueCast(_)
|
| Self::OpaqueCast(_)
|
||||||
|
| Self::Subtype(_)
|
||||||
| Self::ConstantIndex { .. }
|
| Self::ConstantIndex { .. }
|
||||||
| Self::Subslice { .. }
|
| Self::Subslice { .. }
|
||||||
| Self::Downcast(_, _) => true,
|
| Self::Downcast(_, _) => true,
|
||||||
|
@ -95,6 +97,7 @@ impl<V, T> ProjectionElem<V, T> {
|
||||||
| Self::Field(_, _) => true,
|
| Self::Field(_, _) => true,
|
||||||
Self::ConstantIndex { from_end: true, .. }
|
Self::ConstantIndex { from_end: true, .. }
|
||||||
| Self::Index(_)
|
| Self::Index(_)
|
||||||
|
| Self::Subtype(_)
|
||||||
| Self::OpaqueCast(_)
|
| Self::OpaqueCast(_)
|
||||||
| Self::Subslice { .. } => false,
|
| Self::Subslice { .. } => false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1075,6 +1075,18 @@ pub enum ProjectionElem<V, T> {
|
||||||
/// Like an explicit cast from an opaque type to a concrete type, but without
|
/// Like an explicit cast from an opaque type to a concrete type, but without
|
||||||
/// requiring an intermediate variable.
|
/// requiring an intermediate variable.
|
||||||
OpaqueCast(T),
|
OpaqueCast(T),
|
||||||
|
|
||||||
|
/// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where
|
||||||
|
/// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping
|
||||||
|
/// explicit during optimizations and codegen.
|
||||||
|
///
|
||||||
|
/// This projection doesn't impact the runtime behavior of the program except for potentially changing
|
||||||
|
/// some type metadata of the interpreter or codegen backend.
|
||||||
|
///
|
||||||
|
/// This goal is achieved with mir_transform pass `Subtyper`, which runs right after
|
||||||
|
/// borrowchecker, as we only care about subtyping that can affect trait selection and
|
||||||
|
/// `TypeId`.
|
||||||
|
Subtype(T),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Alias for projections as they appear in places, where the base is a place
|
/// Alias for projections as they appear in places, where the base is a place
|
||||||
|
|
|
@ -69,7 +69,7 @@ impl<'tcx> PlaceTy<'tcx> {
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
elem: &ProjectionElem<V, T>,
|
elem: &ProjectionElem<V, T>,
|
||||||
mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>,
|
mut handle_field: impl FnMut(&Self, FieldIdx, T) -> Ty<'tcx>,
|
||||||
mut handle_opaque_cast: impl FnMut(&Self, T) -> Ty<'tcx>,
|
mut handle_opaque_cast_and_subtype: impl FnMut(&Self, T) -> Ty<'tcx>,
|
||||||
) -> PlaceTy<'tcx>
|
) -> PlaceTy<'tcx>
|
||||||
where
|
where
|
||||||
V: ::std::fmt::Debug,
|
V: ::std::fmt::Debug,
|
||||||
|
@ -110,7 +110,12 @@ impl<'tcx> PlaceTy<'tcx> {
|
||||||
PlaceTy { ty: self.ty, variant_index: Some(index) }
|
PlaceTy { ty: self.ty, variant_index: Some(index) }
|
||||||
}
|
}
|
||||||
ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
|
ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)),
|
||||||
ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast(&self, ty)),
|
ProjectionElem::OpaqueCast(ty) => {
|
||||||
|
PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
|
||||||
|
}
|
||||||
|
ProjectionElem::Subtype(ty) => {
|
||||||
|
PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
|
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
|
||||||
answer
|
answer
|
||||||
|
|
|
@ -1109,6 +1109,11 @@ macro_rules! visit_place_fns {
|
||||||
self.visit_ty(&mut new_ty, TyContext::Location(location));
|
self.visit_ty(&mut new_ty, TyContext::Location(location));
|
||||||
if ty != new_ty { Some(PlaceElem::OpaqueCast(new_ty)) } else { None }
|
if ty != new_ty { Some(PlaceElem::OpaqueCast(new_ty)) } else { None }
|
||||||
}
|
}
|
||||||
|
PlaceElem::Subtype(ty) => {
|
||||||
|
let mut new_ty = ty;
|
||||||
|
self.visit_ty(&mut new_ty, TyContext::Location(location));
|
||||||
|
if ty != new_ty { Some(PlaceElem::Subtype(new_ty)) } else { None }
|
||||||
|
}
|
||||||
PlaceElem::Deref
|
PlaceElem::Deref
|
||||||
| PlaceElem::ConstantIndex { .. }
|
| PlaceElem::ConstantIndex { .. }
|
||||||
| PlaceElem::Subslice { .. }
|
| PlaceElem::Subslice { .. }
|
||||||
|
@ -1175,7 +1180,9 @@ macro_rules! visit_place_fns {
|
||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) {
|
||||||
match elem {
|
match elem {
|
||||||
ProjectionElem::OpaqueCast(ty) | ProjectionElem::Field(_, ty) => {
|
ProjectionElem::OpaqueCast(ty)
|
||||||
|
| ProjectionElem::Subtype(ty)
|
||||||
|
| ProjectionElem::Field(_, ty) => {
|
||||||
self.visit_ty(ty, TyContext::Location(location));
|
self.visit_ty(ty, TyContext::Location(location));
|
||||||
}
|
}
|
||||||
ProjectionElem::Index(local) => {
|
ProjectionElem::Index(local) => {
|
||||||
|
|
|
@ -102,7 +102,7 @@ fn convert_to_hir_projections_and_truncate_for_capture(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// These do not affect anything, they just make sure we know the right type.
|
// These do not affect anything, they just make sure we know the right type.
|
||||||
ProjectionElem::OpaqueCast(_) => continue,
|
ProjectionElem::OpaqueCast(_) | ProjectionElem::Subtype(..) => continue,
|
||||||
ProjectionElem::Index(..)
|
ProjectionElem::Index(..)
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
| ProjectionElem::Subslice { .. } => {
|
| ProjectionElem::Subslice { .. } => {
|
||||||
|
@ -709,6 +709,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
ProjectionElem::Field(..)
|
ProjectionElem::Field(..)
|
||||||
| ProjectionElem::Downcast(..)
|
| ProjectionElem::Downcast(..)
|
||||||
| ProjectionElem::OpaqueCast(..)
|
| ProjectionElem::OpaqueCast(..)
|
||||||
|
| ProjectionElem::Subtype(..)
|
||||||
| ProjectionElem::ConstantIndex { .. }
|
| ProjectionElem::ConstantIndex { .. }
|
||||||
| ProjectionElem::Subslice { .. } => (),
|
| ProjectionElem::Subslice { .. } => (),
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ impl<'tcx> Lift for PlaceElem<'tcx> {
|
||||||
ProjectionElem::ConstantIndex { offset, min_length, from_end }
|
ProjectionElem::ConstantIndex { offset, min_length, from_end }
|
||||||
}
|
}
|
||||||
ProjectionElem::Downcast(a, u) => ProjectionElem::Downcast(a, u),
|
ProjectionElem::Downcast(a, u) => ProjectionElem::Downcast(a, u),
|
||||||
|
ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty.lift()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,10 +227,13 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||||
}
|
}
|
||||||
_ => bug!("Unexpected type {place_ty:#?}"),
|
_ => bug!("Unexpected type {place_ty:#?}"),
|
||||||
},
|
},
|
||||||
// `OpaqueCast` only transmutes the type, so no moves there and
|
// `OpaqueCast`:Only transmutes the type, so no moves there.
|
||||||
// `Downcast` only changes information about a `Place` without moving
|
// `Downcast` :Only changes information about a `Place` without moving.
|
||||||
|
// `Subtype` :Only transmutes the type, so moves.
|
||||||
// So it's safe to skip these.
|
// So it's safe to skip these.
|
||||||
ProjectionElem::OpaqueCast(_) | ProjectionElem::Downcast(_, _) => (),
|
ProjectionElem::OpaqueCast(_)
|
||||||
|
| ProjectionElem::Subtype(_)
|
||||||
|
| ProjectionElem::Downcast(_, _) => (),
|
||||||
}
|
}
|
||||||
if union_path.is_none() {
|
if union_path.is_none() {
|
||||||
// inlined from add_move_path because of a borrowck conflict with the iterator
|
// inlined from add_move_path because of a borrowck conflict with the iterator
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
use crate::MirPass;
|
||||||
|
use rustc_index::IndexVec;
|
||||||
|
use rustc_middle::mir::patch::MirPatch;
|
||||||
|
use rustc_middle::mir::visit::MutVisitor;
|
||||||
|
use rustc_middle::mir::*;
|
||||||
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
|
||||||
|
pub struct Subtyper;
|
||||||
|
|
||||||
|
pub struct SubTypeChecker<'a, 'tcx> {
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
patcher: MirPatch<'tcx>,
|
||||||
|
local_decls: &'a IndexVec<Local, LocalDecl<'tcx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> MutVisitor<'tcx> for SubTypeChecker<'a, 'tcx> {
|
||||||
|
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||||
|
self.tcx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_assign(
|
||||||
|
&mut self,
|
||||||
|
place: &mut Place<'tcx>,
|
||||||
|
rvalue: &mut Rvalue<'tcx>,
|
||||||
|
location: Location,
|
||||||
|
) {
|
||||||
|
let mut place_ty = place.ty(self.local_decls, self.tcx).ty;
|
||||||
|
let mut rval_ty = rvalue.ty(self.local_decls, self.tcx);
|
||||||
|
// Not erasing this causes `Free Regions` errors in validator,
|
||||||
|
// when rval is `ReStatic`.
|
||||||
|
rval_ty = self.tcx.erase_regions_ty(rval_ty);
|
||||||
|
place_ty = self.tcx.erase_regions(place_ty);
|
||||||
|
if place_ty != rval_ty {
|
||||||
|
let temp = self
|
||||||
|
.patcher
|
||||||
|
.new_temp(rval_ty, self.local_decls[place.as_ref().local].source_info.span);
|
||||||
|
let new_place = Place::from(temp);
|
||||||
|
self.patcher.add_assign(location, new_place, rvalue.clone());
|
||||||
|
let subtyped = new_place.project_deeper(&[ProjectionElem::Subtype(place_ty)], self.tcx);
|
||||||
|
*rvalue = Rvalue::Use(Operand::Move(subtyped));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aim here is to do this kind of transformation:
|
||||||
|
//
|
||||||
|
// let place: place_ty = rval;
|
||||||
|
// // gets transformed to
|
||||||
|
// let temp: rval_ty = rval;
|
||||||
|
// let place: place_ty = temp as place_ty;
|
||||||
|
//
|
||||||
|
pub fn subtype_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
|
let patch = MirPatch::new(body);
|
||||||
|
let mut checker = SubTypeChecker { tcx, patcher: patch, local_decls: &body.local_decls };
|
||||||
|
|
||||||
|
for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
|
||||||
|
checker.visit_basic_block_data(bb, data);
|
||||||
|
}
|
||||||
|
checker.patcher.apply(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> MirPass<'tcx> for Subtyper {
|
||||||
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
|
subtype_finder(tcx, body);
|
||||||
|
}
|
||||||
|
}
|
|
@ -306,6 +306,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
||||||
}
|
}
|
||||||
ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index),
|
ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index),
|
||||||
ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty),
|
ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty),
|
||||||
|
ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty),
|
||||||
};
|
};
|
||||||
value = self.insert(Value::Projection(value, proj));
|
value = self.insert(Value::Projection(value, proj));
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,7 +218,13 @@ impl<'tcx> Inliner<'tcx> {
|
||||||
// Normally, this shouldn't be required, but trait normalization failure can create a
|
// Normally, this shouldn't be required, but trait normalization failure can create a
|
||||||
// validation ICE.
|
// validation ICE.
|
||||||
let output_type = callee_body.return_ty();
|
let output_type = callee_body.return_ty();
|
||||||
if !util::is_subtype(self.tcx, self.param_env, output_type, destination_ty) {
|
if !util::relate_types(
|
||||||
|
self.tcx,
|
||||||
|
self.param_env,
|
||||||
|
ty::Variance::Covariant,
|
||||||
|
output_type,
|
||||||
|
destination_ty,
|
||||||
|
) {
|
||||||
trace!(?output_type, ?destination_ty);
|
trace!(?output_type, ?destination_ty);
|
||||||
return Err("failed to normalize return type");
|
return Err("failed to normalize return type");
|
||||||
}
|
}
|
||||||
|
@ -248,7 +254,13 @@ impl<'tcx> Inliner<'tcx> {
|
||||||
self_arg_ty.into_iter().chain(arg_tuple_tys).zip(callee_body.args_iter())
|
self_arg_ty.into_iter().chain(arg_tuple_tys).zip(callee_body.args_iter())
|
||||||
{
|
{
|
||||||
let input_type = callee_body.local_decls[input].ty;
|
let input_type = callee_body.local_decls[input].ty;
|
||||||
if !util::is_subtype(self.tcx, self.param_env, input_type, arg_ty) {
|
if !util::relate_types(
|
||||||
|
self.tcx,
|
||||||
|
self.param_env,
|
||||||
|
ty::Variance::Covariant,
|
||||||
|
input_type,
|
||||||
|
arg_ty,
|
||||||
|
) {
|
||||||
trace!(?arg_ty, ?input_type);
|
trace!(?arg_ty, ?input_type);
|
||||||
return Err("failed to normalize tuple argument type");
|
return Err("failed to normalize tuple argument type");
|
||||||
}
|
}
|
||||||
|
@ -257,7 +269,13 @@ impl<'tcx> Inliner<'tcx> {
|
||||||
for (arg, input) in args.iter().zip(callee_body.args_iter()) {
|
for (arg, input) in args.iter().zip(callee_body.args_iter()) {
|
||||||
let input_type = callee_body.local_decls[input].ty;
|
let input_type = callee_body.local_decls[input].ty;
|
||||||
let arg_ty = arg.ty(&caller_body.local_decls, self.tcx);
|
let arg_ty = arg.ty(&caller_body.local_decls, self.tcx);
|
||||||
if !util::is_subtype(self.tcx, self.param_env, input_type, arg_ty) {
|
if !util::relate_types(
|
||||||
|
self.tcx,
|
||||||
|
self.param_env,
|
||||||
|
ty::Variance::Covariant,
|
||||||
|
input_type,
|
||||||
|
arg_ty,
|
||||||
|
) {
|
||||||
trace!(?arg_ty, ?input_type);
|
trace!(?arg_ty, ?input_type);
|
||||||
return Err("failed to normalize argument type");
|
return Err("failed to normalize argument type");
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ mod check_packed_ref;
|
||||||
pub mod check_unsafety;
|
pub mod check_unsafety;
|
||||||
mod remove_place_mention;
|
mod remove_place_mention;
|
||||||
// This pass is public to allow external drivers to perform MIR cleanup
|
// This pass is public to allow external drivers to perform MIR cleanup
|
||||||
|
mod add_subtyping_projections;
|
||||||
pub mod cleanup_post_borrowck;
|
pub mod cleanup_post_borrowck;
|
||||||
mod const_debuginfo;
|
mod const_debuginfo;
|
||||||
mod const_goto;
|
mod const_goto;
|
||||||
|
@ -466,6 +467,7 @@ pub fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'
|
||||||
/// After this series of passes, no lifetime analysis based on borrowing can be done.
|
/// After this series of passes, no lifetime analysis based on borrowing can be done.
|
||||||
fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
let passes: &[&dyn MirPass<'tcx>] = &[
|
let passes: &[&dyn MirPass<'tcx>] = &[
|
||||||
|
&add_subtyping_projections::Subtyper,
|
||||||
&cleanup_post_borrowck::CleanupPostBorrowck,
|
&cleanup_post_borrowck::CleanupPostBorrowck,
|
||||||
&remove_noop_landing_pads::RemoveNoopLandingPads,
|
&remove_noop_landing_pads::RemoveNoopLandingPads,
|
||||||
&simplify::SimplifyCfg::EarlyOpt,
|
&simplify::SimplifyCfg::EarlyOpt,
|
||||||
|
|
|
@ -23,6 +23,7 @@ use rustc_middle::traits::query::NoSolution;
|
||||||
use rustc_middle::ty::error::TypeError;
|
use rustc_middle::ty::error::TypeError;
|
||||||
use rustc_middle::ty::ToPredicate;
|
use rustc_middle::ty::ToPredicate;
|
||||||
use rustc_middle::ty::TypeFoldable;
|
use rustc_middle::ty::TypeFoldable;
|
||||||
|
use rustc_middle::ty::Variance;
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
use rustc_session::config::TraitSolver;
|
use rustc_session::config::TraitSolver;
|
||||||
|
|
||||||
|
@ -156,6 +157,20 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
|
||||||
.map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
|
.map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn relate<T: ToTrace<'tcx>>(
|
||||||
|
&self,
|
||||||
|
cause: &ObligationCause<'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
variance: Variance,
|
||||||
|
expected: T,
|
||||||
|
actual: T,
|
||||||
|
) -> Result<(), TypeError<'tcx>> {
|
||||||
|
self.infcx
|
||||||
|
.at(cause, param_env)
|
||||||
|
.relate(DefineOpaqueTypes::Yes, expected, variance, actual)
|
||||||
|
.map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks whether `expected` is a supertype of `actual`: `expected :> actual`.
|
/// Checks whether `expected` is a supertype of `actual`: `expected :> actual`.
|
||||||
pub fn sup<T: ToTrace<'tcx>>(
|
pub fn sup<T: ToTrace<'tcx>>(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -272,6 +272,7 @@ fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &B
|
||||||
| ProjectionElem::Downcast(..)
|
| ProjectionElem::Downcast(..)
|
||||||
| ProjectionElem::Subslice { .. }
|
| ProjectionElem::Subslice { .. }
|
||||||
| ProjectionElem::Deref
|
| ProjectionElem::Deref
|
||||||
|
| ProjectionElem::Subtype(_)
|
||||||
| ProjectionElem::Index(_) => {},
|
| ProjectionElem::Index(_) => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,12 @@
|
||||||
let mut _2: std::pin::Pin<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}>;
|
let mut _2: std::pin::Pin<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}>;
|
||||||
let mut _3: &mut {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
let mut _3: &mut {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
||||||
let mut _4: {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
let mut _4: {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
||||||
+ let mut _5: bool;
|
+ let mut _6: bool;
|
||||||
scope 1 {
|
scope 1 {
|
||||||
debug _r => _1;
|
debug _r => _1;
|
||||||
}
|
}
|
||||||
+ scope 2 (inlined g) {
|
+ scope 2 (inlined g) {
|
||||||
|
+ let mut _5: {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
||||||
+ }
|
+ }
|
||||||
+ scope 3 (inlined Pin::<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}>::new) {
|
+ scope 3 (inlined Pin::<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}>::new) {
|
||||||
+ debug pointer => _3;
|
+ debug pointer => _3;
|
||||||
|
@ -22,10 +23,10 @@
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+ scope 6 (inlined g::{closure#0}) {
|
+ scope 6 (inlined g::{closure#0}) {
|
||||||
+ debug a => _5;
|
+ debug a => _6;
|
||||||
+ let mut _6: &mut {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
+ let mut _7: &mut {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
||||||
+ let mut _7: u32;
|
+ let mut _8: u32;
|
||||||
+ let mut _8: i32;
|
+ let mut _9: i32;
|
||||||
+ }
|
+ }
|
||||||
|
|
||||||
bb0: {
|
bb0: {
|
||||||
|
@ -34,21 +35,24 @@
|
||||||
StorageLive(_3);
|
StorageLive(_3);
|
||||||
StorageLive(_4);
|
StorageLive(_4);
|
||||||
- _4 = g() -> [return: bb1, unwind unreachable];
|
- _4 = g() -> [return: bb1, unwind unreachable];
|
||||||
+ _4 = {generator@$DIR/inline_generator.rs:16:5: 16:8 (#0)};
|
+ StorageLive(_5);
|
||||||
|
+ _5 = {generator@$DIR/inline_generator.rs:16:5: 16:8 (#0)};
|
||||||
|
+ _4 = move (_5 as subtype {generator@$DIR/inline_generator.rs:16:5: 16:8});
|
||||||
|
+ StorageDead(_5);
|
||||||
+ _3 = &mut _4;
|
+ _3 = &mut _4;
|
||||||
+ _2 = Pin::<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}> { pointer: move _3 };
|
+ _2 = Pin::<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}> { pointer: move _3 };
|
||||||
+ StorageDead(_3);
|
+ StorageDead(_3);
|
||||||
+ StorageLive(_5);
|
+ StorageLive(_6);
|
||||||
+ _5 = const false;
|
+ _6 = const false;
|
||||||
+ _6 = (_2.0: &mut {generator@$DIR/inline_generator.rs:16:5: 16:8});
|
+ _7 = (_2.0: &mut {generator@$DIR/inline_generator.rs:16:5: 16:8});
|
||||||
+ _7 = discriminant((*_6));
|
+ _8 = discriminant((*_7));
|
||||||
+ switchInt(move _7) -> [0: bb3, 1: bb7, 3: bb8, otherwise: bb9];
|
+ switchInt(move _8) -> [0: bb3, 1: bb7, 3: bb8, otherwise: bb9];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
- _3 = &mut _4;
|
- _3 = &mut _4;
|
||||||
- _2 = Pin::<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}>::new(move _3) -> [return: bb2, unwind unreachable];
|
- _2 = Pin::<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}>::new(move _3) -> [return: bb2, unwind unreachable];
|
||||||
+ StorageDead(_5);
|
+ StorageDead(_6);
|
||||||
+ StorageDead(_2);
|
+ StorageDead(_2);
|
||||||
+ drop(_4) -> [return: bb2, unwind unreachable];
|
+ drop(_4) -> [return: bb2, unwind unreachable];
|
||||||
}
|
}
|
||||||
|
@ -65,8 +69,8 @@
|
||||||
bb3: {
|
bb3: {
|
||||||
- StorageDead(_2);
|
- StorageDead(_2);
|
||||||
- drop(_4) -> [return: bb4, unwind unreachable];
|
- drop(_4) -> [return: bb4, unwind unreachable];
|
||||||
+ StorageLive(_8);
|
+ StorageLive(_9);
|
||||||
+ switchInt(_5) -> [0: bb4, otherwise: bb5];
|
+ switchInt(_6) -> [0: bb4, otherwise: bb5];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb4: {
|
bb4: {
|
||||||
|
@ -74,18 +78,18 @@
|
||||||
- _0 = const ();
|
- _0 = const ();
|
||||||
- StorageDead(_1);
|
- StorageDead(_1);
|
||||||
- return;
|
- return;
|
||||||
+ _8 = const 13_i32;
|
+ _9 = const 13_i32;
|
||||||
+ goto -> bb6;
|
+ goto -> bb6;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ bb5: {
|
+ bb5: {
|
||||||
+ _8 = const 7_i32;
|
+ _9 = const 7_i32;
|
||||||
+ goto -> bb6;
|
+ goto -> bb6;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ bb6: {
|
+ bb6: {
|
||||||
+ _1 = GeneratorState::<i32, bool>::Yielded(move _8);
|
+ _1 = GeneratorState::<i32, bool>::Yielded(move _9);
|
||||||
+ discriminant((*_6)) = 3;
|
+ discriminant((*_7)) = 3;
|
||||||
+ goto -> bb1;
|
+ goto -> bb1;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
|
@ -94,10 +98,10 @@
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ bb8: {
|
+ bb8: {
|
||||||
+ StorageLive(_8);
|
+ StorageLive(_9);
|
||||||
+ StorageDead(_8);
|
+ StorageDead(_9);
|
||||||
+ _1 = GeneratorState::<i32, bool>::Complete(_5);
|
+ _1 = GeneratorState::<i32, bool>::Complete(_6);
|
||||||
+ discriminant((*_6)) = 1;
|
+ discriminant((*_7)) = 1;
|
||||||
+ goto -> bb1;
|
+ goto -> bb1;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
|
|
|
@ -7,11 +7,12 @@
|
||||||
let mut _2: std::pin::Pin<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}>;
|
let mut _2: std::pin::Pin<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}>;
|
||||||
let mut _3: &mut {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
let mut _3: &mut {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
||||||
let mut _4: {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
let mut _4: {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
||||||
+ let mut _5: bool;
|
+ let mut _6: bool;
|
||||||
scope 1 {
|
scope 1 {
|
||||||
debug _r => _1;
|
debug _r => _1;
|
||||||
}
|
}
|
||||||
+ scope 2 (inlined g) {
|
+ scope 2 (inlined g) {
|
||||||
|
+ let mut _5: {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
||||||
+ }
|
+ }
|
||||||
+ scope 3 (inlined Pin::<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}>::new) {
|
+ scope 3 (inlined Pin::<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}>::new) {
|
||||||
+ debug pointer => _3;
|
+ debug pointer => _3;
|
||||||
|
@ -22,10 +23,10 @@
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+ scope 6 (inlined g::{closure#0}) {
|
+ scope 6 (inlined g::{closure#0}) {
|
||||||
+ debug a => _5;
|
+ debug a => _6;
|
||||||
+ let mut _6: &mut {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
+ let mut _7: &mut {generator@$DIR/inline_generator.rs:16:5: 16:8};
|
||||||
+ let mut _7: u32;
|
+ let mut _8: u32;
|
||||||
+ let mut _8: i32;
|
+ let mut _9: i32;
|
||||||
+ }
|
+ }
|
||||||
|
|
||||||
bb0: {
|
bb0: {
|
||||||
|
@ -37,7 +38,10 @@
|
||||||
- }
|
- }
|
||||||
-
|
-
|
||||||
- bb1: {
|
- bb1: {
|
||||||
+ _4 = {generator@$DIR/inline_generator.rs:16:5: 16:8 (#0)};
|
+ StorageLive(_5);
|
||||||
|
+ _5 = {generator@$DIR/inline_generator.rs:16:5: 16:8 (#0)};
|
||||||
|
+ _4 = move (_5 as subtype {generator@$DIR/inline_generator.rs:16:5: 16:8});
|
||||||
|
+ StorageDead(_5);
|
||||||
_3 = &mut _4;
|
_3 = &mut _4;
|
||||||
- _2 = Pin::<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}>::new(move _3) -> [return: bb2, unwind: bb5];
|
- _2 = Pin::<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}>::new(move _3) -> [return: bb2, unwind: bb5];
|
||||||
- }
|
- }
|
||||||
|
@ -46,16 +50,16 @@
|
||||||
+ _2 = Pin::<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}> { pointer: move _3 };
|
+ _2 = Pin::<&mut {generator@$DIR/inline_generator.rs:16:5: 16:8}> { pointer: move _3 };
|
||||||
StorageDead(_3);
|
StorageDead(_3);
|
||||||
- _1 = <{generator@$DIR/inline_generator.rs:16:5: 16:8} as Generator<bool>>::resume(move _2, const false) -> [return: bb3, unwind: bb5];
|
- _1 = <{generator@$DIR/inline_generator.rs:16:5: 16:8} as Generator<bool>>::resume(move _2, const false) -> [return: bb3, unwind: bb5];
|
||||||
+ StorageLive(_5);
|
+ StorageLive(_6);
|
||||||
+ _5 = const false;
|
+ _6 = const false;
|
||||||
+ _6 = (_2.0: &mut {generator@$DIR/inline_generator.rs:16:5: 16:8});
|
+ _7 = (_2.0: &mut {generator@$DIR/inline_generator.rs:16:5: 16:8});
|
||||||
+ _7 = discriminant((*_6));
|
+ _8 = discriminant((*_7));
|
||||||
+ switchInt(move _7) -> [0: bb5, 1: bb9, 3: bb10, otherwise: bb11];
|
+ switchInt(move _8) -> [0: bb5, 1: bb9, 3: bb10, otherwise: bb11];
|
||||||
}
|
}
|
||||||
|
|
||||||
- bb3: {
|
- bb3: {
|
||||||
+ bb1: {
|
+ bb1: {
|
||||||
+ StorageDead(_5);
|
+ StorageDead(_6);
|
||||||
StorageDead(_2);
|
StorageDead(_2);
|
||||||
- drop(_4) -> [return: bb4, unwind: bb6];
|
- drop(_4) -> [return: bb4, unwind: bb6];
|
||||||
+ drop(_4) -> [return: bb2, unwind: bb4];
|
+ drop(_4) -> [return: bb2, unwind: bb4];
|
||||||
|
@ -81,23 +85,23 @@
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ bb5: {
|
+ bb5: {
|
||||||
+ StorageLive(_8);
|
+ StorageLive(_9);
|
||||||
+ switchInt(_5) -> [0: bb6, otherwise: bb7];
|
+ switchInt(_6) -> [0: bb6, otherwise: bb7];
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ bb6: {
|
+ bb6: {
|
||||||
+ _8 = const 13_i32;
|
+ _9 = const 13_i32;
|
||||||
+ goto -> bb8;
|
+ goto -> bb8;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ bb7: {
|
+ bb7: {
|
||||||
+ _8 = const 7_i32;
|
+ _9 = const 7_i32;
|
||||||
+ goto -> bb8;
|
+ goto -> bb8;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ bb8: {
|
+ bb8: {
|
||||||
+ _1 = GeneratorState::<i32, bool>::Yielded(move _8);
|
+ _1 = GeneratorState::<i32, bool>::Yielded(move _9);
|
||||||
+ discriminant((*_6)) = 3;
|
+ discriminant((*_7)) = 3;
|
||||||
+ goto -> bb1;
|
+ goto -> bb1;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
|
@ -106,10 +110,10 @@
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ bb10: {
|
+ bb10: {
|
||||||
+ StorageLive(_8);
|
+ StorageLive(_9);
|
||||||
+ StorageDead(_8);
|
+ StorageDead(_9);
|
||||||
+ _1 = GeneratorState::<i32, bool>::Complete(_5);
|
+ _1 = GeneratorState::<i32, bool>::Complete(_6);
|
||||||
+ discriminant((*_6)) = 1;
|
+ discriminant((*_7)) = 1;
|
||||||
+ goto -> bb1;
|
+ goto -> bb1;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue