mv utility methods into separate module

This commit is contained in:
lcnr 2022-11-15 13:42:14 +01:00
parent 45f441a7b4
commit 6aa611a84c
6 changed files with 72 additions and 65 deletions

View File

@ -23,7 +23,7 @@ use super::{
MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, PointerArithmetic, Provenance, MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, PointerArithmetic, Provenance,
Scalar, StackPopJump, Scalar, StackPopJump,
}; };
use crate::transform::validate; use crate::util;
pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> { pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
/// Stores the `Machine` instance. /// Stores the `Machine` instance.
@ -355,7 +355,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 validate::is_subtype(tcx, param_env, src.ty, dest.ty) { if util::is_subtype(tcx, param_env, 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

View File

@ -2,8 +2,6 @@
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_index::bit_set::BitSet; use rustc_index::bit_set::BitSet;
use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo; use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::visit::{PlaceContext, Visitor};
@ -18,7 +16,6 @@ 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};
use rustc_target::abi::{Size, VariantIdx}; use rustc_target::abi::{Size, VariantIdx};
use rustc_trait_selection::traits::ObligationCtxt;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
enum EdgeKind { enum EdgeKind {
@ -71,55 +68,6 @@ impl<'tcx> MirPass<'tcx> for Validator {
} }
} }
/// Returns whether the two types are equal up to subtyping.
///
/// This is used in case we don't know the expected subtyping direction
/// and still want to check whether anything is broken.
pub fn is_equal_up_to_subtyping<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
src: Ty<'tcx>,
dest: Ty<'tcx>,
) -> bool {
// Fast path.
if src == dest {
return true;
}
// Check for subtyping in either direction.
is_subtype(tcx, param_env, src, dest) || is_subtype(tcx, param_env, dest, src)
}
pub fn is_subtype<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
src: Ty<'tcx>,
dest: Ty<'tcx>,
) -> bool {
if src == dest {
return true;
}
let mut builder =
tcx.infer_ctxt().ignoring_regions().with_opaque_type_inference(DefiningAnchor::Bubble);
let infcx = builder.build();
let ocx = ObligationCtxt::new(&infcx);
let cause = ObligationCause::dummy();
let src = ocx.normalize(cause.clone(), param_env, src);
let dest = ocx.normalize(cause.clone(), param_env, dest);
let Ok(infer_ok) = infcx.at(&cause, param_env).sub(src, dest) else {
return false;
};
let () = ocx.register_infer_ok_obligations(infer_ok);
let errors = ocx.select_all_or_error();
// With `Reveal::All`, opaque types get normalized away, with `Reveal::UserFacing`
// we would get unification errors because we're unable to look into opaque types,
// even if they're constrained in our current function.
//
// It seems very unlikely that this hides any bugs.
let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
errors.is_empty()
}
struct TypeChecker<'a, 'tcx> { struct TypeChecker<'a, 'tcx> {
when: &'a str, when: &'a str,
body: &'a Body<'tcx>, body: &'a Body<'tcx>,
@ -195,7 +143,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
return true; return true;
} }
is_subtype(self.tcx, self.param_env, src, dest) crate::util::is_subtype(self.tcx, self.param_env, src, dest)
} }
} }

View File

@ -0,0 +1,60 @@
//! Routines to check for relations between fully inferred types.
//!
//! FIXME: Move this to a more general place. The utility of this extends to
//! other areas of the compiler as well.
use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_trait_selection::traits::ObligationCtxt;
/// Returns whether the two types are equal up to subtyping.
///
/// This is used in case we don't know the expected subtyping direction
/// and still want to check whether anything is broken.
pub fn is_equal_up_to_subtyping<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
src: Ty<'tcx>,
dest: Ty<'tcx>,
) -> bool {
// Fast path.
if src == dest {
return true;
}
// Check for subtyping in either direction.
is_subtype(tcx, param_env, src, dest) || is_subtype(tcx, param_env, dest, src)
}
/// Returns whether `src` is a subtype of `dest`, i.e. `src <: dest`.
pub fn is_subtype<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
src: Ty<'tcx>,
dest: Ty<'tcx>,
) -> bool {
if src == dest {
return true;
}
let mut builder =
tcx.infer_ctxt().ignoring_regions().with_opaque_type_inference(DefiningAnchor::Bubble);
let infcx = builder.build();
let ocx = ObligationCtxt::new(&infcx);
let cause = ObligationCause::dummy();
let src = ocx.normalize(cause.clone(), param_env, src);
let dest = ocx.normalize(cause.clone(), param_env, dest);
let Ok(infer_ok) = infcx.at(&cause, param_env).sub(src, dest) else {
return false;
};
let () = ocx.register_infer_ok_obligations(infer_ok);
let errors = ocx.select_all_or_error();
// With `Reveal::All`, opaque types get normalized away, with `Reveal::UserFacing`
// we would get unification errors because we're unable to look into opaque types,
// even if they're constrained in our current function.
//
// It seems very unlikely that this hides any bugs.
let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
errors.is_empty()
}

