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
|
@ -13,7 +13,7 @@ use rustc_middle::ty::layout::{
|
|||
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
|
||||
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_session::Limit;
|
||||
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
|
||||
// late-bound lifetimes are still around and can lead to type
|
||||
// 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
|
||||
// needs layout equality. For performance reason we skip this check when
|
||||
// the types are equal. Equal types *can* have different layouts when
|
||||
|
|
|
@ -319,6 +319,8 @@ where
|
|||
OpaqueCast(ty) => {
|
||||
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())?,
|
||||
Downcast(_, variant) => self.project_downcast(base, variant)?,
|
||||
Deref => self.deref_pointer(&base.to_op(self)?)?.into(),
|
||||
|
|
|
@ -664,6 +664,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
| ProjectionElem::Downcast(..)
|
||||
| ProjectionElem::OpaqueCast(..)
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::Subtype(..)
|
||||
| ProjectionElem::Field(..)
|
||||
| ProjectionElem::Index(_) => {}
|
||||
}
|
||||
|
|
|
@ -306,6 +306,7 @@ where
|
|||
ProjectionElem::Index(index) if in_local(index) => return true,
|
||||
|
||||
ProjectionElem::Deref
|
||||
| ProjectionElem::Subtype(_)
|
||||
| ProjectionElem::Field(_, _)
|
||||
| ProjectionElem::OpaqueCast(_)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
|
|
|
@ -357,7 +357,9 @@ impl<'tcx> Validator<'_, 'tcx> {
|
|||
return Err(Unpromotable);
|
||||
}
|
||||
|
||||
ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {}
|
||||
ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subtype(_)
|
||||
| ProjectionElem::Subslice { .. } => {}
|
||||
|
||||
ProjectionElem::Index(local) => {
|
||||
let mut promotable = false;
|
||||
|
|
|
@ -7,7 +7,7 @@ use rustc_infer::traits::Reveal;
|
|||
use rustc_middle::mir::interpret::Scalar;
|
||||
use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
|
||||
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::storage::always_storage_live_locals;
|
||||
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::relate_types;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
enum EdgeKind {
|
||||
Unwind,
|
||||
|
@ -602,7 +604,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
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);
|
||||
|
@ -1088,6 +1115,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
// LHS and RHS of the assignment must have the same type.
|
||||
let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
|
||||
let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);
|
||||
|
||||
if !self.mir_assign_valid_types(right_ty, left_ty) {
|
||||
self.fail(
|
||||
location,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
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;
|
||||
|
||||
/// 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.
|
||||
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`.
|
||||
///
|
||||
/// 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
|
||||
/// while still computing the final underlying type.
|
||||
pub fn is_subtype<'tcx>(
|
||||
pub fn relate_types<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
variance: Variance,
|
||||
src: Ty<'tcx>,
|
||||
dest: Ty<'tcx>,
|
||||
) -> bool {
|
||||
|
@ -48,7 +54,7 @@ pub fn is_subtype<'tcx>(
|
|||
let cause = ObligationCause::dummy();
|
||||
let src = ocx.normalize(&cause, param_env, src);
|
||||
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(()) => {}
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@ mod type_name;
|
|||
|
||||
pub use self::alignment::{is_disaligned, is_within_packed};
|
||||
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;
|
||||
|
||||
/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue