diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index b1098c4f980..b6c227638a1 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -589,7 +589,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // Typeck only does a "non-const" check since it operates on HIR and cannot distinguish // which path expressions are getting called on and which path expressions are only used // as function pointers. This is required for correctness. - let infcx = tcx.infer_ctxt().build(body.phase.typing_mode()); + let infcx = tcx.infer_ctxt().build(body.typing_mode(tcx)); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let predicates = tcx.predicates_of(callee).instantiate(tcx, fn_args); diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 270a5880764..961a27ae9ba 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -116,7 +116,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref); - let infcx = tcx.infer_ctxt().build(body.phase.typing_mode()); + let infcx = tcx.infer_ctxt().build(body.typing_mode(tcx)); let mut selcx = SelectionContext::new(&infcx); let implsrc = selcx.select(&obligation); diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index d81368e9fcc..9043bd3e28e 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -3,7 +3,7 @@ use rustc_errors::DiagCtxtHandle; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::at::ToTrace; -use rustc_infer::traits::ObligationCause; +use rustc_infer::traits::{ObligationCause, Reveal}; use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{ @@ -116,6 +116,7 @@ impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> { /// This test should be symmetric, as it is primarily about layout compatibility. pub(super) fn mir_assign_valid_types<'tcx>( tcx: TyCtxt<'tcx>, + typing_mode: TypingMode<'tcx>, param_env: ParamEnv<'tcx>, src: TyAndLayout<'tcx>, dest: TyAndLayout<'tcx>, @@ -124,7 +125,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::relate_types(tcx, param_env, Variance::Covariant, src.ty, dest.ty) { + if util::relate_types(tcx, typing_mode, 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 @@ -144,6 +145,7 @@ pub(super) fn mir_assign_valid_types<'tcx>( #[cfg_attr(not(debug_assertions), inline(always))] pub(super) fn from_known_layout<'tcx>( tcx: TyCtxtAt<'tcx>, + typing_mode: TypingMode<'tcx>, param_env: ParamEnv<'tcx>, known_layout: Option>, compute: impl FnOnce() -> InterpResult<'tcx, TyAndLayout<'tcx>>, @@ -153,7 +155,13 @@ pub(super) fn from_known_layout<'tcx>( Some(known_layout) => { if cfg!(debug_assertions) { let check_layout = compute()?; - if !mir_assign_valid_types(tcx.tcx, param_env, check_layout, known_layout) { + if !mir_assign_valid_types( + tcx.tcx, + typing_mode, + param_env, + check_layout, + known_layout, + ) { span_bug!( tcx.span, "expected type differs from actual type.\nexpected: {}\nactual: {}", @@ -203,6 +211,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + pub fn typing_mode(&self) -> TypingMode<'tcx> { + debug_assert_eq!(self.param_env.reveal(), Reveal::All); + TypingMode::PostAnalysis + } + /// Returns the span of the currently executed statement/terminator. /// This is the span typically used for error reporting. #[inline(always)] @@ -327,7 +340,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return true; } // Slow path: spin up an inference context to check if these traits are sufficiently equal. - let infcx = self.tcx.infer_ctxt().build(TypingMode::from_param_env(self.param_env)); + let infcx = self.tcx.infer_ctxt().build(self.typing_mode()); let ocx = ObligationCtxt::new(&infcx); let cause = ObligationCause::dummy_with_span(self.cur_span()); // equate the two trait refs after normalization diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 43ae98e74b0..a130ae89bcb 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -773,6 +773,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { )?; if !mir_assign_valid_types( *self.tcx, + self.typing_mode(), self.param_env, self.layout_of(normalized_place_ty)?, op.layout, @@ -832,7 +833,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }) }; let layout = - from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty).into())?; + from_known_layout(self.tcx, self.typing_mode(), self.param_env, layout, || { + self.layout_of(ty).into() + })?; let imm = match val_val { mir::ConstValue::Indirect { alloc_id, offset } => { // This is const data, no mutation allowed. diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 139a1db60e0..cc8d1db6cfb 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -540,6 +540,7 @@ where )?; if !mir_assign_valid_types( *self.tcx, + self.typing_mode(), self.param_env, self.layout_of(normalized_place_ty)?, place.layout, @@ -870,8 +871,13 @@ where ) -> InterpResult<'tcx> { // We do NOT compare the types for equality, because well-typed code can // actually "transmute" `&mut T` to `&T` in an assignment without a cast. - let layout_compat = - mir_assign_valid_types(*self.tcx, self.param_env, src.layout(), dest.layout()); + let layout_compat = mir_assign_valid_types( + *self.tcx, + self.typing_mode(), + self.param_env, + src.layout(), + dest.layout(), + ); if !allow_transmute && !layout_compat { span_bug!( self.cur_span(), diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 3bc9f46aea0..50c0446b3cd 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -596,12 +596,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return interp_ok(layout); } - let layout = from_known_layout(self.tcx, self.param_env, layout, || { - let local_ty = frame.body.local_decls[local].ty; - let local_ty = - self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?; - self.layout_of(local_ty).into() - })?; + let layout = + from_known_layout(self.tcx, self.typing_mode(), self.param_env, layout, || { + let local_ty = frame.body.local_decls[local].ty; + let local_ty = + self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?; + self.layout_of(local_ty).into() + })?; // Layouts of locals are requested a lot, so we cache them. state.layout.set(Some(layout)); diff --git a/compiler/rustc_const_eval/src/util/compare_types.rs b/compiler/rustc_const_eval/src/util/compare_types.rs index 7af977bab4d..0cf27d30c36 100644 --- a/compiler/rustc_const_eval/src/util/compare_types.rs +++ b/compiler/rustc_const_eval/src/util/compare_types.rs @@ -8,24 +8,15 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt, TypingMode, Variance}; 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>( +/// Returns whether `src` is a subtype of `dest`, i.e. `src <: dest`. +pub fn sub_types<'tcx>( tcx: TyCtxt<'tcx>, + typing_mode: TypingMode<'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. - relate_types(tcx, param_env, Variance::Covariant, src, dest) - || relate_types(tcx, param_env, Variance::Covariant, dest, src) + relate_types(tcx, typing_mode, param_env, Variance::Covariant, src, dest) } /// Returns whether `src` is a subtype of `dest`, i.e. `src <: dest`. @@ -35,6 +26,7 @@ pub fn is_equal_up_to_subtyping<'tcx>( /// because we want to check for type equality. pub fn relate_types<'tcx>( tcx: TyCtxt<'tcx>, + typing_mode: TypingMode<'tcx>, param_env: ParamEnv<'tcx>, variance: Variance, src: Ty<'tcx>, @@ -45,8 +37,7 @@ pub fn relate_types<'tcx>( } let mut builder = tcx.infer_ctxt().ignoring_regions(); - // FIXME(#132279): This should eventually use the already defined hidden types. - let infcx = builder.build(TypingMode::from_param_env(param_env)); + let infcx = builder.build(typing_mode); let ocx = ObligationCtxt::new(&infcx); let cause = ObligationCause::dummy(); let src = ocx.normalize(&cause, param_env, src); diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 66a1addfb52..25a9dbb2c11 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -8,7 +8,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, relate_types}; +pub use self::compare_types::{relate_types, sub_types}; pub use self::type_name::type_name; /// Classify whether an operator is "left-homogeneous", i.e., the LHS has the diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index cd148aef29b..978abab8b08 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -39,7 +39,7 @@ use crate::ty::fold::{FallibleTypeFolder, TypeFoldable}; use crate::ty::print::{FmtPrinter, Printer, pretty_print_const, with_no_trimmed_paths}; use crate::ty::visit::TypeVisitableExt; use crate::ty::{ - self, AdtDef, GenericArg, GenericArgsRef, Instance, InstanceKind, List, Ty, TyCtxt, + self, AdtDef, GenericArg, GenericArgsRef, Instance, InstanceKind, List, Ty, TyCtxt, TypingMode, UserTypeAnnotationIndex, }; @@ -452,6 +452,15 @@ impl<'tcx> Body<'tcx> { self.basic_blocks.as_mut() } + pub fn typing_mode(&self, _tcx: TyCtxt<'tcx>) -> TypingMode<'tcx> { + match self.phase { + // FIXME(#132279): the MIR is quite clearly inside of a body, so we + // should instead reveal opaques defined by that body here. + MirPhase::Built | MirPhase::Analysis(_) => TypingMode::non_body_analysis(), + MirPhase::Runtime(_) => TypingMode::PostAnalysis, + } + } + #[inline] pub fn local_kind(&self, local: Local) -> LocalKind { let index = local.as_usize(); diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 10b12ee6282..85beb6ef1e7 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -20,9 +20,7 @@ use smallvec::SmallVec; use super::{BasicBlock, Const, Local, UserTypeProjection}; use crate::mir::coverage::CoverageKind; use crate::ty::adjustment::PointerCoercion; -use crate::ty::{ - self, GenericArgsRef, List, Region, Ty, TyCtxt, TypingMode, UserTypeAnnotationIndex, -}; +use crate::ty::{self, GenericArgsRef, List, Region, Ty, TyCtxt, UserTypeAnnotationIndex}; /// Represents the "flavors" of MIR. /// @@ -103,20 +101,10 @@ impl MirPhase { } } - pub fn typing_mode<'tcx>(&self) -> TypingMode<'tcx> { - match self { - // FIXME(#132279): the MIR is quite clearly inside of a body, so we - // should instead reveal opaques defined by that body here. - MirPhase::Built | MirPhase::Analysis(_) => TypingMode::non_body_analysis(), - MirPhase::Runtime(_) => TypingMode::PostAnalysis, - } - } - pub fn param_env<'tcx>(&self, tcx: TyCtxt<'tcx>, body_def_id: DefId) -> ty::ParamEnv<'tcx> { - match self.typing_mode() { - TypingMode::Coherence => unreachable!(), - TypingMode::Analysis { defining_opaque_types: _ } => tcx.param_env(body_def_id), - TypingMode::PostAnalysis => tcx.param_env_reveal_all_normalized(body_def_id), + match self { + MirPhase::Built | MirPhase::Analysis(_) => tcx.param_env(body_def_id), + MirPhase::Runtime(_) => tcx.param_env_reveal_all_normalized(body_def_id), } } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 42d6bdf6cee..404470db5c5 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -244,8 +244,13 @@ impl<'tcx> Inliner<'tcx> { // Normally, this shouldn't be required, but trait normalization failure can create a // validation ICE. let output_type = callee_body.return_ty(); - if !util::relate_types(self.tcx, self.param_env, ty::Covariant, output_type, destination_ty) - { + if !util::sub_types( + self.tcx, + caller_body.typing_mode(self.tcx), + self.param_env, + output_type, + destination_ty, + ) { trace!(?output_type, ?destination_ty); return Err("failed to normalize return type"); } @@ -275,8 +280,13 @@ impl<'tcx> Inliner<'tcx> { self_arg_ty.into_iter().chain(arg_tuple_tys).zip(callee_body.args_iter()) { let input_type = callee_body.local_decls[input].ty; - if !util::relate_types(self.tcx, self.param_env, ty::Covariant, input_type, arg_ty) - { + if !util::sub_types( + self.tcx, + caller_body.typing_mode(self.tcx), + self.param_env, + input_type, + arg_ty, + ) { trace!(?arg_ty, ?input_type); return Err("failed to normalize tuple argument type"); } @@ -285,8 +295,13 @@ impl<'tcx> Inliner<'tcx> { for (arg, input) in args.iter().zip(callee_body.args_iter()) { let input_type = callee_body.local_decls[input].ty; let arg_ty = arg.node.ty(&caller_body.local_decls, self.tcx); - if !util::relate_types(self.tcx, self.param_env, ty::Covariant, input_type, arg_ty) - { + if !util::sub_types( + self.tcx, + caller_body.typing_mode(self.tcx), + self.param_env, + input_type, + arg_ty, + ) { trace!(?arg_ty, ?input_type); return Err("failed to normalize argument type"); } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 1fcbfdde4f7..8109a9b8ba0 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -20,7 +20,7 @@ use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits::ObligationCtxt; use rustc_type_ir::Upcast; -use crate::util::{is_within_packed, relate_types}; +use crate::util::{self, is_within_packed}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum EdgeKind { @@ -583,7 +583,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Variance::Covariant }; - crate::util::relate_types(self.tcx, self.param_env, variance, src, dest) + crate::util::relate_types( + self.tcx, + self.body.typing_mode(self.tcx), + self.param_env, + variance, + src, + dest, + ) } /// Check that the given predicate definitely holds in the param-env of this MIR body. @@ -602,7 +609,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return true; } - let infcx = self.tcx.infer_ctxt().build(self.body.phase.typing_mode()); + let infcx = self.tcx.infer_ctxt().build(self.body.typing_mode(self.tcx)); let ocx = ObligationCtxt::new(&infcx); ocx.register_obligation(Obligation::new( self.tcx, @@ -794,10 +801,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } ProjectionElem::Subtype(ty) => { - if !relate_types( + if !util::sub_types( self.tcx, + self.body.typing_mode(self.tcx), self.param_env, - Variance::Covariant, ty, place_ref.ty(&self.body.local_decls, self.tcx).ty, ) { diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 22223e4a890..aadb64f45d7 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -12,6 +12,13 @@ use crate::{self as ty, Interner}; /// The current typing mode of an inference context. We unfortunately have some /// slightly different typing rules depending on the current context. See the /// doc comment for each variant for how and why they are used. +/// +/// In most cases you can get the correct typing mode automically via: +/// - `mir::Body::typing_mode` +/// - `rustc_lint::LateContext::typing_mode` +/// +/// If neither of these functions are available, feel free to reach out to +/// t-types for help. #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub enum TypingMode { 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 30c97a57514..8f9f75d6824 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 @@ -420,7 +420,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx> TraitRef::new(tcx, tcx.require_lang_item(LangItem::Destruct, Some(body.span)), [ty]), ); - let infcx = tcx.infer_ctxt().build(body.phase.typing_mode()); + let infcx = tcx.infer_ctxt().build(body.typing_mode(tcx)); let mut selcx = SelectionContext::new(&infcx); let Some(impl_src) = selcx.select(&obligation).ok().flatten() else { return false;