View File

@ -2,6 +2,7 @@ pub mod aggregate;
mod alignment; mod alignment;
mod call_kind; mod call_kind;
pub mod collect_writes; pub mod collect_writes;
mod compare_types;
mod find_self_call; mod find_self_call;
mod might_permit_raw_init; mod might_permit_raw_init;
mod type_name; mod type_name;
@ -9,6 +10,7 @@ mod type_name;
pub use self::aggregate::expand_aggregate; pub use self::aggregate::expand_aggregate;
pub use self::alignment::is_disaligned; pub use self::alignment::is_disaligned;
pub use self::call_kind::{call_kind, CallDesugaringKind, CallKind}; pub use self::call_kind::{call_kind, CallDesugaringKind, CallKind};
pub use self::compare_types::{is_equal_up_to_subtyping, is_subtype};
pub use self::find_self_call::find_self_call; pub use self::find_self_call::find_self_call;
pub use self::might_permit_raw_init::might_permit_raw_init; pub use self::might_permit_raw_init::might_permit_raw_init;
pub use self::type_name::type_name; pub use self::type_name::type_name;

View File

@ -557,11 +557,8 @@ where
debug!(?self.ambient_variance); debug!(?self.ambient_variance);
// In a bivariant context this always succeeds. // In a bivariant context this always succeeds.
let r = if self.ambient_variance == ty::Variance::Bivariant { let r =
a if self.ambient_variance == ty::Variance::Bivariant { a } else { self.relate(a, b)? };
} else {
self.relate(a, b)?
};
self.ambient_variance = old_ambient_variance; self.ambient_variance = old_ambient_variance;

View File

@ -14,7 +14,7 @@ use rustc_target::abi::VariantIdx;
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
use crate::simplify::{remove_dead_blocks, CfgSimplifier}; use crate::simplify::{remove_dead_blocks, CfgSimplifier};
use crate::validate; use crate::util;
use crate::MirPass; use crate::MirPass;
use std::iter; use std::iter;
use std::ops::{Range, RangeFrom}; use std::ops::{Range, RangeFrom};
@ -180,7 +180,7 @@ impl<'tcx> Inliner<'tcx> {
let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() }; let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() };
let destination_ty = destination.ty(&caller_body.local_decls, self.tcx).ty; let destination_ty = destination.ty(&caller_body.local_decls, self.tcx).ty;
let output_type = callee_body.return_ty(); let output_type = callee_body.return_ty();
if !validate::is_subtype(self.tcx, self.param_env, output_type, destination_ty) { if !util::is_subtype(self.tcx, self.param_env, 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");
} }
@ -200,7 +200,7 @@ impl<'tcx> Inliner<'tcx> {
arg_tuple_tys.iter().zip(callee_body.args_iter().skip(skipped_args)) arg_tuple_tys.iter().zip(callee_body.args_iter().skip(skipped_args))
{ {
let input_type = callee_body.local_decls[input].ty; let input_type = callee_body.local_decls[input].ty;
if !validate::is_subtype(self.tcx, self.param_env, input_type, arg_ty) { if !util::is_subtype(self.tcx, self.param_env, 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");
} }
@ -209,7 +209,7 @@ 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 !validate::is_subtype(self.tcx, self.param_env, input_type, arg_ty) { if !util::is_subtype(self.tcx, self.param_env, 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");
} }
@ -847,7 +847,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
let parent = Place { local, projection: self.tcx.intern_place_elems(proj_base) }; let parent = Place { local, projection: self.tcx.intern_place_elems(proj_base) };
let parent_ty = parent.ty(&self.callee_body.local_decls, self.tcx); let parent_ty = parent.ty(&self.callee_body.local_decls, self.tcx);
let check_equal = |this: &mut Self, f_ty| { let check_equal = |this: &mut Self, f_ty| {
if !validate::is_equal_up_to_subtyping(this.tcx, this.param_env, ty, f_ty) { if !util::is_equal_up_to_subtyping(this.tcx, this.param_env, ty, f_ty) {
trace!(?ty, ?f_ty); trace!(?ty, ?f_ty);
this.validation = Err("failed to normalize projection type"); this.validation = Err("failed to normalize projection type");
return; return;