summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_const_eval/src/transform
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
commit218caa410aa38c29984be31a5229b9fa717560ee (patch)
treec54bd55eeb6e4c508940a30e94c0032fbd45d677 /compiler/rustc_const_eval/src/transform
parentReleasing progress-linux version 1.67.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-218caa410aa38c29984be31a5229b9fa717560ee.tar.xz
rustc-218caa410aa38c29984be31a5229b9fa717560ee.zip
Merging upstream version 1.68.2+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.rs66
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/mod.rs15
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs10
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs116
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs134
6 files changed, 228 insertions, 115 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 54213d55a..79f1737e3 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -242,7 +242,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
// impl trait is gone in MIR, so check the return type of a const fn by its signature
// instead of the type of the return place.
self.span = body.local_decls[RETURN_PLACE].source_info.span;
- let return_ty = tcx.fn_sig(def_id).output();
+ let return_ty = self.ccx.fn_sig().output();
self.check_local_or_return_ty(return_ty.skip_binder(), RETURN_PLACE);
}
@@ -442,7 +442,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
self.super_rvalue(rvalue, location);
- match *rvalue {
+ match rvalue {
Rvalue::ThreadLocalRef(_) => self.check_op(ops::ThreadLocalAccess),
Rvalue::Use(_)
@@ -451,18 +451,15 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
| Rvalue::Discriminant(..)
| Rvalue::Len(_) => {}
- Rvalue::Aggregate(ref kind, ..) => {
- if let AggregateKind::Generator(def_id, ..) = kind.as_ref() {
- if let Some(generator_kind) = self.tcx.generator_kind(def_id.to_def_id()) {
- if matches!(generator_kind, hir::GeneratorKind::Async(..)) {
- self.check_op(ops::Generator(generator_kind));
- }
- }
+ Rvalue::Aggregate(kind, ..) => {
+ if let AggregateKind::Generator(def_id, ..) = kind.as_ref()
+ && let Some(generator_kind @ hir::GeneratorKind::Async(..)) = self.tcx.generator_kind(def_id.to_def_id())
+ {
+ self.check_op(ops::Generator(generator_kind));
}
}
- Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place)
- | Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place) => {
+ Rvalue::Ref(_, kind @ (BorrowKind::Mut { .. } | BorrowKind::Unique), place) => {
let ty = place.ty(self.body, self.tcx).ty;
let is_allowed = match ty.kind() {
// Inside a `static mut`, `&mut [...]` is allowed.
@@ -491,12 +488,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
}
- Rvalue::AddressOf(Mutability::Mut, ref place) => {
+ Rvalue::AddressOf(Mutability::Mut, place) => {
self.check_mut_borrow(place.local, hir::BorrowKind::Raw)
}
- Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, ref place)
- | Rvalue::AddressOf(Mutability::Not, ref place) => {
+ Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, place)
+ | Rvalue::AddressOf(Mutability::Not, place) => {
let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>(
&self.ccx,
&mut |local| self.qualifs.has_mut_interior(self.ccx, local, location),
@@ -564,7 +561,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => {}
Rvalue::ShallowInitBox(_, _) => {}
- Rvalue::UnaryOp(_, ref operand) => {
+ Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(self.body, self.tcx);
if is_int_bool_or_char(ty) {
// Int, bool, and char operations are fine.
@@ -575,8 +572,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
}
- Rvalue::BinaryOp(op, box (ref lhs, ref rhs))
- | Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs)) => {
+ Rvalue::BinaryOp(op, box (lhs, rhs))
+ | Rvalue::CheckedBinaryOp(op, box (lhs, rhs)) => {
let lhs_ty = lhs.ty(self.body, self.tcx);
let rhs_ty = rhs.ty(self.body, self.tcx);
@@ -585,13 +582,16 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
} else if lhs_ty.is_fn_ptr() || lhs_ty.is_unsafe_ptr() {
assert_eq!(lhs_ty, rhs_ty);
assert!(
- op == BinOp::Eq
- || op == BinOp::Ne
- || op == BinOp::Le
- || op == BinOp::Lt
- || op == BinOp::Ge
- || op == BinOp::Gt
- || op == BinOp::Offset
+ matches!(
+ op,
+ BinOp::Eq
+ | BinOp::Ne
+ | BinOp::Le
+ | BinOp::Lt
+ | BinOp::Ge
+ | BinOp::Gt
+ | BinOp::Offset
+ )
);
self.check_op(ops::RawPtrComparison);
@@ -730,6 +730,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
substs,
span: *fn_span,
from_hir_call: *from_hir_call,
+ feature: Some(sym::const_trait_impl),
});
return;
}
@@ -782,6 +783,20 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
);
return;
}
+ Ok(Some(ImplSource::Closure(data))) => {
+ if !tcx.is_const_fn_raw(data.closure_def_id) {
+ self.check_op(ops::FnCallNonConst {
+ caller,
+ callee,
+ substs,
+ span: *fn_span,
+ from_hir_call: *from_hir_call,
+ feature: None,
+ });
+
+ return;
+ }
+ }
Ok(Some(ImplSource::UserDefined(data))) => {
let callee_name = tcx.item_name(callee);
if let Some(&did) = tcx
@@ -802,6 +817,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
substs,
span: *fn_span,
from_hir_call: *from_hir_call,
+ feature: None,
});
return;
}
@@ -844,6 +860,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
substs,
span: *fn_span,
from_hir_call: *from_hir_call,
+ feature: None,
});
return;
}
@@ -903,6 +920,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
substs,
span: *fn_span,
from_hir_call: *from_hir_call,
+ feature: None,
});
return;
}
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 655ec345e..54868e418 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs
@@ -8,7 +8,7 @@ use rustc_attr as attr;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::mir;
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, PolyFnSig, TyCtxt};
use rustc_span::Symbol;
pub use self::qualifs::Qualif;
@@ -64,6 +64,17 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> {
fn is_async(&self) -> bool {
self.tcx.asyncness(self.def_id()).is_async()
}
+
+ 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);
+ let ty::Closure(_, substs) = ty.kind() else { bug!("type_of closure not ty::Closure") };
+ substs.as_closure().sig()
+ } else {
+ self.tcx.fn_sig(did)
+ }
+ }
}
pub fn rustc_allow_const_fn_unstable(
@@ -115,7 +126,7 @@ fn is_parent_const_stable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
let local_def_id = def_id.expect_local();
let hir_id = tcx.local_def_id_to_hir_id(local_def_id);
- let Some(parent) = tcx.hir().find_parent_node(hir_id) else { return false };
+ let Some(parent) = tcx.hir().opt_parent_id(hir_id) else { return false };
let parent_def = tcx.hir().get(parent);
if !matches!(
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 b19d270e6..0cb5d2ff8 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -111,6 +111,7 @@ pub struct FnCallNonConst<'tcx> {
pub substs: SubstsRef<'tcx>,
pub span: Span,
pub from_hir_call: bool,
+ pub feature: Option<Symbol>,
}
impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
@@ -119,7 +120,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
ccx: &ConstCx<'_, 'tcx>,
_: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- let FnCallNonConst { caller, callee, substs, span, from_hir_call } = *self;
+ let FnCallNonConst { caller, callee, substs, span, from_hir_call, feature } = *self;
let ConstCx { tcx, param_env, .. } = *ccx;
let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
@@ -318,6 +319,13 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
ccx.const_kind(),
));
+ if let Some(feature) = feature && ccx.tcx.sess.is_nightly_build() {
+ err.help(&format!(
+ "add `#![feature({})]` to the crate attributes to enable",
+ feature,
+ ));
+ }
+
if let ConstContext::Static(_) = ccx.const_kind() {
err.note("consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell");
}
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 d4570c598..cf4e875c9 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
@@ -95,7 +95,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
}
// Drop elaboration is not precise enough to accept code like
- // `src/test/ui/consts/control-flow/drop-pass.rs`; e.g., when an `Option<Vec<T>>` is
+ // `tests/ui/consts/control-flow/drop-pass.rs`; e.g., when an `Option<Vec<T>>` is
// initialized with `None` and never changed, it still emits drop glue.
// Hence we additionally check the qualifs here to allow more code to pass.
if self.qualifs.needs_non_const_drop(self.ccx, dropped_place.local, location) {
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 6777fae74..fae6117f8 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -133,7 +133,7 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
}
_ => { /* mark as unpromotable below */ }
}
- } else if let TempState::Defined { ref mut uses, .. } = *temp {
+ } else if let TempState::Defined { uses, .. } = temp {
// We always allow borrows, even mutable ones, as we need
// to promote mutable borrows of some ZSTs e.g., `&mut []`.
let allowed_use = match context {
@@ -216,12 +216,6 @@ impl<'tcx> Validator<'_, 'tcx> {
return Err(Unpromotable);
}
- // We cannot promote things that need dropping, since the promoted value
- // would not get dropped.
- if self.qualif_local::<qualifs::NeedsDrop>(place.local) {
- return Err(Unpromotable);
- }
-
Ok(())
}
_ => bug!(),
@@ -262,13 +256,17 @@ impl<'tcx> Validator<'_, 'tcx> {
}
}
} else {
- let span = self.body.local_decls[local].source_info.span;
- span_bug!(span, "{:?} not promotable, qualif_local shouldn't have been called", local);
+ false
}
}
fn validate_local(&mut self, local: Local) -> Result<(), Unpromotable> {
if let TempState::Defined { location: loc, uses, valid } = self.temps[local] {
+ // We cannot promote things that need dropping, since the promoted value
+ // would not get dropped.
+ if self.qualif_local::<qualifs::NeedsDrop>(local) {
+ return Err(Unpromotable);
+ }
valid.or_else(|_| {
let ok = {
let block = &self.body[loc.block];
@@ -750,7 +748,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
if loc.statement_index < num_stmts {
let (mut rvalue, source_info) = {
let statement = &mut self.source[loc.block].statements[loc.statement_index];
- let StatementKind::Assign(box (_, ref mut rhs)) = statement.kind else {
+ let StatementKind::Assign(box (_, rhs)) = &mut statement.kind else {
span_bug!(
statement.source_info.span,
"{:?} is not an assignment",
@@ -780,9 +778,9 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
self.source[loc.block].terminator().clone()
} else {
let terminator = self.source[loc.block].terminator_mut();
- let target = match terminator.kind {
- TerminatorKind::Call { target: Some(target), .. } => target,
- ref kind => {
+ let target = match &terminator.kind {
+ TerminatorKind::Call { target: Some(target), .. } => *target,
+ kind => {
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
}
};
@@ -816,7 +814,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
..terminator
};
}
- ref kind => {
+ kind => {
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
}
};
@@ -849,54 +847,50 @@ 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];
- match statement.kind {
- StatementKind::Assign(box (
- _,
- Rvalue::Ref(ref mut region, borrow_kind, ref mut place),
- )) => {
- // Use the underlying local for this (necessarily interior) borrow.
- let ty = local_decls[place.local].ty;
- let span = statement.source_info.span;
-
- let ref_ty = tcx.mk_ref(
- tcx.lifetimes.re_erased,
- ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() },
- );
+ let StatementKind::Assign(box (_, Rvalue::Ref(region, borrow_kind, place))) = &mut statement.kind else {
+ bug!()
+ };
- *region = tcx.lifetimes.re_erased;
-
- let mut projection = vec![PlaceElem::Deref];
- projection.extend(place.projection);
- place.projection = tcx.intern_place_elems(&projection);
-
- // Create a temp to hold the promoted reference.
- // This is because `*r` requires `r` to be a local,
- // otherwise we would use the `promoted` directly.
- let mut promoted_ref = LocalDecl::new(ref_ty, span);
- promoted_ref.source_info = statement.source_info;
- let promoted_ref = local_decls.push(promoted_ref);
- assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
-
- let promoted_ref_statement = Statement {
- source_info: statement.source_info,
- kind: StatementKind::Assign(Box::new((
- Place::from(promoted_ref),
- Rvalue::Use(promoted_operand(ref_ty, span)),
- ))),
- };
- self.extra_statements.push((loc, promoted_ref_statement));
-
- Rvalue::Ref(
- tcx.lifetimes.re_erased,
- borrow_kind,
- Place {
- local: mem::replace(&mut place.local, promoted_ref),
- projection: List::empty(),
- },
- )
- }
- _ => bug!(),
- }
+ // Use the underlying local for this (necessarily interior) borrow.
+ let ty = local_decls[place.local].ty;
+ let span = statement.source_info.span;
+
+ let ref_ty = tcx.mk_ref(
+ tcx.lifetimes.re_erased,
+ ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() },
+ );
+
+ *region = tcx.lifetimes.re_erased;
+
+ let mut projection = vec![PlaceElem::Deref];
+ projection.extend(place.projection);
+ place.projection = tcx.intern_place_elems(&projection);
+
+ // Create a temp to hold the promoted reference.
+ // This is because `*r` requires `r` to be a local,
+ // otherwise we would use the `promoted` directly.
+ let mut promoted_ref = LocalDecl::new(ref_ty, span);
+ promoted_ref.source_info = statement.source_info;
+ let promoted_ref = local_decls.push(promoted_ref);
+ assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
+
+ let promoted_ref_statement = Statement {
+ source_info: statement.source_info,
+ kind: StatementKind::Assign(Box::new((
+ Place::from(promoted_ref),
+ Rvalue::Use(promoted_operand(ref_ty, span)),
+ ))),
+ };
+ self.extra_statements.push((loc, promoted_ref_statement));
+
+ Rvalue::Ref(
+ tcx.lifetimes.re_erased,
+ *borrow_kind,
+ Place {
+ local: mem::replace(&mut place.local, promoted_ref),
+ projection: List::empty(),
+ },
+ )
};
assert_eq!(self.new_block(), START_BLOCK);
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 5c9263dc5..dd168a9ac 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -1,7 +1,8 @@
//! Validates the MIR to ensure that invariants are upheld.
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
use rustc_infer::traits::Reveal;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
@@ -9,8 +10,8 @@ use rustc_middle::mir::visit::{PlaceContext, Visitor};
use rustc_middle::mir::{
traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping,
Local, Location, MirPass, MirPhase, NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef,
- ProjectionElem, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind, Terminator,
- TerminatorKind, UnOp, START_BLOCK,
+ ProjectionElem, RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind,
+ Terminator, TerminatorKind, UnOp, START_BLOCK,
};
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitable};
use rustc_mir_dataflow::impls::MaybeStorageLive;
@@ -18,7 +19,7 @@ use rustc_mir_dataflow::storage::always_storage_live_locals;
use rustc_mir_dataflow::{Analysis, ResultsCursor};
use rustc_target::abi::{Size, VariantIdx};
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum EdgeKind {
Unwind,
Normal,
@@ -52,23 +53,25 @@ impl<'tcx> MirPass<'tcx> for Validator {
};
let always_live_locals = always_storage_live_locals(body);
- let storage_liveness = MaybeStorageLive::new(always_live_locals)
+ let storage_liveness = MaybeStorageLive::new(std::borrow::Cow::Owned(always_live_locals))
.into_engine(tcx, body)
.iterate_to_fixpoint()
.into_results_cursor(body);
- TypeChecker {
+ let mut checker = TypeChecker {
when: &self.when,
body,
tcx,
param_env,
mir_phase,
+ unwind_edge_count: 0,
reachable_blocks: traversal::reachable_as_bitset(body),
storage_liveness,
place_cache: Vec::new(),
value_cache: Vec::new(),
- }
- .visit_body(body);
+ };
+ checker.visit_body(body);
+ checker.check_cleanup_control_flow();
}
}
@@ -78,8 +81,9 @@ struct TypeChecker<'a, '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>,
+ storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>,
place_cache: Vec<PlaceRef<'tcx>>,
value_cache: Vec<u128>,
}
@@ -102,7 +106,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
);
}
- fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
+ fn check_edge(&mut self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
if bb == START_BLOCK {
self.fail(location, "start block must not have predecessors")
}
@@ -111,10 +115,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
match (src.is_cleanup, bb.is_cleanup, edge_kind) {
// Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
(false, false, EdgeKind::Normal)
- // Non-cleanup blocks can jump to cleanup blocks along unwind edges
- | (false, true, EdgeKind::Unwind)
// Cleanup blocks can jump to cleanup blocks along non-unwind edges
| (true, true, EdgeKind::Normal) => {}
+ // Non-cleanup blocks can jump to cleanup blocks along unwind edges
+ (false, true, EdgeKind::Unwind) => {
+ self.unwind_edge_count += 1;
+ }
// All other jumps are invalid
_ => {
self.fail(
@@ -134,6 +140,88 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
+ fn check_cleanup_control_flow(&self) {
+ if self.unwind_edge_count <= 1 {
+ return;
+ }
+ let doms = self.body.basic_blocks.dominators();
+ let mut post_contract_node = FxHashMap::default();
+ // Reusing the allocation across invocations of the closure
+ let mut dom_path = vec![];
+ let mut get_post_contract_node = |mut bb| {
+ let root = loop {
+ if let Some(root) = post_contract_node.get(&bb) {
+ break *root;
+ }
+ let parent = doms.immediate_dominator(bb);
+ dom_path.push(bb);
+ if !self.body.basic_blocks[parent].is_cleanup {
+ break bb;
+ }
+ bb = parent;
+ };
+ for bb in dom_path.drain(..) {
+ post_contract_node.insert(bb, root);
+ }
+ root
+ };
+
+ let mut parent = IndexVec::from_elem(None, &self.body.basic_blocks);
+ for (bb, bb_data) in self.body.basic_blocks.iter_enumerated() {
+ if !bb_data.is_cleanup || !self.reachable_blocks.contains(bb) {
+ continue;
+ }
+ let bb = get_post_contract_node(bb);
+ for s in bb_data.terminator().successors() {
+ let s = get_post_contract_node(s);
+ if s == bb {
+ continue;
+ }
+ let parent = &mut parent[bb];
+ match parent {
+ None => {
+ *parent = Some(s);
+ }
+ Some(e) if *e == s => (),
+ Some(e) => self.fail(
+ Location { block: bb, statement_index: 0 },
+ format!(
+ "Cleanup control flow violation: The blocks dominated by {:?} have edges to both {:?} and {:?}",
+ bb,
+ s,
+ *e
+ )
+ ),
+ }
+ }
+ }
+
+ // Check for cycles
+ let mut stack = FxHashSet::default();
+ for i in 0..parent.len() {
+ let mut bb = BasicBlock::from_usize(i);
+ stack.clear();
+ stack.insert(bb);
+ loop {
+ 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,
+ ),
+ );
+ break;
+ }
+ bb = parent;
+ }
+ }
+ }
+
/// 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 {
@@ -241,7 +329,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
};
let kind = match parent_ty.ty.kind() {
- &ty::Opaque(def_id, substs) => {
+ &ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
self.tcx.bound_type_of(def_id).subst(self.tcx, substs).kind()
}
kind => kind,
@@ -652,7 +740,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
}
let pty = place.ty(&self.body.local_decls, self.tcx).ty.kind();
- if !matches!(pty, ty::Adt(..) | ty::Generator(..) | ty::Opaque(..)) {
+ if !matches!(pty, ty::Adt(..) | ty::Generator(..) | ty::Alias(ty::Opaque, ..)) {
self.fail(
location,
format!(
@@ -667,10 +755,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.fail(location, "`Deinit`is not allowed until deaggregation");
}
}
- StatementKind::Retag(_, _) => {
+ 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 *kind == RetagKind::Raw || *kind == RetagKind::TwoPhase {
+ self.fail(location, format!("explicit `{:?}` is forbidden", kind));
+ }
}
StatementKind::StorageLive(..)
| StatementKind::StorageDead(..)
@@ -686,17 +777,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
TerminatorKind::Goto { target } => {
self.check_edge(location, *target, EdgeKind::Normal);
}
- TerminatorKind::SwitchInt { targets, switch_ty, discr } => {
- let ty = discr.ty(&self.body.local_decls, self.tcx);
- if ty != *switch_ty {
- self.fail(
- location,
- format!(
- "encountered `SwitchInt` terminator with type mismatch: {:?} != {:?}",
- ty, switch_ty,
- ),
- );
- }
+ TerminatorKind::SwitchInt { targets, discr } => {
+ let switch_ty = discr.ty(&self.body.local_decls, self.tcx);
let target_width = self.tcx.sess.target.pointer_width;