summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_const_eval/src/transform
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
commitd1b2d29528b7794b41e66fc2136e395a02f8529b (patch)
treea4a17504b260206dec3cf55b2dca82929a348ac2 /compiler/rustc_const_eval/src/transform
parentReleasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz
rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_const_eval/src/transform')
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs71
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/mod.rs19
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs29
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs6
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs30
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/resolver.rs13
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs16
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs627
8 files changed, 479 insertions, 332 deletions
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 14540e8df..fae047bff 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -8,8 +8,9 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
-use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
+use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt};
+use rustc_middle::ty::{GenericArgKind, GenericArgs};
use rustc_middle::ty::{TraitRef, TypeVisitableExt};
use rustc_mir_dataflow::{self, Analysis};
use rustc_span::{sym, Span, Symbol};
@@ -20,7 +21,7 @@ use std::mem;
use std::ops::Deref;
use super::ops::{self, NonConstOp, Status};
-use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
+use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop};
use super::resolver::FlowSensitiveAnalysis;
use super::{ConstCx, Qualif};
use crate::const_eval::is_unstable_const_fn;
@@ -33,7 +34,7 @@ type QualifResults<'mir, 'tcx, Q> =
pub struct Qualifs<'mir, 'tcx> {
has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
- needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
+ // needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
}
impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
@@ -76,15 +77,17 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
local: Local,
location: Location,
) -> bool {
+ // FIXME(effects) replace with `NeedsNonconstDrop` after const traits work again
+ /*
let ty = ccx.body.local_decls[local].ty;
- if !NeedsNonConstDrop::in_any_value_of_ty(ccx, ty) {
+ if !NeedsDrop::in_any_value_of_ty(ccx, ty) {
return false;
}
let needs_non_const_drop = self.needs_non_const_drop.get_or_insert_with(|| {
let ConstCx { tcx, body, .. } = *ccx;
- FlowSensitiveAnalysis::new(NeedsNonConstDrop, ccx)
+ FlowSensitiveAnalysis::new(NeedsDrop, ccx)
.into_engine(tcx, &body)
.iterate_to_fixpoint()
.into_results_cursor(&body)
@@ -92,6 +95,9 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
needs_non_const_drop.seek_before_primary_effect(location);
needs_non_const_drop.get().contains(local)
+ */
+
+ self.needs_drop(ccx, local, location)
}
/// Returns `true` if `local` is `HasMutInterior` at the given `Location`.
@@ -701,8 +707,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
let fn_ty = func.ty(body, tcx);
- let (mut callee, mut substs) = match *fn_ty.kind() {
- ty::FnDef(def_id, substs) => (def_id, substs),
+ let (mut callee, mut fn_args) = match *fn_ty.kind() {
+ ty::FnDef(def_id, fn_args) => (def_id, fn_args),
ty::FnPtr(_) => {
self.check_op(ops::FnCallIndirect);
@@ -721,7 +727,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
let infcx = tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(&infcx);
- let predicates = tcx.predicates_of(callee).instantiate(tcx, substs);
+ let predicates = tcx.predicates_of(callee).instantiate(tcx, fn_args);
let cause = ObligationCause::new(
terminator.source_info.span,
self.body.source.def_id().expect_local(),
@@ -740,13 +746,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
// Attempting to call a trait method?
+ // FIXME(effects) do we need this?
if let Some(trait_id) = tcx.trait_of_item(callee) {
trace!("attempting to call a trait method");
if !self.tcx.features().const_trait_impl {
self.check_op(ops::FnCallNonConst {
caller,
callee,
- substs,
+ args: fn_args,
span: *fn_span,
call_source: *call_source,
feature: Some(sym::const_trait_impl),
@@ -754,8 +761,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
return;
}
- let trait_ref = TraitRef::from_method(tcx, trait_id, substs);
- let trait_ref = trait_ref.with_constness(ty::BoundConstness::ConstIfConst);
+ let trait_ref = TraitRef::from_method(tcx, trait_id, fn_args);
let obligation =
Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);
@@ -766,7 +772,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
};
match implsrc {
- Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
+ Ok(Some(ImplSource::Param(_))) if tcx.features().effects => {
debug!(
"const_trait_impl: provided {:?} via where-clause in {:?}",
trait_ref, param_env
@@ -774,12 +780,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
return;
}
// Closure: Fn{Once|Mut}
- Ok(Some(ImplSource::Builtin(_)))
+ Ok(Some(ImplSource::Builtin(BuiltinImplSource::Misc, _)))
if trait_ref.self_ty().is_closure()
&& tcx.fn_trait_kind_from_def_id(trait_id).is_some() =>
{
- let ty::Closure(closure_def_id, substs) =
- *trait_ref.self_ty().kind()
+ let ty::Closure(closure_def_id, fn_args) = *trait_ref.self_ty().kind()
else {
unreachable!()
};
@@ -787,7 +792,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
self.check_op(ops::FnCallNonConst {
caller,
callee,
- substs,
+ args: fn_args,
span: *fn_span,
call_source: *call_source,
feature: None,
@@ -798,28 +803,29 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
Ok(Some(ImplSource::UserDefined(data))) => {
let callee_name = tcx.item_name(callee);
- if let Some(&did) = tcx
- .associated_item_def_ids(data.impl_def_id)
- .iter()
- .find(|did| tcx.item_name(**did) == callee_name)
- {
- // using internal substs is ok here, since this is only
- // used for the `resolve` call below
- substs = InternalSubsts::identity_for_item(tcx, did);
- callee = did;
- }
if let hir::Constness::NotConst = tcx.constness(data.impl_def_id) {
self.check_op(ops::FnCallNonConst {
caller,
callee,
- substs,
+ args: fn_args,
span: *fn_span,
call_source: *call_source,
feature: None,
});
return;
}
+
+ if let Some(&did) = tcx
+ .associated_item_def_ids(data.impl_def_id)
+ .iter()
+ .find(|did| tcx.item_name(**did) == callee_name)
+ {
+ // using internal args is ok here, since this is only
+ // used for the `resolve` call below
+ fn_args = GenericArgs::identity_for_item(tcx, did);
+ callee = did;
+ }
}
_ if !tcx.is_const_fn_raw(callee) => {
// At this point, it is only legal when the caller is in a trait
@@ -829,7 +835,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
&& tcx.has_attr(callee_trait, sym::const_trait)
&& Some(callee_trait) == tcx.trait_of_item(caller.to_def_id())
// Can only call methods when it's `<Self as TheTrait>::f`.
- && tcx.types.self_param == substs.type_at(0)
+ && tcx.types.self_param == fn_args.type_at(0)
{
nonconst_call_permission = true;
}
@@ -856,7 +862,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
self.check_op(ops::FnCallNonConst {
caller,
callee,
- substs,
+ args: fn_args,
span: *fn_span,
call_source: *call_source,
feature: None,
@@ -869,7 +875,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
// Resolve a trait method call to its concrete implementation, which may be in a
// `const` trait impl.
- let instance = Instance::resolve(tcx, param_env, callee, substs);
+ let instance = Instance::resolve(tcx, param_env, callee, fn_args);
debug!("Resolving ({:?}) -> {:?}", callee, instance);
if let Ok(Some(func)) = instance {
if let InstanceDef::Item(def) = func.def {
@@ -916,7 +922,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
self.check_op(ops::FnCallNonConst {
caller,
callee,
- substs,
+ args: fn_args,
span: *fn_span,
call_source: *call_source,
feature: None,
@@ -996,8 +1002,9 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
let mut err_span = self.span;
let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty;
+ // FIXME(effects) replace with `NeedsNonConstDrop` once we fix const traits
let ty_needs_non_const_drop =
- qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place);
+ qualifs::NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place);
debug!(?ty_of_dropped_place, ?ty_needs_non_const_drop);
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs
index 8ebfee887..e51082e1e 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs
@@ -68,11 +68,11 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> {
pub fn fn_sig(&self) -> PolyFnSig<'tcx> {
let did = self.def_id().to_def_id();
if self.tcx.is_closure(did) {
- let ty = self.tcx.type_of(did).subst_identity();
- let ty::Closure(_, substs) = ty.kind() else { bug!("type_of closure not ty::Closure") };
- substs.as_closure().sig()
+ let ty = self.tcx.type_of(did).instantiate_identity();
+ let ty::Closure(_, args) = ty.kind() else { bug!("type_of closure not ty::Closure") };
+ args.as_closure().sig()
} else {
- self.tcx.fn_sig(did).subst_identity()
+ self.tcx.fn_sig(did).instantiate_identity()
}
}
}
@@ -127,15 +127,8 @@ fn is_parent_const_stable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
let hir_id = tcx.local_def_id_to_hir_id(local_def_id);
let Some(parent) = tcx.hir().opt_parent_id(hir_id) else { return false };
- let parent_def = tcx.hir().get(parent);
-
- if !matches!(
- parent_def,
- hir::Node::Item(hir::Item {
- kind: hir::ItemKind::Impl(hir::Impl { constness: hir::Constness::Const, .. }),
- ..
- })
- ) {
+
+ if !tcx.is_const_trait_impl_raw(parent.owner.def_id.to_def_id()) {
return false;
}
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index 4eb278252..1f3cda35c 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -9,9 +9,9 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
use rustc_middle::mir::{self, CallSource};
use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
use rustc_middle::ty::TraitRef;
use rustc_middle::ty::{suggest_constraining_type_param, Adt, Closure, FnDef, FnPtr, Param, Ty};
+use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
use rustc_middle::util::{call_kind, CallDesugaringKind, CallKind};
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
@@ -98,7 +98,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
pub struct FnCallNonConst<'tcx> {
pub caller: LocalDefId,
pub callee: DefId,
- pub substs: SubstsRef<'tcx>,
+ pub args: GenericArgsRef<'tcx>,
pub span: Span,
pub call_source: CallSource,
pub feature: Option<Symbol>,
@@ -110,11 +110,11 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
ccx: &ConstCx<'_, 'tcx>,
_: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- let FnCallNonConst { caller, callee, substs, span, call_source, feature } = *self;
+ let FnCallNonConst { caller, callee, args, span, call_source, feature } = *self;
let ConstCx { tcx, param_env, .. } = *ccx;
let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
- let trait_ref = TraitRef::from_method(tcx, trait_id, substs);
+ let trait_ref = TraitRef::from_method(tcx, trait_id, args);
match self_ty.kind() {
Param(param_ty) => {
@@ -145,8 +145,11 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
let implsrc = selcx.select(&obligation);
if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
- let span = tcx.def_span(data.impl_def_id);
- err.subdiagnostic(errors::NonConstImplNote { span });
+ // FIXME(effects) revisit this
+ if !tcx.is_const_trait_impl_raw(data.impl_def_id) {
+ let span = tcx.def_span(data.impl_def_id);
+ err.subdiagnostic(errors::NonConstImplNote { span });
+ }
}
}
_ => {}
@@ -154,7 +157,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
};
let call_kind =
- call_kind(tcx, ccx.param_env, callee, substs, span, call_source.from_hir_call(), None);
+ call_kind(tcx, ccx.param_env, callee, args, span, call_source.from_hir_call(), None);
debug!(?call_kind);
@@ -226,7 +229,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
let mut sugg = None;
if Some(trait_id) == ccx.tcx.lang_items().eq_trait() {
- match (substs[0].unpack(), substs[1].unpack()) {
+ match (args[0].unpack(), args[1].unpack()) {
(GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
if self_ty == rhs_ty
&& self_ty.is_ref()
@@ -297,7 +300,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
.create_err(errors::NonConstFmtMacroCall { span, kind: ccx.const_kind() }),
_ => ccx.tcx.sess.create_err(errors::NonConstFnCall {
span,
- def_path_str: ccx.tcx.def_path_str_with_substs(callee, substs),
+ def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
kind: ccx.const_kind(),
}),
};
@@ -310,8 +313,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
if let Some(feature) = feature && ccx.tcx.sess.is_nightly_build() {
err.help(format!(
- "add `#![feature({})]` to the crate attributes to enable",
- feature,
+ "add `#![feature({feature})]` to the crate attributes to enable",
));
}
@@ -346,10 +348,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
err.help("const-stable functions can only call other const-stable functions");
} else if ccx.tcx.sess.is_nightly_build() {
if let Some(feature) = feature {
- err.help(format!(
- "add `#![feature({})]` to the crate attributes to enable",
- feature
- ));
+ err.help(format!("add `#![feature({feature})]` to the crate attributes to enable"));
}
}
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
index 1f1640fd8..e3377bd10 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
@@ -5,7 +5,7 @@ use rustc_span::{symbol::sym, Span};
use super::check::Qualifs;
use super::ops::{self, NonConstOp};
-use super::qualifs::{NeedsNonConstDrop, Qualif};
+use super::qualifs::{NeedsDrop, Qualif};
use super::ConstCx;
/// Returns `true` if we should use the more precise live drop checker that runs after drop
@@ -82,7 +82,9 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
match &terminator.kind {
mir::TerminatorKind::Drop { place: dropped_place, .. } => {
let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
- if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
+
+ // FIXME(effects) use `NeedsNonConstDrop`
+ if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
// Instead of throwing a bug, we just return here. This is because we have to
// run custom `const Drop` impls.
return;
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 015a4aa94..b1b2859ef 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -7,7 +7,8 @@ use rustc_hir::LangItem;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir;
use rustc_middle::mir::*;
-use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty};
+use rustc_middle::traits::BuiltinImplSource;
+use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty};
use rustc_trait_selection::traits::{
self, ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext,
};
@@ -22,7 +23,8 @@ pub fn in_any_value_of_ty<'tcx>(
ConstQualifs {
has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
- needs_non_const_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty),
+ // FIXME(effects)
+ needs_non_const_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
custom_eq: CustomEq::in_any_value_of_ty(cx, ty),
tainted_by_errors,
}
@@ -72,7 +74,7 @@ pub trait Qualif {
fn in_adt_inherently<'tcx>(
cx: &ConstCx<'_, 'tcx>,
adt: AdtDef<'tcx>,
- substs: SubstsRef<'tcx>,
+ args: GenericArgsRef<'tcx>,
) -> bool;
}
@@ -97,7 +99,7 @@ impl Qualif for HasMutInterior {
fn in_adt_inherently<'tcx>(
_cx: &ConstCx<'_, 'tcx>,
adt: AdtDef<'tcx>,
- _: SubstsRef<'tcx>,
+ _: GenericArgsRef<'tcx>,
) -> bool {
// Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently.
// It arises structurally for all other types.
@@ -127,7 +129,7 @@ impl Qualif for NeedsDrop {
fn in_adt_inherently<'tcx>(
cx: &ConstCx<'_, 'tcx>,
adt: AdtDef<'tcx>,
- _: SubstsRef<'tcx>,
+ _: GenericArgsRef<'tcx>,
) -> bool {
adt.has_dtor(cx.tcx)
}
@@ -153,12 +155,12 @@ impl Qualif for NeedsNonConstDrop {
return false;
}
+ // FIXME(effects) constness
let obligation = Obligation::new(
cx.tcx,
ObligationCause::dummy_with_span(cx.body.span),
cx.param_env,
- ty::TraitRef::from_lang_item(cx.tcx, LangItem::Destruct, cx.body.span, [ty])
- .with_constness(ty::BoundConstness::ConstIfConst),
+ ty::TraitRef::from_lang_item(cx.tcx, LangItem::Destruct, cx.body.span, [ty]),
);
let infcx = cx.tcx.infer_ctxt().build();
@@ -172,7 +174,7 @@ impl Qualif for NeedsNonConstDrop {
if !matches!(
impl_src,
- ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
+ ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(_)
) {
// If our const destruct candidate is not ConstDestruct or implied by the param env,
// then it's bad
@@ -193,7 +195,7 @@ impl Qualif for NeedsNonConstDrop {
fn in_adt_inherently<'tcx>(
cx: &ConstCx<'_, 'tcx>,
adt: AdtDef<'tcx>,
- _: SubstsRef<'tcx>,
+ _: GenericArgsRef<'tcx>,
) -> bool {
adt.has_non_const_dtor(cx.tcx)
}
@@ -221,9 +223,9 @@ impl Qualif for CustomEq {
fn in_adt_inherently<'tcx>(
cx: &ConstCx<'_, 'tcx>,
def: AdtDef<'tcx>,
- substs: SubstsRef<'tcx>,
+ args: GenericArgsRef<'tcx>,
) -> bool {
- let ty = Ty::new_adt(cx.tcx, def, substs);
+ let ty = Ty::new_adt(cx.tcx, def, args);
!ty.is_structural_eq_shallow(cx.tcx)
}
}
@@ -276,9 +278,9 @@ where
Rvalue::Aggregate(kind, operands) => {
// Return early if we know that the struct or enum being constructed is always
// qualified.
- if let AggregateKind::Adt(adt_did, _, substs, ..) = **kind {
+ if let AggregateKind::Adt(adt_did, _, args, ..) = **kind {
let def = cx.tcx.adt_def(adt_did);
- if Q::in_adt_inherently(cx, def, substs) {
+ if Q::in_adt_inherently(cx, def, args) {
return true;
}
if def.is_union() && Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx)) {
@@ -360,7 +362,7 @@ where
ConstantKind::Val(..) => None,
};
- if let Some(mir::UnevaluatedConst { def, substs: _, promoted }) = uneval {
+ if let Some(mir::UnevaluatedConst { def, args: _, promoted }) = uneval {
// Use qualifs of the type for the promoted. Promoteds in MIR body should be possible
// only for `NeedsNonConstDrop` with precise drop checking. This is the only const
// check performed after the promotion. Verify that with an assertion.
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
index 3a869f7f5..a137f84b7 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
@@ -4,10 +4,12 @@
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::Visitor;
-use rustc_middle::mir::{self, BasicBlock, Local, Location, Statement, StatementKind};
+use rustc_middle::mir::{
+ self, BasicBlock, CallReturnPlaces, Local, Location, Statement, StatementKind, TerminatorEdges,
+};
use rustc_mir_dataflow::fmt::DebugWithContext;
use rustc_mir_dataflow::JoinSemiLattice;
-use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces};
+use rustc_mir_dataflow::{Analysis, AnalysisDomain};
use std::fmt;
use std::marker::PhantomData;
@@ -345,13 +347,14 @@ where
self.transfer_function(state).visit_statement(statement, location);
}
- fn apply_terminator_effect(
+ fn apply_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
- terminator: &mir::Terminator<'tcx>,
+ terminator: &'mir mir::Terminator<'tcx>,
location: Location,
- ) {
+ ) -> TerminatorEdges<'mir, 'tcx> {
self.transfer_function(state).visit_terminator(terminator, location);
+ terminator.edges()
}
fn apply_call_return_effect(
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 1b39a76e4..d79c65f1d 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -16,7 +16,7 @@ use rustc_hir as hir;
use rustc_middle::mir;
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
-use rustc_middle::ty::subst::InternalSubsts;
+use rustc_middle::ty::GenericArgs;
use rustc_middle::ty::{self, List, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::Span;
@@ -759,11 +759,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
let (mut rvalue, source_info) = {
let statement = &mut self.source[loc.block].statements[loc.statement_index];
let StatementKind::Assign(box (_, rhs)) = &mut statement.kind else {
- span_bug!(
- statement.source_info.span,
- "{:?} is not an assignment",
- statement
- );
+ span_bug!(statement.source_info.span, "{:?} is not an assignment", statement);
};
(
@@ -845,8 +841,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
let mut promoted_operand = |ty, span| {
promoted.span = span;
promoted.local_decls[RETURN_PLACE] = LocalDecl::new(ty, span);
- let substs = tcx.erase_regions(InternalSubsts::identity_for_item(tcx, def));
- let uneval = mir::UnevaluatedConst { def, substs, promoted: Some(promoted_id) };
+ let args = tcx.erase_regions(GenericArgs::identity_for_item(tcx, def));
+ let uneval = mir::UnevaluatedConst { def, args, promoted: Some(promoted_id) };
Operand::Constant(Box::new(Constant {
span,
@@ -859,7 +855,9 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
let local_decls = &mut self.source.local_decls;
let loc = candidate.location;
let statement = &mut blocks[loc.block].statements[loc.statement_index];
- let StatementKind::Assign(box (_, Rvalue::Ref(region, borrow_kind, place))) = &mut statement.kind else {
+ let StatementKind::Assign(box (_, Rvalue::Ref(region, borrow_kind, place))) =
+ &mut statement.kind
+ else {
bug!()
};
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 4cc923cd9..783b52d00 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -18,6 +18,7 @@ use rustc_mir_dataflow::impls::MaybeStorageLive;
use rustc_mir_dataflow::storage::always_storage_live_locals;
use rustc_mir_dataflow::{Analysis, ResultsCursor};
use rustc_target::abi::{Size, FIRST_VARIANT};
+use rustc_target::spec::abi::Abi;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum EdgeKind {
@@ -58,25 +59,48 @@ impl<'tcx> MirPass<'tcx> for Validator {
.iterate_to_fixpoint()
.into_results_cursor(body);
- let mut checker = TypeChecker {
+ let can_unwind = if mir_phase <= MirPhase::Runtime(RuntimePhase::Initial) {
+ // In this case `AbortUnwindingCalls` haven't yet been executed.
+ true
+ } else if !tcx.def_kind(def_id).is_fn_like() {
+ true
+ } else {
+ let body_ty = tcx.type_of(def_id).skip_binder();
+ let body_abi = match body_ty.kind() {
+ ty::FnDef(..) => body_ty.fn_sig(tcx).abi(),
+ ty::Closure(..) => Abi::RustCall,
+ ty::Generator(..) => Abi::Rust,
+ _ => {
+ span_bug!(body.span, "unexpected body ty: {:?} phase {:?}", body_ty, mir_phase)
+ }
+ };
+
+ ty::layout::fn_can_unwind(tcx, Some(def_id), body_abi)
+ };
+
+ let mut cfg_checker = CfgChecker {
when: &self.when,
body,
tcx,
- param_env,
mir_phase,
unwind_edge_count: 0,
reachable_blocks: traversal::reachable_as_bitset(body),
storage_liveness,
place_cache: FxHashSet::default(),
value_cache: FxHashSet::default(),
+ can_unwind,
};
- checker.visit_body(body);
- checker.check_cleanup_control_flow();
+ cfg_checker.visit_body(body);
+ cfg_checker.check_cleanup_control_flow();
+
+ for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body) {
+ cfg_checker.fail(location, msg);
+ }
if let MirPhase::Runtime(_) = body.phase {
if let ty::InstanceDef::Item(_) = body.source.instance {
if body.has_free_regions() {
- checker.fail(
+ cfg_checker.fail(
Location::START,
format!("Free regions in optimized {} MIR", body.phase.name()),
);
@@ -86,20 +110,22 @@ impl<'tcx> MirPass<'tcx> for Validator {
}
}
-struct TypeChecker<'a, 'tcx> {
+struct CfgChecker<'a, 'tcx> {
when: &'a str,
body: &'a Body<'tcx>,
tcx: TyCtxt<'tcx>,
- param_env: ParamEnv<'tcx>,
mir_phase: MirPhase,
unwind_edge_count: usize,
reachable_blocks: BitSet<BasicBlock>,
storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>,
place_cache: FxHashSet<PlaceRef<'tcx>>,
value_cache: FxHashSet<u128>,
+ // If `false`, then the MIR must not contain `UnwindAction::Continue` or
+ // `TerminatorKind::Resume`.
+ can_unwind: bool,
}
-impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
+impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
#[track_caller]
fn fail(&self, location: Location, msg: impl AsRef<str>) {
let span = self.body.source_info(location).span;
@@ -147,7 +173,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
} else {
- self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
+ self.fail(location, format!("encountered jump to invalid basic block {bb:?}"))
}
}
@@ -214,16 +240,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
stack.clear();
stack.insert(bb);
loop {
- let Some(parent)= parent[bb].take() else {
- break
- };
+ let Some(parent) = parent[bb].take() else { break };
let no_cycle = stack.insert(parent);
if !no_cycle {
self.fail(
Location { block: bb, statement_index: 0 },
format!(
- "Cleanup control flow violation: Cycle involving edge {:?} -> {:?}",
- bb, parent,
+ "Cleanup control flow violation: Cycle involving edge {bb:?} -> {parent:?}",
),
);
break;
@@ -238,47 +261,30 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
match unwind {
UnwindAction::Cleanup(unwind) => {
if is_cleanup {
- self.fail(location, "unwind on cleanup block");
+ self.fail(location, "`UnwindAction::Cleanup` in cleanup block");
}
self.check_edge(location, unwind, EdgeKind::Unwind);
}
UnwindAction::Continue => {
if is_cleanup {
- self.fail(location, "unwind on cleanup block");
+ self.fail(location, "`UnwindAction::Continue` in cleanup block");
+ }
+
+ if !self.can_unwind {
+ self.fail(location, "`UnwindAction::Continue` in no-unwind function");
}
}
UnwindAction::Unreachable | UnwindAction::Terminate => (),
}
}
-
- /// Check if src can be assigned into dest.
- /// This is not precise, it will accept some incorrect assignments.
- fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
- // Fast path before we normalize.
- if src == dest {
- // Equal types, all is good.
- return true;
- }
-
- // We sometimes have to use `defining_opaque_types` for subtyping
- // to succeed here and figuring out how exactly that should work
- // is annoying. It is harmless enough to just not validate anything
- // in that case. We still check this after analysis as all opaque
- // types have been revealed at this point.
- if (src, dest).has_opaque_types() {
- return true;
- }
-
- crate::util::is_subtype(self.tcx, self.param_env, src, dest)
- }
}
-impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
+impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
if self.body.local_decls.get(local).is_none() {
self.fail(
location,
- format!("local {:?} has no corresponding declaration in `body.local_decls`", local),
+ format!("local {local:?} has no corresponding declaration in `body.local_decls`"),
);
}
@@ -293,11 +299,286 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.storage_liveness.seek_after_primary_effect(location);
let locals_with_storage = self.storage_liveness.get();
if !locals_with_storage.contains(local) {
- self.fail(location, format!("use of local {:?}, which has no storage here", local));
+ self.fail(location, format!("use of local {local:?}, which has no storage here"));
+ }
+ }
+ }
+
+ fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
+ match &statement.kind {
+ StatementKind::Assign(box (dest, rvalue)) => {
+ // FIXME(JakobDegen): Check this for all rvalues, not just this one.
+ if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue {
+ // The sides of an assignment must not alias. Currently this just checks whether
+ // the places are identical.
+ if dest == src {
+ self.fail(
+ location,
+ "encountered `Assign` statement with overlapping memory",
+ );
+ }
+ }
+ }
+ StatementKind::AscribeUserType(..) => {
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+ self.fail(
+ location,
+ "`AscribeUserType` should have been removed after drop lowering phase",
+ );
+ }
+ }
+ StatementKind::FakeRead(..) => {
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+ self.fail(
+ location,
+ "`FakeRead` should have been removed after drop lowering phase",
+ );
+ }
+ }
+ StatementKind::SetDiscriminant { .. } => {
+ if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
+ self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
+ }
+ }
+ StatementKind::Deinit(..) => {
+ if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
+ self.fail(location, "`Deinit`is not allowed until deaggregation");
+ }
+ }
+ StatementKind::Retag(kind, _) => {
+ // FIXME(JakobDegen) The validator should check that `self.mir_phase <
+ // DropsLowered`. However, this causes ICEs with generation of drop shims, which
+ // seem to fail to set their `MirPhase` correctly.
+ if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) {
+ self.fail(location, format!("explicit `{kind:?}` is forbidden"));
+ }
+ }
+ StatementKind::StorageLive(local) => {
+ // We check that the local is not live when entering a `StorageLive` for it.
+ // Technically, violating this restriction is only UB and not actually indicative
+ // of not well-formed MIR. This means that an optimization which turns MIR that
+ // already has UB into MIR that fails this check is not necessarily wrong. However,
+ // we have no such optimizations at the moment, and so we include this check anyway
+ // to help us catch bugs. If you happen to write an optimization that might cause
+ // this to incorrectly fire, feel free to remove this check.
+ if self.reachable_blocks.contains(location.block) {
+ self.storage_liveness.seek_before_primary_effect(location);
+ let locals_with_storage = self.storage_liveness.get();
+ if locals_with_storage.contains(*local) {
+ self.fail(
+ location,
+ format!("StorageLive({local:?}) which already has storage here"),
+ );
+ }
+ }
+ }
+ StatementKind::StorageDead(_)
+ | StatementKind::Intrinsic(_)
+ | StatementKind::Coverage(_)
+ | StatementKind::ConstEvalCounter
+ | StatementKind::PlaceMention(..)
+ | StatementKind::Nop => {}
+ }
+
+ self.super_statement(statement, location);
+ }
+
+ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
+ match &terminator.kind {
+ TerminatorKind::Goto { target } => {
+ self.check_edge(location, *target, EdgeKind::Normal);
+ }
+ TerminatorKind::SwitchInt { targets, discr: _ } => {
+ for (_, target) in targets.iter() {
+ self.check_edge(location, target, EdgeKind::Normal);
+ }
+ self.check_edge(location, targets.otherwise(), EdgeKind::Normal);
+
+ self.value_cache.clear();
+ self.value_cache.extend(targets.iter().map(|(value, _)| value));
+ let has_duplicates = targets.iter().len() != self.value_cache.len();
+ if has_duplicates {
+ self.fail(
+ location,
+ format!(
+ "duplicated values in `SwitchInt` terminator: {:?}",
+ terminator.kind,
+ ),
+ );
+ }
+ }
+ TerminatorKind::Drop { target, unwind, .. } => {
+ self.check_edge(location, *target, EdgeKind::Normal);
+ self.check_unwind_edge(location, *unwind);
+ }
+ TerminatorKind::Call { args, destination, target, unwind, .. } => {
+ if let Some(target) = target {
+ self.check_edge(location, *target, EdgeKind::Normal);
+ }
+ self.check_unwind_edge(location, *unwind);
+
+ // The call destination place and Operand::Move place used as an argument might be
+ // passed by a reference to the callee. Consequently they must be non-overlapping.
+ // Currently this simply checks for duplicate places.
+ self.place_cache.clear();
+ self.place_cache.insert(destination.as_ref());
+ let mut has_duplicates = false;
+ for arg in args {
+ if let Operand::Move(place) = arg {
+ has_duplicates |= !self.place_cache.insert(place.as_ref());
+ }
+ }
+
+ if has_duplicates {
+ self.fail(
+ location,
+ format!(
+ "encountered overlapping memory in `Call` terminator: {:?}",
+ terminator.kind,
+ ),
+ );
+ }
+ }
+ TerminatorKind::Assert { target, unwind, .. } => {
+ self.check_edge(location, *target, EdgeKind::Normal);
+ self.check_unwind_edge(location, *unwind);
+ }
+ TerminatorKind::Yield { resume, drop, .. } => {
+ if self.body.generator.is_none() {
+ self.fail(location, "`Yield` cannot appear outside generator bodies");
+ }
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+ self.fail(location, "`Yield` should have been replaced by generator lowering");
+ }
+ self.check_edge(location, *resume, EdgeKind::Normal);
+ if let Some(drop) = drop {
+ self.check_edge(location, *drop, EdgeKind::Normal);
+ }
+ }
+ TerminatorKind::FalseEdge { real_target, imaginary_target } => {
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+ self.fail(
+ location,
+ "`FalseEdge` should have been removed after drop elaboration",
+ );
+ }
+ self.check_edge(location, *real_target, EdgeKind::Normal);
+ self.check_edge(location, *imaginary_target, EdgeKind::Normal);
+ }
+ TerminatorKind::FalseUnwind { real_target, unwind } => {
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+ self.fail(
+ location,
+ "`FalseUnwind` should have been removed after drop elaboration",
+ );
+ }
+ self.check_edge(location, *real_target, EdgeKind::Normal);
+ self.check_unwind_edge(location, *unwind);
+ }
+ TerminatorKind::InlineAsm { destination, unwind, .. } => {
+ if let Some(destination) = destination {
+ self.check_edge(location, *destination, EdgeKind::Normal);
+ }
+ self.check_unwind_edge(location, *unwind);
+ }
+ TerminatorKind::GeneratorDrop => {
+ if self.body.generator.is_none() {
+ self.fail(location, "`GeneratorDrop` cannot appear outside generator bodies");
+ }
+ if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
+ self.fail(
+ location,
+ "`GeneratorDrop` should have been replaced by generator lowering",
+ );
+ }
+ }
+ TerminatorKind::Resume => {
+ let bb = location.block;
+ if !self.body.basic_blocks[bb].is_cleanup {
+ self.fail(location, "Cannot `Resume` from non-cleanup basic block")
+ }
+ if !self.can_unwind {
+ self.fail(location, "Cannot `Resume` in a function that cannot unwind")
+ }
+ }
+ TerminatorKind::Terminate => {
+ let bb = location.block;
+ if !self.body.basic_blocks[bb].is_cleanup {
+ self.fail(location, "Cannot `Terminate` from non-cleanup basic block")
+ }
+ }
+ TerminatorKind::Return => {
+ let bb = location.block;
+ if self.body.basic_blocks[bb].is_cleanup {
+ self.fail(location, "Cannot `Return` from cleanup basic block")
+ }
}
+ TerminatorKind::Unreachable => {}
+ }
+
+ self.super_terminator(terminator, location);
+ }
+
+ fn visit_source_scope(&mut self, scope: SourceScope) {
+ if self.body.source_scopes.get(scope).is_none() {
+ self.tcx.sess.diagnostic().delay_span_bug(
+ self.body.span,
+ format!(
+ "broken MIR in {:?} ({}):\ninvalid source scope {:?}",
+ self.body.source.instance, self.when, scope,
+ ),
+ );
}
}
+}
+
+pub fn validate_types<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ mir_phase: MirPhase,
+ param_env: ty::ParamEnv<'tcx>,
+ body: &Body<'tcx>,
+) -> Vec<(Location, String)> {
+ let mut type_checker = TypeChecker { body, tcx, param_env, mir_phase, failures: Vec::new() };
+ type_checker.visit_body(body);
+ type_checker.failures
+}
+
+struct TypeChecker<'a, 'tcx> {
+ body: &'a Body<'tcx>,
+ tcx: TyCtxt<'tcx>,
+ param_env: ParamEnv<'tcx>,
+ mir_phase: MirPhase,
+ failures: Vec<(Location, String)>,
+}
+
+impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
+ fn fail(&mut self, location: Location, msg: impl Into<String>) {
+ self.failures.push((location, msg.into()));
+ }
+
+ /// Check if src can be assigned into dest.
+ /// This is not precise, it will accept some incorrect assignments.
+ fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
+ // Fast path before we normalize.
+ if src == dest {
+ // Equal types, all is good.
+ return true;
+ }
+ // We sometimes have to use `defining_opaque_types` for subtyping
+ // to succeed here and figuring out how exactly that should work
+ // is annoying. It is harmless enough to just not validate anything
+ // in that case. We still check this after analysis as all opaque
+ // types have been revealed at this point.
+ if (src, dest).has_opaque_types() {
+ return true;
+ }
+
+ crate::util::is_subtype(self.tcx, self.param_env, src, dest)
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
// This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
if self.tcx.sess.opts.unstable_opts.validate_mir
@@ -308,7 +589,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
let ty = place.ty(&self.body.local_decls, self.tcx).ty;
if !ty.is_copy_modulo_regions(self.tcx, self.param_env) {
- self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
+ self.fail(location, format!("`Operand::Copy` with non-`Copy` type {ty}"));
}
}
}
@@ -327,7 +608,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
ProjectionElem::Index(index) => {
let index_ty = self.body.local_decls[index].ty;
if index_ty != self.tcx.types.usize {
- self.fail(location, format!("bad index ({:?} != usize)", index_ty))
+ self.fail(location, format!("bad index ({index_ty:?} != usize)"))
}
}
ProjectionElem::Deref
@@ -338,30 +619,29 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
if base_ty.is_box() {
self.fail(
location,
- format!("{:?} dereferenced after ElaborateBoxDerefs", base_ty),
+ format!("{base_ty:?} dereferenced after ElaborateBoxDerefs"),
)
}
}
ProjectionElem::Field(f, ty) => {
let parent_ty = place_ref.ty(&self.body.local_decls, self.tcx);
- let fail_out_of_bounds = |this: &Self, location| {
- this.fail(location, format!("Out of bounds field {:?} for {:?}", f, parent_ty));
+ let fail_out_of_bounds = |this: &mut Self, location| {
+ this.fail(location, format!("Out of bounds field {f:?} for {parent_ty:?}"));
};
- let check_equal = |this: &Self, location, f_ty| {
+ let check_equal = |this: &mut Self, location, f_ty| {
if !this.mir_assign_valid_types(ty, f_ty) {
this.fail(
location,
format!(
- "Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is `{:?}`",
- place_ref, f, ty, f_ty
+ "Field projection `{place_ref:?}.{f:?}` specified type `{ty:?}`, but actual type is `{f_ty:?}`"
)
)
}
};
let kind = match parent_ty.ty.kind() {
- &ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
- self.tcx.type_of(def_id).subst(self.tcx, substs).kind()
+ &ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
+ self.tcx.type_of(def_id).instantiate(self.tcx, args).kind()
}
kind => kind,
};
@@ -374,23 +654,23 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
};
check_equal(self, location, *f_ty);
}
- ty::Adt(adt_def, substs) => {
+ ty::Adt(adt_def, args) => {
let var = parent_ty.variant_index.unwrap_or(FIRST_VARIANT);
let Some(field) = adt_def.variant(var).fields.get(f) else {
fail_out_of_bounds(self, location);
return;
};
- check_equal(self, location, field.ty(self.tcx, substs));
+ check_equal(self, location, field.ty(self.tcx, args));
}
- ty::Closure(_, substs) => {
- let substs = substs.as_closure();
- let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else {
+ ty::Closure(_, args) => {
+ let args = args.as_closure();
+ let Some(&f_ty) = args.upvar_tys().get(f.as_usize()) else {
fail_out_of_bounds(self, location);
return;
};
check_equal(self, location, f_ty);
}
- &ty::Generator(def_id, substs, _) => {
+ &ty::Generator(def_id, args, _) => {
let f_ty = if let Some(var) = parent_ty.variant_index {
let gen_body = if def_id == self.body.source.def_id() {
self.body
@@ -399,7 +679,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
};
let Some(layout) = gen_body.generator_layout() else {
- self.fail(location, format!("No generator layout for {:?}", parent_ty));
+ self.fail(
+ location,
+ format!("No generator layout for {parent_ty:?}"),
+ );
return;
};
@@ -409,13 +692,17 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
};
let Some(f_ty) = layout.field_tys.get(local) else {
- self.fail(location, format!("Out of bounds local {:?} for {:?}", local, parent_ty));
+ self.fail(
+ location,
+ format!("Out of bounds local {local:?} for {parent_ty:?}"),
+ );
return;
};
- f_ty.ty
+ ty::EarlyBinder::bind(f_ty.ty).instantiate(self.tcx, args)
} else {
- let Some(f_ty) = substs.as_generator().prefix_tys().nth(f.index()) else {
+ let Some(&f_ty) = args.as_generator().prefix_tys().get(f.index())
+ else {
fail_out_of_bounds(self, location);
return;
};
@@ -436,9 +723,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
fn visit_var_debug_info(&mut self, debuginfo: &VarDebugInfo<'tcx>) {
- let check_place = |place: Place<'_>| {
+ let check_place = |this: &mut Self, place: Place<'_>| {
if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
- self.fail(
+ this.fail(
START_BLOCK.start_location(),
format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
);
@@ -447,21 +734,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
match debuginfo.value {
VarDebugInfoContents::Const(_) => {}
VarDebugInfoContents::Place(place) => {
- check_place(place);
- if debuginfo.references != 0 && place.projection.last() == Some(&PlaceElem::Deref) {
- self.fail(
- START_BLOCK.start_location(),
- format!("debuginfo {:?}, has both ref and deref", debuginfo),
- );
- }
+ check_place(self, place);
}
VarDebugInfoContents::Composite { ty, ref fragments } => {
for f in fragments {
- check_place(f.contents);
+ check_place(self, f.contents);
if ty.is_union() || ty.is_enum() {
self.fail(
START_BLOCK.start_location(),
- format!("invalid type {:?} for composite debuginfo", ty),
+ format!("invalid type {ty:?} for composite debuginfo"),
);
}
if f.projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) {
@@ -488,7 +769,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
&& cntxt != PlaceContext::NonUse(NonUseContext::VarDebugInfo)
&& place.projection[1..].contains(&ProjectionElem::Deref)
{
- self.fail(location, format!("{:?}, has deref at the wrong place", place));
+ self.fail(location, format!("{place:?}, has deref at the wrong place"));
}
self.super_place(place, cntxt, location);
@@ -548,7 +829,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
Offset => {
check_kinds!(a, "Cannot offset non-pointer type {:?}", ty::RawPtr(..));
if b != self.tcx.types.isize && b != self.tcx.types.usize {
- self.fail(location, format!("Cannot offset by non-isize type {:?}", b));
+ self.fail(location, format!("Cannot offset by non-isize type {b:?}"));
}
}
Eq | Lt | Le | Ne | Ge | Gt => {
@@ -613,13 +894,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.fail(
location,
format!(
- "Cannot perform checked arithmetic on unequal types {:?} and {:?}",
- a, b
+ "Cannot perform checked arithmetic on unequal types {a:?} and {b:?}"
),
);
}
}
- _ => self.fail(location, format!("There is no checked version of {:?}", op)),
+ _ => self.fail(location, format!("There is no checked version of {op:?}")),
}
}
Rvalue::UnaryOp(op, operand) => {
@@ -714,7 +994,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
Rvalue::NullaryOp(NullOp::OffsetOf(fields), container) => {
- let fail_out_of_bounds = |this: &Self, location, field, ty| {
+ let fail_out_of_bounds = |this: &mut Self, location, field, ty| {
this.fail(location, format!("Out of bounds field {field:?} for {ty:?}"));
};
@@ -730,7 +1010,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
current_ty = self.tcx.normalize_erasing_regions(self.param_env, f_ty);
}
- ty::Adt(adt_def, substs) => {
+ ty::Adt(adt_def, args) => {
if adt_def.is_enum() {
self.fail(
location,
@@ -744,7 +1024,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
return;
};
- let f_ty = field.ty(self.tcx, substs);
+ let f_ty = field.ty(self.tcx, args);
current_ty = self.tcx.normalize_erasing_regions(self.param_env, f_ty);
}
_ => {
@@ -824,7 +1104,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
if !ty.is_bool() {
self.fail(
location,
- format!("`assume` argument must be `bool`, but got: `{}`", ty),
+ format!("`assume` argument must be `bool`, but got: `{ty}`"),
);
}
}
@@ -837,7 +1117,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
} else {
self.fail(
location,
- format!("Expected src to be ptr in copy_nonoverlapping, got: {}", src_ty),
+ format!("Expected src to be ptr in copy_nonoverlapping, got: {src_ty}"),
);
return;
};
@@ -847,19 +1127,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
} else {
self.fail(
location,
- format!("Expected dst to be ptr in copy_nonoverlapping, got: {}", dst_ty),
+ format!("Expected dst to be ptr in copy_nonoverlapping, got: {dst_ty}"),
);
return;
};
// since CopyNonOverlapping is parametrized by 1 type,
// we only need to check that they are equal and not keep an extra parameter.
if !self.mir_assign_valid_types(op_src_ty, op_dst_ty) {
- self.fail(location, format!("bad arg ({:?} != {:?})", op_src_ty, op_dst_ty));
+ self.fail(location, format!("bad arg ({op_src_ty:?} != {op_dst_ty:?})"));
}
let op_cnt_ty = count.ty(&self.body.local_decls, self.tcx);
if op_cnt_ty != self.tcx.types.usize {
- self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty))
+ self.fail(location, format!("bad arg ({op_cnt_ty:?} != usize)"))
}
}
StatementKind::SetDiscriminant { place, .. } => {
@@ -871,8 +1151,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.fail(
location,
format!(
- "`SetDiscriminant` is only allowed on ADTs and generators, not {:?}",
- pty
+ "`SetDiscriminant` is only allowed on ADTs and generators, not {pty:?}"
),
);
}
@@ -887,29 +1166,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
// DropsLowered`. However, this causes ICEs with generation of drop shims, which
// seem to fail to set their `MirPhase` correctly.
if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) {
- self.fail(location, format!("explicit `{:?}` is forbidden", kind));
+ self.fail(location, format!("explicit `{kind:?}` is forbidden"));
}
}
- StatementKind::StorageLive(local) => {
- // We check that the local is not live when entering a `StorageLive` for it.
- // Technically, violating this restriction is only UB and not actually indicative
- // of not well-formed MIR. This means that an optimization which turns MIR that
- // already has UB into MIR that fails this check is not necessarily wrong. However,
- // we have no such optimizations at the moment, and so we include this check anyway
- // to help us catch bugs. If you happen to write an optimization that might cause
- // this to incorrectly fire, feel free to remove this check.
- if self.reachable_blocks.contains(location.block) {
- self.storage_liveness.seek_before_primary_effect(location);
- let locals_with_storage = self.storage_liveness.get();
- if locals_with_storage.contains(*local) {
- self.fail(
- location,
- format!("StorageLive({local:?}) which already has storage here"),
- );
- }
- }
- }
- StatementKind::StorageDead(_)
+ StatementKind::StorageLive(_)
+ | StatementKind::StorageDead(_)
| StatementKind::Coverage(_)
| StatementKind::ConstEvalCounter
| StatementKind::PlaceMention(..)
@@ -921,9 +1182,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
match &terminator.kind {
- TerminatorKind::Goto { target } => {
- self.check_edge(location, *target, EdgeKind::Normal);
- }
TerminatorKind::SwitchInt { targets, discr } => {
let switch_ty = discr.ty(&self.body.local_decls, self.tcx);
@@ -937,164 +1195,49 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
other => bug!("unhandled type: {:?}", other),
});
- for (value, target) in targets.iter() {
+ for (value, _) in targets.iter() {
if Scalar::<()>::try_from_uint(value, size).is_none() {
self.fail(
location,
- format!("the value {:#x} is not a proper {:?}", value, switch_ty),
+ format!("the value {value:#x} is not a proper {switch_ty:?}"),
)
}
-
- self.check_edge(location, target, EdgeKind::Normal);
- }
- self.check_edge(location, targets.otherwise(), EdgeKind::Normal);
-
- self.value_cache.clear();
- self.value_cache.extend(targets.iter().map(|(value, _)| value));
- let has_duplicates = targets.iter().len() != self.value_cache.len();
- if has_duplicates {
- self.fail(
- location,
- format!(
- "duplicated values in `SwitchInt` terminator: {:?}",
- terminator.kind,
- ),
- );
}
}
- TerminatorKind::Drop { target, unwind, .. } => {
- self.check_edge(location, *target, EdgeKind::Normal);
- self.check_unwind_edge(location, *unwind);
- }
- TerminatorKind::Call { func, args, destination, target, unwind, .. } => {
+ TerminatorKind::Call { func, .. } => {
let func_ty = func.ty(&self.body.local_decls, self.tcx);
match func_ty.kind() {
ty::FnPtr(..) | ty::FnDef(..) => {}
_ => self.fail(
location,
- format!("encountered non-callable type {} in `Call` terminator", func_ty),
+ format!("encountered non-callable type {func_ty} in `Call` terminator"),
),
}
- if let Some(target) = target {
- self.check_edge(location, *target, EdgeKind::Normal);
- }
- self.check_unwind_edge(location, *unwind);
-
- // The call destination place and Operand::Move place used as an argument might be
- // passed by a reference to the callee. Consequently they must be non-overlapping.
- // Currently this simply checks for duplicate places.
- self.place_cache.clear();
- self.place_cache.insert(destination.as_ref());
- let mut has_duplicates = false;
- for arg in args {
- if let Operand::Move(place) = arg {
- has_duplicates |= !self.place_cache.insert(place.as_ref());
- }
- }
-
- if has_duplicates {
- self.fail(
- location,
- format!(
- "encountered overlapping memory in `Call` terminator: {:?}",
- terminator.kind,
- ),
- );
- }
}
- TerminatorKind::Assert { cond, target, unwind, .. } => {
+ TerminatorKind::Assert { cond, .. } => {
let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
if cond_ty != self.tcx.types.bool {
self.fail(
location,
format!(
- "encountered non-boolean condition of type {} in `Assert` terminator",
- cond_ty
+ "encountered non-boolean condition of type {cond_ty} in `Assert` terminator"
),
);
}
- self.check_edge(location, *target, EdgeKind::Normal);
- self.check_unwind_edge(location, *unwind);
- }
- TerminatorKind::Yield { resume, drop, .. } => {
- if self.body.generator.is_none() {
- self.fail(location, "`Yield` cannot appear outside generator bodies");
- }
- if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
- self.fail(location, "`Yield` should have been replaced by generator lowering");
- }
- self.check_edge(location, *resume, EdgeKind::Normal);
- if let Some(drop) = drop {
- self.check_edge(location, *drop, EdgeKind::Normal);
- }
}
- TerminatorKind::FalseEdge { real_target, imaginary_target } => {
- if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
- self.fail(
- location,
- "`FalseEdge` should have been removed after drop elaboration",
- );
- }
- self.check_edge(location, *real_target, EdgeKind::Normal);
- self.check_edge(location, *imaginary_target, EdgeKind::Normal);
- }
- TerminatorKind::FalseUnwind { real_target, unwind } => {
- if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
- self.fail(
- location,
- "`FalseUnwind` should have been removed after drop elaboration",
- );
- }
- self.check_edge(location, *real_target, EdgeKind::Normal);
- self.check_unwind_edge(location, *unwind);
- }
- TerminatorKind::InlineAsm { destination, unwind, .. } => {
- if let Some(destination) = destination {
- self.check_edge(location, *destination, EdgeKind::Normal);
- }
- self.check_unwind_edge(location, *unwind);
- }
- TerminatorKind::GeneratorDrop => {
- if self.body.generator.is_none() {
- self.fail(location, "`GeneratorDrop` cannot appear outside generator bodies");
- }
- if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
- self.fail(
- location,
- "`GeneratorDrop` should have been replaced by generator lowering",
- );
- }
- }
- TerminatorKind::Resume | TerminatorKind::Terminate => {
- let bb = location.block;
- if !self.body.basic_blocks[bb].is_cleanup {
- self.fail(
- location,
- "Cannot `Resume` or `Terminate` from non-cleanup basic block",
- )
- }
- }
- TerminatorKind::Return => {
- let bb = location.block;
- if self.body.basic_blocks[bb].is_cleanup {
- self.fail(location, "Cannot `Return` from cleanup basic block")
- }
- }
- TerminatorKind::Unreachable => {}
+ TerminatorKind::Goto { .. }
+ | TerminatorKind::Drop { .. }
+ | TerminatorKind::Yield { .. }
+ | TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. }
+ | TerminatorKind::InlineAsm { .. }
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::Resume
+ | TerminatorKind::Terminate
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable => {}
}
self.super_terminator(terminator, location);
}
-
- fn visit_source_scope(&mut self, scope: SourceScope) {
- if self.body.source_scopes.get(scope).is_none() {
- self.tcx.sess.diagnostic().delay_span_bug(
- self.body.span,
- format!(
- "broken MIR in {:?} ({}):\ninvalid source scope {:?}",
- self.body.source.instance, self.when, scope,
- ),
- );
- }
- }
}