diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b55ab229811b..3680136d89ff 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -323,17 +323,6 @@ jobs:
NO_DEBUG_ASSERTIONS: 1
NO_OVERFLOW_CHECKS: 1
os: macos-13
- - name: dist-x86_64-apple-alt
- env:
- SCRIPT: "./x.py dist bootstrap --include-default-paths"
- RUST_CONFIGURE_ARGS: "--enable-extended --enable-profiler --set rust.jemalloc --set llvm.ninja=false"
- RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
- MACOSX_DEPLOYMENT_TARGET: 10.7
- SELECT_XCODE: /Applications/Xcode_13.4.1.app
- NO_LLVM_ASSERTIONS: 1
- NO_DEBUG_ASSERTIONS: 1
- NO_OVERFLOW_CHECKS: 1
- os: macos-13
- name: x86_64-apple-1
env:
SCRIPT: "./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc --skip tests/run-make-fulldeps"
diff --git a/Cargo.lock b/Cargo.lock
index 8d707067bb92..266f34e69edb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -167,9 +167,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.71"
+version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
dependencies = [
"backtrace",
]
@@ -5126,9 +5126,9 @@ checksum = "aac81b6fd6beb5884b0cf3321b8117e6e5d47ecb6fc89f414cfdcca8b2fe2dd8"
[[package]]
name = "thiserror"
-version = "1.0.40"
+version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
+checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
dependencies = [
"thiserror-impl",
]
@@ -5155,9 +5155,9 @@ dependencies = [
[[package]]
name = "thiserror-impl"
-version = "1.0.40"
+version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
+checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
dependencies = [
"proc-macro2",
"quote",
diff --git a/RELEASES.md b/RELEASES.md
index e8c79c573f97..722f7e5dd083 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,3 +1,108 @@
+Version 1.72.0 (2023-08-24)
+==========================
+
+
+
+Language
+--------
+
+- [Replace const eval limit by a lint and add an exponential backoff warning](https://github.com/rust-lang/rust/pull/103877/)
+- [expand: Change how `#![cfg(FALSE)]` behaves on crate root](https://github.com/rust-lang/rust/pull/110141/)
+- [Stabilize inline asm for LoongArch64](https://github.com/rust-lang/rust/pull/111235/)
+- [Uplift `clippy::undropped_manually_drops` lint](https://github.com/rust-lang/rust/pull/111530/)
+- [Uplift `clippy::invalid_utf8_in_unchecked` lint](https://github.com/rust-lang/rust/pull/111543/)
+- [Uplift `clippy::cast_ref_to_mut` lint](https://github.com/rust-lang/rust/pull/111567/)
+- [Uplift `clippy::cmp_nan` lint](https://github.com/rust-lang/rust/pull/111818/)
+- [resolve: Remove artificial import ambiguity errors](https://github.com/rust-lang/rust/pull/112086/)
+- [Don't require associated types with Self: Sized bounds in `dyn Trait` objects](https://github.com/rust-lang/rust/pull/112319/)
+
+
+
+Compiler
+--------
+
+- [Remember names of `cfg`-ed out items to mention them in diagnostics](https://github.com/rust-lang/rust/pull/109005/)
+- [Support for native WASM exceptions](https://github.com/rust-lang/rust/pull/111322/)
+- [Add support for NetBSD/aarch64-be (big-endian arm64).](https://github.com/rust-lang/rust/pull/111326/)
+- [Write to stdout if `-` is given as output file](https://github.com/rust-lang/rust/pull/111626/)
+- [Force all native libraries to be statically linked when linking a static binary](https://github.com/rust-lang/rust/pull/111698/)
+- [Add Tier 3 support for `loongarch64-unknown-none*`](https://github.com/rust-lang/rust/pull/112310/)
+- [Prevent `.eh_frame` from being emitted for `-C panic=abort`](https://github.com/rust-lang/rust/pull/112403/)
+- [Support 128-bit enum variant in debuginfo codegen](https://github.com/rust-lang/rust/pull/112474/)
+- [compiler: update solaris/illumos to enable tsan support.](https://github.com/rust-lang/rust/pull/112039/)
+
+Refer to Rust's [platform support page][platform-support-doc]
+for more information on Rust's tiered platform support.
+
+
+
+Libraries
+---------
+
+- [Document memory orderings of `thread::{park, unpark}`](https://github.com/rust-lang/rust/pull/99587/)
+- [io: soften ‘at most one write attempt’ requirement in io::Write::write](https://github.com/rust-lang/rust/pull/107200/)
+- [Specify behavior of HashSet::insert](https://github.com/rust-lang/rust/pull/107619/)
+- [Relax implicit `T: Sized` bounds on `BufReader`, `BufWriter` and `LineWriter`](https://github.com/rust-lang/rust/pull/111074/)
+- [Update runtime guarantee for `select_nth_unstable`](https://github.com/rust-lang/rust/pull/111974/)
+- [Return `Ok` on kill if process has already exited](https://github.com/rust-lang/rust/pull/112594/)
+- [Implement PartialOrd for `Vec`s over different allocators](https://github.com/rust-lang/rust/pull/112632/)
+- [Use 128 bits for TypeId hash](https://github.com/rust-lang/rust/pull/109953/)
+- [Don't drain-on-drop in DrainFilter impls of various collections.](https://github.com/rust-lang/rust/pull/104455/)
+- [Make `{Arc,Rc,Weak}::ptr_eq` ignore pointer metadata](https://github.com/rust-lang/rust/pull/106450/)
+
+
+
+Rustdoc
+-------
+
+- [Allow whitespace as path separator like double colon](https://github.com/rust-lang/rust/pull/108537/)
+- [Add search result item types after their name](https://github.com/rust-lang/rust/pull/110688/)
+- [Search for slices and arrays by type with `[]`](https://github.com/rust-lang/rust/pull/111958/)
+- [Clean up type unification and "unboxing"](https://github.com/rust-lang/rust/pull/112233/)
+
+
+
+Stabilized APIs
+---------------
+
+- [`impl Sync for mpsc::Sender`](https://doc.rust-lang.org/nightly/std/sync/mpsc/struct.Sender.html#impl-Sync-for-Sender%3CT%3E)
+- [`impl TryFrom<&OsStr> for &str`](https://doc.rust-lang.org/nightly/std/primitive.str.html#impl-TryFrom%3C%26'a+OsStr%3E-for-%26'a+str)
+- [`String::leak`](https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.leak)
+
+These APIs are now stable in const contexts:
+
+- [`CStr::from_bytes_with_nul`](https://doc.rust-lang.org/nightly/std/ffi/struct.CStr.html#method.from_bytes_with_nul)
+- [`CStr::to_bytes`](https://doc.rust-lang.org/nightly/std/ffi/struct.CStr.html#method.from_bytes_with_nul)
+- [`CStr::to_bytes_with_nul`](https://doc.rust-lang.org/nightly/std/ffi/struct.CStr.html#method.from_bytes_with_nul)
+- [`CStr::to_str`](https://doc.rust-lang.org/nightly/std/ffi/struct.CStr.html#method.from_bytes_with_nul)
+
+
+
+Cargo
+-----
+
+- Enable `-Zdoctest-in-workspace` by default. When running each documentation
+ test, the working directory is set to the root directory of the package the
+ test belongs to.
+ [docs](https://doc.rust-lang.org/nightly/cargo/commands/cargo-test.html#working-directory-of-tests)
+ [#12221](https://github.com/rust-lang/cargo/pull/12221)
+ [#12288](https://github.com/rust-lang/cargo/pull/12288)
+- Add support of the "default" keyword to reset previously set `build.jobs`
+ parallelism back to the default.
+ [#12222](https://github.com/rust-lang/cargo/pull/12222)
+
+
+
+Compatibility Notes
+-------------------
+
+- [Alter `Display` for `Ipv6Addr` for IPv4-compatible addresses](https://github.com/rust-lang/rust/pull/112606/)
+- Cargo changed feature name validation check to a hard error. The warning was
+ added in Rust 1.49. These extended characters aren't allowed on crates.io, so
+ this should only impact users of other registries, or people who don't publish
+ to a registry.
+ [#12291](https://github.com/rust-lang/cargo/pull/12291)
+
Version 1.71.0 (2023-07-13)
==========================
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index f4900ece1cc5..e45b7c154faf 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -11,6 +11,7 @@
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
test(no_crate_inject, attr(deny(warnings)))
)]
+#![feature(core_intrinsics)]
#![feature(dropck_eyepatch)]
#![feature(new_uninit)]
#![feature(maybe_uninit_slice)]
@@ -30,11 +31,11 @@ use smallvec::SmallVec;
use std::alloc::Layout;
use std::cell::{Cell, RefCell};
-use std::cmp;
use std::marker::PhantomData;
use std::mem::{self, MaybeUninit};
use std::ptr::{self, NonNull};
use std::slice;
+use std::{cmp, intrinsics};
#[inline(never)]
#[cold]
@@ -363,6 +364,22 @@ unsafe impl<#[may_dangle] T> Drop for TypedArena {
unsafe impl Send for TypedArena {}
+#[inline(always)]
+fn align_down(val: usize, align: usize) -> usize {
+ debug_assert!(align.is_power_of_two());
+ val & !(align - 1)
+}
+
+#[inline(always)]
+fn align_up(val: usize, align: usize) -> usize {
+ debug_assert!(align.is_power_of_two());
+ (val + align - 1) & !(align - 1)
+}
+
+// Pointer alignment is common in compiler types, so keep `DroplessArena` aligned to them
+// to optimize away alignment code.
+const DROPLESS_ALIGNMENT: usize = mem::align_of::();
+
/// An arena that can hold objects of multiple different types that impl `Copy`
/// and/or satisfy `!mem::needs_drop`.
pub struct DroplessArena {
@@ -375,6 +392,8 @@ pub struct DroplessArena {
/// start. (This is slightly simpler and faster than allocating upwards,
/// see .)
/// When this pointer crosses the start pointer, a new chunk is allocated.
+ ///
+ /// This is kept aligned to DROPLESS_ALIGNMENT.
end: Cell<*mut u8>,
/// A vector of arena chunks.
@@ -395,9 +414,11 @@ impl Default for DroplessArena {
}
impl DroplessArena {
- #[inline(never)]
- #[cold]
- fn grow(&self, additional: usize) {
+ fn grow(&self, layout: Layout) {
+ // Add some padding so we can align `self.end` while
+ // stilling fitting in a `layout` allocation.
+ let additional = layout.size() + cmp::max(DROPLESS_ALIGNMENT, layout.align()) - 1;
+
unsafe {
let mut chunks = self.chunks.borrow_mut();
let mut new_cap;
@@ -416,13 +437,35 @@ impl DroplessArena {
// Also ensure that this chunk can fit `additional`.
new_cap = cmp::max(additional, new_cap);
- let mut chunk = ArenaChunk::new(new_cap);
+ let mut chunk = ArenaChunk::new(align_up(new_cap, PAGE));
self.start.set(chunk.start());
- self.end.set(chunk.end());
+
+ // Align the end to DROPLESS_ALIGNMENT
+ let end = align_down(chunk.end().addr(), DROPLESS_ALIGNMENT);
+
+ // Make sure we don't go past `start`. This should not happen since the allocation
+ // should be at least DROPLESS_ALIGNMENT - 1 bytes.
+ debug_assert!(chunk.start().addr() <= end);
+
+ self.end.set(chunk.end().with_addr(end));
+
chunks.push(chunk);
}
}
+ #[inline(never)]
+ #[cold]
+ fn grow_and_alloc_raw(&self, layout: Layout) -> *mut u8 {
+ self.grow(layout);
+ self.alloc_raw_without_grow(layout).unwrap()
+ }
+
+ #[inline(never)]
+ #[cold]
+ fn grow_and_alloc(&self) -> *mut u8 {
+ self.grow_and_alloc_raw(Layout::new::())
+ }
+
/// Allocates a byte slice with specified layout from the current memory
/// chunk. Returns `None` if there is no free space left to satisfy the
/// request.
@@ -432,12 +475,17 @@ impl DroplessArena {
let old_end = self.end.get();
let end = old_end.addr();
- let align = layout.align();
- let bytes = layout.size();
+ // Align allocated bytes so that `self.end` stays aligned to DROPLESS_ALIGNMENT
+ let bytes = align_up(layout.size(), DROPLESS_ALIGNMENT);
- let new_end = end.checked_sub(bytes)? & !(align - 1);
+ // Tell LLVM that `end` is aligned to DROPLESS_ALIGNMENT
+ unsafe { intrinsics::assume(end == align_down(end, DROPLESS_ALIGNMENT)) };
+
+ let new_end = align_down(end.checked_sub(bytes)?, layout.align());
if start <= new_end {
let new_end = old_end.with_addr(new_end);
+ // `new_end` is aligned to DROPLESS_ALIGNMENT as `align_down` preserves alignment
+ // as both `end` and `bytes` are already aligned to DROPLESS_ALIGNMENT.
self.end.set(new_end);
Some(new_end)
} else {
@@ -448,21 +496,26 @@ impl DroplessArena {
#[inline]
pub fn alloc_raw(&self, layout: Layout) -> *mut u8 {
assert!(layout.size() != 0);
- loop {
- if let Some(a) = self.alloc_raw_without_grow(layout) {
- break a;
- }
- // No free space left. Allocate a new chunk to satisfy the request.
- // On failure the grow will panic or abort.
- self.grow(layout.size());
+ if let Some(a) = self.alloc_raw_without_grow(layout) {
+ return a;
}
+ // No free space left. Allocate a new chunk to satisfy the request.
+ // On failure the grow will panic or abort.
+ self.grow_and_alloc_raw(layout)
}
#[inline]
pub fn alloc(&self, object: T) -> &mut T {
assert!(!mem::needs_drop::());
+ assert!(mem::size_of::() != 0);
- let mem = self.alloc_raw(Layout::for_value::(&object)) as *mut T;
+ let mem = if let Some(a) = self.alloc_raw_without_grow(Layout::for_value::(&object)) {
+ a
+ } else {
+ // No free space left. Allocate a new chunk to satisfy the request.
+ // On failure the grow will panic or abort.
+ self.grow_and_alloc::()
+ } as *mut T;
unsafe {
// Write into uninitialized memory.
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index bae3979fbf9f..48e9b180b74f 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -13,6 +13,7 @@ use crate::tokenstream::*;
use crate::{ast::*, StaticItem};
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
+use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::sync::Lrc;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::Ident;
@@ -1369,7 +1370,7 @@ pub fn noop_visit_expr(
ExprKind::If(cond, tr, fl) => {
vis.visit_expr(cond);
vis.visit_block(tr);
- visit_opt(fl, |fl| vis.visit_expr(fl));
+ visit_opt(fl, |fl| ensure_sufficient_stack(|| vis.visit_expr(fl)));
}
ExprKind::While(cond, body, label) => {
vis.visit_expr(cond);
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 068b255e9f28..58ce73047bce 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -150,6 +150,8 @@ pub fn print_crate<'a>(
/// and also addresses some specific regressions described in #63896 and #73345.
fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
if let TokenTree::Token(token, _) = prev {
+ // No space after these tokens, e.g. `x.y`, `$e`
+ // (The carets point to `prev`.) ^ ^
if matches!(token.kind, token::Dot | token::Dollar) {
return false;
}
@@ -158,10 +160,19 @@ fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
}
}
match tt {
+ // No space before these tokens, e.g. `foo,`, `println!`, `x.y`
+ // (The carets point to `token`.) ^ ^ ^
+ //
+ // FIXME: having `Not` here works well for macro invocations like
+ // `println!()`, but is bad when `!` means "logical not" or "the never
+ // type", where the lack of space causes ugliness like this:
+ // `Fn() ->!`, `x =! y`, `if! x { f(); }`.
TokenTree::Token(token, _) => !matches!(token.kind, token::Comma | token::Not | token::Dot),
+ // No space before parentheses if preceded by these tokens, e.g. `foo(...)`
TokenTree::Delimited(_, Delimiter::Parenthesis, _) => {
!matches!(prev, TokenTree::Token(Token { kind: token::Ident(..), .. }, _))
}
+ // No space before brackets if preceded by these tokens, e.g. `#[...]`
TokenTree::Delimited(_, Delimiter::Bracket, _) => {
!matches!(prev, TokenTree::Token(Token { kind: token::Pound, .. }, _))
}
diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs
index d257145373f7..becfa535a59c 100644
--- a/compiler/rustc_borrowck/src/consumers.rs
+++ b/compiler/rustc_borrowck/src/consumers.rs
@@ -30,7 +30,7 @@ pub use super::{
/// will be retrieved.
#[derive(Debug, Copy, Clone)]
pub enum ConsumerOptions {
- /// Retrieve the [`Body`] along with the [`BorrowSet`](super::borrow_set::BorrowSet)
+ /// Retrieve the [`Body`] along with the [`BorrowSet`]
/// and [`RegionInferenceContext`]. If you would like the body only, use
/// [`TyCtxt::mir_promoted`].
///
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs
index 1e89a9f51445..4ac633c263fa 100644
--- a/compiler/rustc_borrowck/src/dataflow.rs
+++ b/compiler/rustc_borrowck/src/dataflow.rs
@@ -2,12 +2,14 @@
#![deny(rustc::diagnostic_outside_of_impl)]
use rustc_data_structures::fx::FxIndexMap;
use rustc_index::bit_set::BitSet;
-use rustc_middle::mir::{self, BasicBlock, Body, Location, Place};
+use rustc_middle::mir::{
+ self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
+};
use rustc_middle::ty::RegionVid;
use rustc_middle::ty::TyCtxt;
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
use rustc_mir_dataflow::ResultsVisitable;
-use rustc_mir_dataflow::{self, fmt::DebugWithContext, CallReturnPlaces, GenKill};
+use rustc_mir_dataflow::{self, fmt::DebugWithContext, GenKill};
use rustc_mir_dataflow::{Analysis, Direction, Results};
use std::fmt;
@@ -334,6 +336,10 @@ impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
type Idx = BorrowIndex;
+ fn domain_size(&self, _: &mir::Body<'tcx>) -> usize {
+ self.borrow_set.len()
+ }
+
fn before_statement_effect(
&mut self,
trans: &mut impl GenKill,
@@ -400,12 +406,12 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
self.kill_loans_out_of_scope_at_location(trans, location);
}
- fn terminator_effect(
+ fn terminator_effect<'mir>(
&mut self,
- trans: &mut impl GenKill,
- terminator: &mir::Terminator<'tcx>,
+ trans: &mut Self::Domain,
+ terminator: &'mir mir::Terminator<'tcx>,
_location: Location,
- ) {
+ ) -> TerminatorEdges<'mir, 'tcx> {
if let mir::TerminatorKind::InlineAsm { operands, .. } = &terminator.kind {
for op in operands {
if let mir::InlineAsmOperand::Out { place: Some(place), .. }
@@ -415,6 +421,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
}
}
}
+ terminator.edges()
}
fn call_return_effect(
diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs
index df5e383ad40b..d4c42a758748 100644
--- a/compiler/rustc_borrowck/src/invalidation.rs
+++ b/compiler/rustc_borrowck/src/invalidation.rs
@@ -159,7 +159,9 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
self.mutate_place(location, *resume_arg, Deep);
}
- TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => {
+ TerminatorKind::UnwindResume
+ | TerminatorKind::Return
+ | TerminatorKind::GeneratorDrop => {
// Invalidate all borrows of local places
let borrow_set = self.borrow_set;
let start = self.location_table.start_index(location);
@@ -200,7 +202,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
}
}
TerminatorKind::Goto { target: _ }
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Unreachable
| TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
| TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => {
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index efe525c224d0..ef2788efbcfc 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -770,9 +770,9 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
}
TerminatorKind::Goto { target: _ }
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Unreachable
- | TerminatorKind::Resume
+ | TerminatorKind::UnwindResume
| TerminatorKind::Return
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
@@ -803,7 +803,9 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
}
}
- TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => {
+ TerminatorKind::UnwindResume
+ | TerminatorKind::Return
+ | TerminatorKind::GeneratorDrop => {
// Returning from the function implicitly kills storage for all locals and statics.
// Often, the storage will already have been killed by an explicit
// StorageDead, but we don't always emit those (notably on unwind paths),
@@ -815,7 +817,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
}
}
- TerminatorKind::Terminate
+ TerminatorKind::UnwindTerminate
| TerminatorKind::Assert { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::Drop { .. }
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 50d875dfae99..d91a3d94045e 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1333,8 +1333,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
debug!("terminator kind: {:?}", term.kind);
match &term.kind {
TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::GeneratorDrop
| TerminatorKind::Unreachable
@@ -1608,12 +1608,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.assert_iscleanup(body, block_data, *target, is_cleanup);
}
}
- TerminatorKind::Resume => {
+ TerminatorKind::UnwindResume => {
if !is_cleanup {
span_mirbug!(self, block_data, "resume on non-cleanup block!")
}
}
- TerminatorKind::Terminate => {
+ TerminatorKind::UnwindTerminate => {
if !is_cleanup {
span_mirbug!(self, block_data, "abort on non-cleanup block!")
}
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 522dd7189fe6..ed371a04c537 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -474,10 +474,10 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
*destination,
);
}
- TerminatorKind::Terminate => {
+ TerminatorKind::UnwindTerminate => {
codegen_panic_cannot_unwind(fx, source_info);
}
- TerminatorKind::Resume => {
+ TerminatorKind::UnwindResume => {
// FIXME implement unwinding
fx.bcx.ins().trap(TrapCode::UnreachableCodeReached);
}
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index c31535742957..7db5f79eead9 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -550,8 +550,8 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
match &bb_data.terminator().kind {
TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 4c9094bf1f56..b6c01545f308 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -128,7 +128,10 @@ fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attr
// The function name varies on platforms.
// See test/CodeGen/mcount.c in clang.
- let mcount_name = cx.sess().target.mcount.as_ref();
+ let mcount_name = match &cx.sess().target.llvm_mcount_intrinsic {
+ Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(),
+ None => cx.sess().target.mcount.as_ref(),
+ };
attrs.push(llvm::CreateAttrStringValue(
cx.llcx,
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index 0be84c9fa833..4c8547407531 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -16,6 +16,7 @@ use rustc_metadata::fs::METADATA_FILENAME;
use rustc_metadata::EncodedMetadata;
use rustc_session::cstore::MetadataLoader;
use rustc_session::Session;
+use rustc_span::sym;
use rustc_target::abi::Endian;
use rustc_target::spec::{ef_avr_arch, RelocModel, Target};
@@ -272,35 +273,38 @@ pub(crate) fn create_object_file(sess: &Session) -> Option {
// Source: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/079772828bd10933d34121117a222b4cc0ee2200/riscv-elf.adoc
let mut e_flags: u32 = 0x0;
- let features = &sess.target.options.features;
+
// Check if compressed is enabled
- if features.contains("+c") {
+ // `unstable_target_features` is used here because "c" is gated behind riscv_target_feature.
+ if sess.unstable_target_features.contains(&sym::c) {
e_flags |= elf::EF_RISCV_RVC;
}
- // Select the appropriate floating-point ABI
- if features.contains("+d") {
- e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE;
- } else if features.contains("+f") {
- e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE;
- } else {
- e_flags |= elf::EF_RISCV_FLOAT_ABI_SOFT;
+ // Set the appropriate flag based on ABI
+ // This needs to match LLVM `RISCVELFStreamer.cpp`
+ match &*sess.target.llvm_abiname {
+ "" | "ilp32" | "lp64" => (),
+ "ilp32f" | "lp64f" => e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE,
+ "ilp32d" | "lp64d" => e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE,
+ "ilp32e" => e_flags |= elf::EF_RISCV_RVE,
+ _ => bug!("unknown RISC-V ABI name"),
}
+
e_flags
}
Architecture::LoongArch64 => {
// Source: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc#e_flags-identifies-abi-type-and-version
let mut e_flags: u32 = elf::EF_LARCH_OBJABI_V1;
- let features = &sess.target.options.features;
- // Select the appropriate floating-point ABI
- if features.contains("+d") {
- e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT;
- } else if features.contains("+f") {
- e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT;
- } else {
- e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT;
+ // Set the appropriate flag based on ABI
+ // This needs to match LLVM `LoongArchELFStreamer.cpp`
+ match &*sess.target.llvm_abiname {
+ "ilp32s" | "lp64s" => e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT,
+ "ilp32f" | "lp64f" => e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT,
+ "ilp32d" | "lp64d" => e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT,
+ _ => bug!("unknown RISC-V ABI name"),
}
+
e_flags
}
Architecture::Avr => {
diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
index 22c1f05974dd..3f5b46333d9b 100644
--- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
@@ -284,8 +284,8 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec> FunctionCx<'a, 'tcx, Bx> {
self.set_debug_loc(bx, terminator.source_info);
match terminator.kind {
- mir::TerminatorKind::Resume => {
+ mir::TerminatorKind::UnwindResume => {
self.codegen_resume_terminator(helper, bx);
MergingSucc::False
}
- mir::TerminatorKind::Terminate => {
+ mir::TerminatorKind::UnwindTerminate => {
self.codegen_terminate_terminator(helper, bx, terminator);
MergingSucc::False
}
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index 4167a85ccd59..564b5da32cc0 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -42,9 +42,6 @@ pub struct PerLocalVarDebugInfo<'tcx, D> {
/// `.place.projection` from `mir::VarDebugInfo`.
pub projection: &'tcx ty::List>,
-
- /// `references` from `mir::VarDebugInfo`.
- pub references: u8,
}
#[derive(Clone, Copy, Debug)]
@@ -323,7 +320,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
dbg_var,
fragment: None,
projection: ty::List::empty(),
- references: 0,
})
}
} else {
@@ -399,15 +395,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
&self,
bx: &mut Bx,
local: mir::Local,
- mut base: PlaceRef<'tcx, Bx::Value>,
+ base: PlaceRef<'tcx, Bx::Value>,
var: PerLocalVarDebugInfo<'tcx, Bx::DIVariable>,
) {
let Some(dbg_var) = var.dbg_var else { return };
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { return };
- let DebugInfoOffset { mut direct_offset, indirect_offsets, result: _ } =
+ let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
calculate_debuginfo_offset(bx, local, &var, base.layout);
- let mut indirect_offsets = &indirect_offsets[..];
// When targeting MSVC, create extra allocas for arguments instead of pointing multiple
// dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
@@ -421,9 +416,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// LLVM can handle simple things but anything more complex than just a direct
// offset or one indirect offset of 0 is too complex for it to generate CV records
// correctly.
- && (direct_offset != Size::ZERO || !matches!(indirect_offsets, [Size::ZERO] | []));
+ && (direct_offset != Size::ZERO || !matches!(&indirect_offsets[..], [Size::ZERO] | []));
+
+ if should_create_individual_allocas {
+ let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } =
+ calculate_debuginfo_offset(bx, local, &var, base);
- let create_alloca = |bx: &mut Bx, place: PlaceRef<'tcx, Bx::Value>, refcount| {
// Create a variable which will be a pointer to the actual value
let ptr_ty = Ty::new_ptr(
bx.tcx(),
@@ -431,35 +429,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
);
let ptr_layout = bx.layout_of(ptr_ty);
let alloca = PlaceRef::alloca(bx, ptr_layout);
- bx.set_var_name(alloca.llval, &format!("{}.ref{}.dbg.spill", var.name, refcount));
+ bx.set_var_name(alloca.llval, &(var.name.to_string() + ".dbg.spill"));
// Write the pointer to the variable
bx.store(place.llval, alloca.llval, alloca.align);
// Point the debug info to `*alloca` for the current variable
- alloca
- };
-
- if var.references > 0 {
- base = calculate_debuginfo_offset(bx, local, &var, base).result;
-
- // Point the debug info to `&...&base == alloca` for the current variable
- for refcount in 0..var.references {
- base = create_alloca(bx, base, refcount);
- }
-
- direct_offset = Size::ZERO;
- indirect_offsets = &[];
- } else if should_create_individual_allocas {
- let place = calculate_debuginfo_offset(bx, local, &var, base).result;
-
- // Point the debug info to `*alloca` for the current variable
- base = create_alloca(bx, place, 0);
- direct_offset = Size::ZERO;
- indirect_offsets = &[Size::ZERO];
+ bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO], None);
+ } else {
+ bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets, None);
}
-
- bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, indirect_offsets, None);
}
pub fn debug_introduce_locals(&self, bx: &mut Bx) {
@@ -492,7 +471,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
};
let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
- let (mut var_ty, var_kind) = match var.value {
+ let (var_ty, var_kind) = match var.value {
mir::VarDebugInfoContents::Place(place) => {
let var_ty = self.monomorphized_place_ty(place.as_ref());
let var_kind = if let Some(arg_index) = var.argument_index
@@ -529,13 +508,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
};
- for _ in 0..var.references {
- var_ty = Ty::new_ptr(
- bx.tcx(),
- ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: var_ty },
- );
- }
-
self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
});
@@ -547,7 +519,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
dbg_var,
fragment: None,
projection: place.projection,
- references: var.references,
});
}
mir::VarDebugInfoContents::Const(c) => {
@@ -601,7 +572,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
Some(fragment_start..fragment_start + fragment_layout.size)
},
projection: place.projection,
- references: var.references,
});
}
}
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs
index d39a7e8a1925..f146b93ff0cc 100644
--- a/compiler/rustc_const_eval/src/const_eval/error.rs
+++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -18,7 +18,6 @@ pub enum ConstEvalErrKind {
ModifiedGlobal,
AssertFailure(AssertKind),
Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
- Abort(String),
}
impl MachineStopType for ConstEvalErrKind {
@@ -30,7 +29,6 @@ impl MachineStopType for ConstEvalErrKind {
ModifiedGlobal => const_eval_modified_global,
Panic { .. } => const_eval_panic,
AssertFailure(x) => x.diagnostic_message(),
- Abort(msg) => msg.to_string().into(),
}
}
fn add_args(
@@ -39,7 +37,7 @@ impl MachineStopType for ConstEvalErrKind {
) {
use ConstEvalErrKind::*;
match *self {
- ConstAccessesStatic | ModifiedGlobal | Abort(_) => {}
+ ConstAccessesStatic | ModifiedGlobal => {}
AssertFailure(kind) => kind.add_args(adder),
Panic { msg, line, col, file } => {
adder("msg".into(), msg.into_diagnostic_arg());
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 032f4be6c997..f16aea6f34b7 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -427,52 +427,48 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
fn find_mir_or_eval_fn(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
- instance: ty::Instance<'tcx>,
+ orig_instance: ty::Instance<'tcx>,
_abi: CallAbi,
args: &[FnArg<'tcx>],
dest: &PlaceTy<'tcx>,
ret: Option,
_unwind: mir::UnwindAction, // unwinding is not supported in consts
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
- debug!("find_mir_or_eval_fn: {:?}", instance);
+ debug!("find_mir_or_eval_fn: {:?}", orig_instance);
+
+ // Replace some functions.
+ let Some(instance) = ecx.hook_special_const_fn(orig_instance, args, dest, ret)? else {
+ // Call has already been handled.
+ return Ok(None);
+ };
// Only check non-glue functions
if let ty::InstanceDef::Item(def) = instance.def {
// Execution might have wandered off into other crates, so we cannot do a stability-
- // sensitive check here. But we can at least rule out functions that are not const
- // at all.
- if !ecx.tcx.is_const_fn_raw(def) {
- // allow calling functions inside a trait marked with #[const_trait].
- if !ecx.tcx.is_const_default_method(def) {
- // We certainly do *not* want to actually call the fn
- // though, so be sure we return here.
- throw_unsup_format!("calling non-const function `{}`", instance)
- }
- }
-
- let Some(new_instance) = ecx.hook_special_const_fn(instance, args, dest, ret)? else {
- return Ok(None);
- };
-
- if new_instance != instance {
- // We call another const fn instead.
- // However, we return the *original* instance to make backtraces work out
- // (and we hope this does not confuse the FnAbi checks too much).
- return Ok(Self::find_mir_or_eval_fn(
- ecx,
- new_instance,
- _abi,
- args,
- dest,
- ret,
- _unwind,
- )?
- .map(|(body, _instance)| (body, instance)));
+ // sensitive check here. But we can at least rule out functions that are not const at
+ // all. That said, we have to allow calling functions inside a trait marked with
+ // #[const_trait]. These *are* const-checked!
+ // FIXME: why does `is_const_fn_raw` not classify them as const?
+ if (!ecx.tcx.is_const_fn_raw(def) && !ecx.tcx.is_const_default_method(def))
+ || ecx.tcx.has_attr(def, sym::rustc_do_not_const_check)
+ {
+ // We certainly do *not* want to actually call the fn
+ // though, so be sure we return here.
+ throw_unsup_format!("calling non-const function `{}`", instance)
}
}
// This is a const fn. Call it.
- Ok(Some((ecx.load_mir(instance.def, None)?, instance)))
+ // In case of replacement, we return the *original* instance to make backtraces work out
+ // (and we hope this does not confuse the FnAbi checks too much).
+ Ok(Some((ecx.load_mir(instance.def, None)?, orig_instance)))
+ }
+
+ fn panic_nounwind(ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: &str) -> InterpResult<'tcx> {
+ let msg = Symbol::intern(msg);
+ let span = ecx.find_closest_untracked_caller_location();
+ let (file, line, col) = ecx.location_triple_for_span(span);
+ Err(ConstEvalErrKind::Panic { msg, file, line, col }.into())
}
fn call_intrinsic(
@@ -595,10 +591,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
Err(ConstEvalErrKind::AssertFailure(err).into())
}
- fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: String) -> InterpResult<'tcx, !> {
- Err(ConstEvalErrKind::Abort(msg).into())
- }
-
fn binary_ptr_op(
_ecx: &InterpCx<'mir, 'tcx, Self>,
_bin_op: mir::BinOp,
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 3ac6f07e8b7b..61d3de5c4050 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -765,7 +765,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
mir::UnwindAction::Terminate => {
self.frame_mut().loc = Right(self.frame_mut().body.span);
- M::abort(self, "panic in a function that cannot unwind".to_owned())?;
+ M::unwind_terminate(self)?;
+ // This might have pushed a new stack frame, or it terminated execution.
+ // Either way, `loc` will not be updated.
+ return Ok(());
}
};
Ok(())
@@ -865,6 +868,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
panic!("encountered StackPopCleanup::Root when unwinding!")
}
};
+ // This must be the very last thing that happens, since it can in fact push a new stack frame.
self.unwind_to_block(unwind)
} else {
// Follow the normal return edge.
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index f22cd919c369..d6ca6fe73a6e 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -125,15 +125,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
) -> InterpResult<'tcx, bool> {
let instance_args = instance.args;
let intrinsic_name = self.tcx.item_name(instance.def_id());
-
- // First handle intrinsics without return place.
- let ret = match ret {
- None => match intrinsic_name {
- sym::abort => M::abort(self, "the program aborted execution".to_owned())?,
- // Unsupported diverging intrinsic.
- _ => return Ok(false),
- },
- Some(p) => p,
+ let Some(ret) = ret else {
+ // We don't support any intrinsic without return place.
+ return Ok(false);
};
match intrinsic_name {
@@ -410,7 +404,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"),
};
- M::abort(self, msg)?;
+ M::panic_nounwind(self, &msg)?;
+ // Skip the `go_to_block` at the end.
+ return Ok(true);
}
}
sym::simd_insert => {
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index e101785b6e24..d3c73b896c08 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -218,10 +218,11 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
unwind: mir::UnwindAction,
) -> InterpResult<'tcx>;
- /// Called to abort evaluation.
- fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _msg: String) -> InterpResult<'tcx, !> {
- throw_unsup_format!("aborting execution is not supported")
- }
+ /// Called to trigger a non-unwinding panic.
+ fn panic_nounwind(_ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: &str) -> InterpResult<'tcx>;
+
+ /// Called when unwinding reached a state where execution should be terminated.
+ fn unwind_terminate(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>;
/// Called for all binary operations where the LHS has pointer type.
///
@@ -499,6 +500,11 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
false
}
+ #[inline(always)]
+ fn unwind_terminate(_ecx: &mut InterpCx<$mir, $tcx, Self>) -> InterpResult<$tcx> {
+ unreachable!("unwinding cannot happen during compile-time evaluation")
+ }
+
#[inline(always)]
fn call_extra_fn(
_ecx: &mut InterpCx<$mir, $tcx, Self>,
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 3c03172bbeff..b2ebcceceb32 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -196,15 +196,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
- Terminate => {
- // FIXME: maybe should call `panic_no_unwind` lang item instead.
- M::abort(self, "panic in a function that cannot unwind".to_owned())?;
+ UnwindTerminate => {
+ M::unwind_terminate(self)?;
}
// When we encounter Resume, we've finished unwinding
// cleanup for the current stack frame. We pop it in order
// to continue unwinding the next frame
- Resume => {
+ UnwindResume => {
trace!("unwinding: resuming from cleanup");
// By definition, a Resume terminator means
// that we're unwinding
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 fae047bff9e7..e67098a3ac38 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -1037,7 +1037,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
self.check_op(ops::Generator(hir::GeneratorKind::Gen))
}
- TerminatorKind::Terminate => {
+ TerminatorKind::UnwindTerminate => {
// Cleanup blocks are skipped for const checking (see `visit_basic_block_data`).
span_bug!(self.span, "`Terminate` terminator outside of cleanup block")
}
@@ -1046,7 +1046,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
+ | TerminatorKind::UnwindResume
| TerminatorKind::Return
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::Unreachable => {}
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 e3377bd10c61..a8c61d2c8fd8 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
@@ -106,7 +106,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
}
}
- mir::TerminatorKind::Terminate
+ mir::TerminatorKind::UnwindTerminate
| mir::TerminatorKind::Call { .. }
| mir::TerminatorKind::Assert { .. }
| mir::TerminatorKind::FalseEdge { .. }
@@ -114,7 +114,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
| mir::TerminatorKind::GeneratorDrop
| mir::TerminatorKind::Goto { .. }
| mir::TerminatorKind::InlineAsm { .. }
- | mir::TerminatorKind::Resume
+ | mir::TerminatorKind::UnwindResume
| mir::TerminatorKind::Return
| mir::TerminatorKind::SwitchInt { .. }
| mir::TerminatorKind::Unreachable
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 3a869f7f5472..a137f84b7382 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/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 83004492c8bf..0dfbbf73dce4 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,6 +59,25 @@ impl<'tcx> MirPass<'tcx> for Validator {
.iterate_to_fixpoint()
.into_results_cursor(body);
+ 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,
@@ -68,6 +88,7 @@ impl<'tcx> MirPass<'tcx> for Validator {
storage_liveness,
place_cache: FxHashSet::default(),
value_cache: FxHashSet::default(),
+ can_unwind,
};
cfg_checker.visit_body(body);
cfg_checker.check_cleanup_control_flow();
@@ -99,6 +120,9 @@ struct CfgChecker<'a, 'tcx> {
storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>,
place_cache: FxHashSet>,
value_cache: FxHashSet,
+ // If `false`, then the MIR must not contain `UnwindAction::Continue` or
+ // `TerminatorKind::Resume`.
+ can_unwind: bool,
}
impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
@@ -237,13 +261,17 @@ impl<'a, 'tcx> CfgChecker<'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 => (),
@@ -464,13 +492,19 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
);
}
}
- TerminatorKind::Resume | TerminatorKind::Terminate => {
+ TerminatorKind::UnwindResume => {
let bb = location.block;
if !self.body.basic_blocks[bb].is_cleanup {
- self.fail(
- location,
- "Cannot `Resume` or `Terminate` from non-cleanup basic block",
- )
+ self.fail(location, "Cannot `UnwindResume` from non-cleanup basic block")
+ }
+ if !self.can_unwind {
+ self.fail(location, "Cannot `UnwindResume` in a function that cannot unwind")
+ }
+ }
+ TerminatorKind::UnwindTerminate => {
+ let bb = location.block;
+ if !self.body.basic_blocks[bb].is_cleanup {
+ self.fail(location, "Cannot `UnwindTerminate` from non-cleanup basic block")
}
}
TerminatorKind::Return => {
@@ -665,7 +699,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
return;
};
- f_ty.ty
+ ty::EarlyBinder::bind(f_ty.ty).instantiate(self.tcx, args)
} else {
let Some(&f_ty) = args.as_generator().prefix_tys().get(f.index())
else {
@@ -701,12 +735,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
VarDebugInfoContents::Const(_) => {}
VarDebugInfoContents::Place(place) => {
check_place(self, place);
- if debuginfo.references != 0 && place.projection.last() == Some(&PlaceElem::Deref) {
- self.fail(
- START_BLOCK.start_location(),
- format!("debuginfo {debuginfo:?}, has both ref and deref"),
- );
- }
}
VarDebugInfoContents::Composite { ty, ref fragments } => {
for f in fragments {
@@ -1204,8 +1232,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::InlineAsm { .. }
| TerminatorKind::GeneratorDrop
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::Unreachable => {}
}
diff --git a/compiler/rustc_error_codes/src/error_codes/E0132.md b/compiler/rustc_error_codes/src/error_codes/E0132.md
index a23cc988bdb6..51258739b89c 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0132.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0132.md
@@ -13,7 +13,7 @@ It is not possible to declare type parameters on a function that has the `start`
attribute. Such a function must have the following type signature (for more
information, view [the unstable book][1]):
-[1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html#writing-an-executable-without-stdlib
+[1]: https://doc.rust-lang.org/unstable-book/language-features/start.html
```
# let _:
diff --git a/compiler/rustc_error_codes/src/error_codes/E0152.md b/compiler/rustc_error_codes/src/error_codes/E0152.md
index ef17b8b4c75c..d862766571df 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0152.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0152.md
@@ -20,6 +20,6 @@ attributes:
#![no_std]
```
-See also the [unstable book][1].
+See also [this section of the Rustonomicon][beneath std].
-[1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html#writing-an-executable-without-stdlib
+[beneath std]: https://doc.rust-lang.org/nomicon/beneath-std.html
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 7d660d2dbaa9..34518b53759d 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -452,11 +452,11 @@ struct HandlerInner {
/// have been converted.
check_unstable_expect_diagnostics: bool,
- /// Expected [`Diagnostic`][diagnostic::Diagnostic]s store a [`LintExpectationId`] as part of
+ /// Expected [`Diagnostic`][struct@diagnostic::Diagnostic]s store a [`LintExpectationId`] as part of
/// the lint level. [`LintExpectationId`]s created early during the compilation
/// (before `HirId`s have been defined) are not stable and can therefore not be
/// stored on disk. This buffer stores these diagnostics until the ID has been
- /// replaced by a stable [`LintExpectationId`]. The [`Diagnostic`][diagnostic::Diagnostic]s are the
+ /// replaced by a stable [`LintExpectationId`]. The [`Diagnostic`][struct@diagnostic::Diagnostic]s are the
/// submitted for storage and added to the list of fulfilled expectations.
unstable_expect_diagnostics: Vec,
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index 05c0cd952b8b..7e85beaadcbc 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -81,7 +81,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use rustc_errors::ErrorGuaranteed;
use rustc_lint_defs::pluralize;
-use rustc_parse::parser::{NtOrTt, Parser};
+use rustc_parse::parser::{ParseNtResult, Parser};
use rustc_span::symbol::Ident;
use rustc_span::symbol::MacroRulesNormalizedIdent;
use rustc_span::Span;
@@ -692,8 +692,8 @@ impl TtParser {
Ok(nt) => nt,
};
let m = match nt {
- NtOrTt::Nt(nt) => MatchedNonterminal(Lrc::new(nt)),
- NtOrTt::Tt(tt) => MatchedTokenTree(tt),
+ ParseNtResult::Nt(nt) => MatchedNonterminal(Lrc::new(nt)),
+ ParseNtResult::Tt(tt) => MatchedTokenTree(tt),
};
mp.push_match(next_metavar, seq_depth, m);
mp.idx += 1;
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index ce8b46217209..a5959d68fbc8 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -1328,7 +1328,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
_ => IsInFollow::No(TOKENS),
}
}
- NonterminalKind::PatWithOr { .. } => {
+ NonterminalKind::PatWithOr => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"];
match tok {
TokenTree::Token(token) => match token.kind {
diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs
index b6382dcb8944..7c37aadc67ae 100644
--- a/compiler/rustc_expand/src/mbe/metavar_expr.rs
+++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs
@@ -93,7 +93,17 @@ fn parse_count<'sess>(
span: Span,
) -> PResult<'sess, MetaVarExpr> {
let ident = parse_ident(iter, sess, span)?;
- let depth = if try_eat_comma(iter) { Some(parse_depth(iter, sess, span)?) } else { None };
+ let depth = if try_eat_comma(iter) {
+ if iter.look_ahead(0).is_none() {
+ return Err(sess.span_diagnostic.struct_span_err(
+ span,
+ "`count` followed by a comma must have an associated index indicating its depth",
+ ));
+ }
+ Some(parse_depth(iter, sess, span)?)
+ } else {
+ None
+ };
Ok(MetaVarExpr::Count(ident, depth))
}
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index a5f83b88f7e4..15e7ab3fe3ee 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -220,16 +220,15 @@ pub(super) fn transcribe<'a>(
MatchedTokenTree(tt) => {
// `tt`s are emitted into the output stream directly as "raw tokens",
// without wrapping them into groups.
- let token = tt.clone();
- result.push(token);
+ result.push(tt.clone());
}
MatchedNonterminal(nt) => {
// Other variables are emitted into the output stream as groups with
// `Delimiter::Invisible` to maintain parsing priorities.
// `Interpolated` is currently used for such groups in rustc parser.
marker.visit_span(&mut sp);
- let token = TokenTree::token_alone(token::Interpolated(nt.clone()), sp);
- result.push(token);
+ result
+ .push(TokenTree::token_alone(token::Interpolated(nt.clone()), sp));
}
MatchedSeq(..) => {
// We were unable to descend far enough. This is an error.
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 953ea1bf523d..f5708f933d50 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -352,6 +352,8 @@ declare_features! (
(active, c_variadic, "1.34.0", Some(44930), None),
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
(active, cfg_overflow_checks, "1.71.0", Some(111466), None),
+ /// Provides the relocation model information as cfg entry
+ (active, cfg_relocation_model, "CURRENT_RUSTC_VERSION", Some(114929), None),
/// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used.
(active, cfg_sanitize, "1.41.0", Some(39699), None),
/// Allows `cfg(target_abi = "...")`.
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index a183cfd8776a..2f7cff3ce5c8 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -35,6 +35,7 @@ const GATED_CFGS: &[GatedCfg] = &[
(sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
(sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)),
(sym::version, sym::cfg_version, cfg_fn!(cfg_version)),
+ (sym::relocation_model, sym::cfg_relocation_model, cfg_fn!(cfg_relocation_model)),
];
/// Find a gated cfg determined by the `pred`icate which is given the cfg's name.
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 166760166c10..597cae6ff33c 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -1,6 +1,9 @@
hir_analysis_ambiguous_lifetime_bound =
ambiguous lifetime bound, explicit lifetime bound required
+hir_analysis_assoc_bound_on_const = expected associated type, found {$descr}
+ .note = trait bounds not allowed on {$descr}
+
hir_analysis_assoc_type_binding_not_allowed =
associated type bindings are not allowed here
.label = associated type not allowed here
diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
index 30145b1a185a..ba152cd48dea 100644
--- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
@@ -13,7 +13,7 @@ use crate::astconv::{
AstConv, ConvertedBinding, ConvertedBindingKind, OnlySelfBounds, PredicateFilter,
};
use crate::bounds::Bounds;
-use crate::errors::{MultipleRelaxedDefaultBounds, ValueOfAssociatedStructAlreadySpecified};
+use crate::errors;
impl<'tcx> dyn AstConv<'tcx> + '_ {
/// Sets `implicitly_sized` to true on `Bounds` if necessary
@@ -35,7 +35,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
if unbound.is_none() {
unbound = Some(&ptr.trait_ref);
} else {
- tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span });
+ tcx.sess.emit_err(errors::MultipleRelaxedDefaultBounds { span });
}
}
}
@@ -326,7 +326,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
dup_bindings
.entry(assoc_item.def_id)
.and_modify(|prev_span| {
- tcx.sess.emit_err(ValueOfAssociatedStructAlreadySpecified {
+ tcx.sess.emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
span: binding.span,
prev_span: *prev_span,
item_name: binding.item_name,
@@ -488,6 +488,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
}
}
+ let assoc_item_def_id = projection_ty.skip_binder().def_id;
+ let def_kind = tcx.def_kind(assoc_item_def_id);
match binding.kind {
ConvertedBindingKind::Equality(..) if return_type_notation => {
return Err(self.tcx().sess.emit_err(
@@ -499,11 +501,9 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
// the "projection predicate" for:
//
// `::Item = u32`
- let assoc_item_def_id = projection_ty.skip_binder().def_id;
- let def_kind = tcx.def_kind(assoc_item_def_id);
match (def_kind, term.unpack()) {
- (hir::def::DefKind::AssocTy, ty::TermKind::Ty(_))
- | (hir::def::DefKind::AssocConst, ty::TermKind::Const(_)) => (),
+ (DefKind::AssocTy, ty::TermKind::Ty(_))
+ | (DefKind::AssocConst, ty::TermKind::Const(_)) => (),
(_, _) => {
let got = if let Some(_) = term.ty() { "type" } else { "constant" };
let expected = tcx.def_descr(assoc_item_def_id);
@@ -516,7 +516,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
format!("{expected} defined here"),
);
- if let hir::def::DefKind::AssocConst = def_kind
+ if let DefKind::AssocConst = def_kind
&& let Some(t) = term.ty() && (t.is_enum() || t.references_error())
&& tcx.features().associated_const_equality {
err.span_suggestion(
@@ -528,8 +528,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
}
let reported = err.emit();
term = match def_kind {
- hir::def::DefKind::AssocTy => Ty::new_error(tcx, reported).into(),
- hir::def::DefKind::AssocConst => ty::Const::new_error(
+ DefKind::AssocTy => Ty::new_error(tcx, reported).into(),
+ DefKind::AssocConst => ty::Const::new_error(
tcx,
reported,
tcx.type_of(assoc_item_def_id)
@@ -548,6 +548,15 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
);
}
ConvertedBindingKind::Constraint(ast_bounds) => {
+ match def_kind {
+ DefKind::AssocTy => {}
+ _ => {
+ return Err(tcx.sess.emit_err(errors::AssocBoundOnConst {
+ span: assoc_ident.span,
+ descr: tcx.def_descr(assoc_item_def_id),
+ }));
+ }
+ }
// "Desugar" a constraint like `T: Iterator` to
//
// `::Item: Debug`
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 0babdf7e5b3e..9471ad9ca906 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -918,3 +918,12 @@ pub struct UnusedAssociatedTypeBounds {
#[suggestion(code = "")]
pub span: Span,
}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_assoc_bound_on_const)]
+#[note]
+pub struct AssocBoundOnConst {
+ #[primary_span]
+ pub span: Span,
+ pub descr: &'static str,
+}
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index dd79d1afc62f..02371f85ac3b 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -599,6 +599,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
= self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id)
// Only suggest removing parens if there are no arguments
&& arg_exprs.is_empty()
+ && call_expr.span.contains(callee_expr.span)
{
let descr = match kind {
def::CtorOf::Struct => "struct",
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 5bc0e2ee86c8..31a03fabe4f2 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -389,34 +389,26 @@ impl<'a, 'tcx> CastCheck<'tcx> {
if let ty::Ref(reg, cast_ty, mutbl) = *self.cast_ty.kind() {
if let ty::RawPtr(TypeAndMut { ty: expr_ty, .. }) = *self.expr_ty.kind()
&& fcx
- .try_coerce(
- self.expr,
+ .can_coerce(
Ty::new_ref(fcx.tcx,
fcx.tcx.lifetimes.re_erased,
TypeAndMut { ty: expr_ty, mutbl },
),
self.cast_ty,
- AllowTwoPhase::No,
- None,
)
- .is_ok()
{
sugg = Some((format!("&{}*", mutbl.prefix_str()), cast_ty == expr_ty));
} else if let ty::Ref(expr_reg, expr_ty, expr_mutbl) = *self.expr_ty.kind()
&& expr_mutbl == Mutability::Not
&& mutbl == Mutability::Mut
&& fcx
- .try_coerce(
- self.expr,
+ .can_coerce(
Ty::new_ref(fcx.tcx,
expr_reg,
TypeAndMut { ty: expr_ty, mutbl: Mutability::Mut },
),
self.cast_ty,
- AllowTwoPhase::No,
- None,
)
- .is_ok()
{
sugg_mutref = true;
}
@@ -424,30 +416,22 @@ impl<'a, 'tcx> CastCheck<'tcx> {
if !sugg_mutref
&& sugg == None
&& fcx
- .try_coerce(
- self.expr,
+ .can_coerce(
Ty::new_ref(fcx.tcx,reg, TypeAndMut { ty: self.expr_ty, mutbl }),
self.cast_ty,
- AllowTwoPhase::No,
- None,
)
- .is_ok()
{
sugg = Some((format!("&{}", mutbl.prefix_str()), false));
}
} else if let ty::RawPtr(TypeAndMut { mutbl, .. }) = *self.cast_ty.kind()
&& fcx
- .try_coerce(
- self.expr,
+ .can_coerce(
Ty::new_ref(fcx.tcx,
fcx.tcx.lifetimes.re_erased,
TypeAndMut { ty: self.expr_ty, mutbl },
),
self.cast_ty,
- AllowTwoPhase::No,
- None,
)
- .is_ok()
{
sugg = Some((format!("&{}", mutbl.prefix_str()), false));
}
@@ -760,7 +744,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
ty::FnDef(..) => {
// Attempt a coercion to a fn pointer type.
let f = fcx.normalize(self.expr_span, self.expr_ty.fn_sig(fcx.tcx));
- let res = fcx.try_coerce(
+ let res = fcx.coerce(
self.expr,
self.expr_ty,
Ty::new_fn_ptr(fcx.tcx, f),
@@ -860,7 +844,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
(_, DynStar) => {
if fcx.tcx.features().dyn_star {
- bug!("should be handled by `try_coerce`")
+ bug!("should be handled by `coerce`")
} else {
Err(CastError::IllegalCast)
}
@@ -956,7 +940,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
// Coerce to a raw pointer so that we generate AddressOf in MIR.
let array_ptr_type = Ty::new_ptr(fcx.tcx, m_expr);
- fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None)
+ fcx.coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None)
.unwrap_or_else(|_| {
bug!(
"could not cast from reference to array to pointer to array ({:?} to {:?})",
@@ -992,7 +976,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
}
fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<(), ty::error::TypeError<'tcx>> {
- match fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No, None) {
+ match fcx.coerce(self.expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No, None) {
Ok(_) => Ok(()),
Err(err) => Err(err),
}
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index 3e67afb1c3d9..b19fb6da6def 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -711,6 +711,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
};
+ let span = self.tcx.def_span(expr_def_id);
+
let output_ty = match *ret_ty.kind() {
ty::Infer(ty::TyVar(ret_vid)) => {
self.obligations_for_self_ty(ret_vid).find_map(|obligation| {
@@ -724,20 +726,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.find_map(|(p, s)| get_future_output(p.as_predicate(), s))?,
ty::Error(_) => return None,
_ => span_bug!(
- self.tcx.def_span(expr_def_id),
+ span,
"async fn generator return type not an inference variable: {ret_ty}"
),
};
+ let output_ty = self.normalize(span, output_ty);
+
// async fn that have opaque types in their return type need to redo the conversion to inference variables
// as they fetch the still opaque version from the signature.
let InferOk { value: output_ty, obligations } = self
- .replace_opaque_types_with_inference_vars(
- output_ty,
- body_def_id,
- self.tcx.def_span(expr_def_id),
- self.param_env,
- );
+ .replace_opaque_types_with_inference_vars(output_ty, body_def_id, span, self.param_env);
self.register_predicates(obligations);
Some(output_ty)
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 726914a995b1..fca675ea9d80 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -1005,7 +1005,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// adjusted type of the expression, if successful.
/// Adjustments are only recorded if the coercion succeeded.
/// The expressions *must not* have any preexisting adjustments.
- pub fn try_coerce(
+ pub fn coerce(
&self,
expr: &hir::Expr<'_>,
expr_ty: Ty<'tcx>,
@@ -1036,7 +1036,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
})
}
- /// Same as `try_coerce()`, but without side-effects.
+ /// Same as `coerce()`, but without side-effects.
///
/// Returns false if the coercion creates any obligations that result in
/// errors.
@@ -1494,7 +1494,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
// Special-case the first expression we are coercing.
// To be honest, I'm not entirely sure why we do this.
// We don't allow two-phase borrows, see comment in try_find_coercion_lub for why
- fcx.try_coerce(
+ fcx.coerce(
expression,
expression_ty,
self.expected_ty,
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 5b06088c3481..2c16f21b4916 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -53,7 +53,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|| self.suggest_no_capture_closure(err, expected, expr_ty)
|| self.suggest_boxing_when_appropriate(err, expr.span, expr.hir_id, expected, expr_ty)
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
- || self.suggest_copied_cloned_or_as_ref(err, expr, expr_ty, expected, expected_ty_expr)
+ || self.suggest_copied_cloned_or_as_ref(err, expr, expr_ty, expected)
|| self.suggest_clone_for_ref(err, expr, expr_ty, expected)
|| self.suggest_into(err, expr, expr_ty, expected)
|| self.suggest_floating_point_literal(err, expr, expected)
@@ -254,7 +254,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> (Ty<'tcx>, Option>) {
let expected = self.resolve_vars_with_obligations(expected);
- let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase, None) {
+ let e = match self.coerce(expr, checked_ty, expected, allow_two_phase, None) {
Ok(ty) => return (ty, None),
Err(e) => e,
};
@@ -475,7 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
let Some(arg_ty) = self.node_ty_opt(arg_expr.hir_id) else { continue; };
let arg_ty = arg_ty.fold_with(&mut fudger);
- let _ = self.try_coerce(
+ let _ = self.coerce(
arg_expr,
arg_ty,
*expected_arg_ty,
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 36096aa35d4c..054d23c71d4a 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -253,7 +253,7 @@ impl HelpUseLatestEdition {
}
#[derive(Subdiagnostic)]
-pub enum OptionResultRefMismatch<'tcx> {
+pub enum OptionResultRefMismatch {
#[suggestion(
hir_typeck_option_result_copied,
code = ".copied()",
@@ -276,19 +276,20 @@ pub enum OptionResultRefMismatch<'tcx> {
span: Span,
def_path: String,
},
- #[suggestion(
- hir_typeck_option_result_asref,
- code = ".as_ref()",
- style = "verbose",
- applicability = "machine-applicable"
- )]
- AsRef {
- #[primary_span]
- span: Span,
- def_path: String,
- expected_ty: Ty<'tcx>,
- expr_ty: Ty<'tcx>,
- },
+ // FIXME: #114050
+ // #[suggestion(
+ // hir_typeck_option_result_asref,
+ // code = ".as_ref()",
+ // style = "verbose",
+ // applicability = "machine-applicable"
+ // )]
+ // AsRef {
+ // #[primary_span]
+ // span: Span,
+ // def_path: String,
+ // expected_ty: Ty<'tcx>,
+ // expr_ty: Ty<'tcx>,
+ // },
}
#[derive(Diagnostic)]
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 817115012a46..4def78673841 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -260,9 +260,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// fulfillment error to be more accurate.
let coerced_ty = self.resolve_vars_with_obligations(coerced_ty);
- let coerce_error = self
- .try_coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None)
- .err();
+ let coerce_error =
+ self.coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None).err();
if coerce_error.is_some() {
return Compatibility::Incompatible(coerce_error);
@@ -519,7 +518,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// suggestions and labels are (more) correct when an arg is a
// macro invocation.
let normalize_span = |span: Span| -> Span {
- let normalized_span = span.find_ancestor_inside(error_span).unwrap_or(span);
+ let normalized_span = span.find_ancestor_inside_same_ctxt(error_span).unwrap_or(span);
// Sometimes macros mess up the spans, so do not normalize the
// arg span to equal the error span, because that's less useful
// than pointing out the arg expr in the wrong context.
@@ -929,7 +928,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
labels.push((provided_span, format!("unexpected argument{provided_ty_name}")));
let mut span = provided_span;
- if span.can_be_used_for_suggestions() {
+ if span.can_be_used_for_suggestions()
+ && error_span.can_be_used_for_suggestions()
+ {
if arg_idx.index() > 0
&& let Some((_, prev)) = provided_arg_tys
.get(ProvidedIdx::from_usize(arg_idx.index() - 1)
@@ -1220,22 +1221,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
if let Some(suggestion_text) = suggestion_text {
let source_map = self.sess().source_map();
- let (mut suggestion, suggestion_span) =
- if let Some(call_span) = full_call_span.find_ancestor_inside(error_span) {
- ("(".to_string(), call_span.shrink_to_hi().to(error_span.shrink_to_hi()))
- } else {
- (
- format!(
- "{}(",
- source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| {
- fn_def_id.map_or("".to_string(), |fn_def_id| {
- tcx.item_name(fn_def_id).to_string()
- })
+ let (mut suggestion, suggestion_span) = if let Some(call_span) =
+ full_call_span.find_ancestor_inside_same_ctxt(error_span)
+ {
+ ("(".to_string(), call_span.shrink_to_hi().to(error_span.shrink_to_hi()))
+ } else {
+ (
+ format!(
+ "{}(",
+ source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| {
+ fn_def_id.map_or("".to_string(), |fn_def_id| {
+ tcx.item_name(fn_def_id).to_string()
})
- ),
- error_span,
- )
- };
+ })
+ ),
+ error_span,
+ )
+ };
let mut needs_comma = false;
for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() {
if needs_comma {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 48358e338dae..d2a53ee8b5e1 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1097,7 +1097,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &hir::Expr<'_>,
expr_ty: Ty<'tcx>,
expected_ty: Ty<'tcx>,
- expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
) -> bool {
let ty::Adt(adt_def, args) = expr_ty.kind() else {
return false;
@@ -1115,7 +1114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
let expr_inner_ty = args.type_at(0);
let expected_inner_ty = expected_args.type_at(0);
- if let &ty::Ref(_, ty, mutability) = expr_inner_ty.kind()
+ if let &ty::Ref(_, ty, _mutability) = expr_inner_ty.kind()
&& self.can_eq(self.param_env, ty, expected_inner_ty)
{
let def_path = self.tcx.def_path_str(adt_def.did());
@@ -1124,14 +1123,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
errors::OptionResultRefMismatch::Copied {
span, def_path
}
- } else if let Some(expected_ty_expr) = expected_ty_expr
- // FIXME: suggest changes to both expressions to convert both to
- // Option/Result<&T>
- && mutability.is_not()
- {
- errors::OptionResultRefMismatch::AsRef {
- span: expected_ty_expr.span.shrink_to_hi(), expected_ty, expr_ty, def_path
- }
} else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
self,
diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
index 5b19a4c525f8..3f1dca5b1dea 100644
--- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs
+++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
@@ -14,6 +14,7 @@ use rustc_span::symbol::kw::{Empty, Underscore};
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt;
+use std::fmt::Write;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(super) fn lint_dot_call_from_2018(
@@ -143,16 +144,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp);
if precise {
- let args = args
- .iter()
- .map(|arg| {
- let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
- format!(
- ", {}",
- self.sess().source_map().span_to_snippet(span).unwrap()
- )
- })
- .collect::();
+ let args = args.iter().fold(String::new(), |mut string, arg| {
+ let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
+ write!(
+ string,
+ ", {}",
+ self.sess().source_map().span_to_snippet(span).unwrap()
+ )
+ .unwrap();
+ string
+ });
lint.span_suggestion(
sp,
diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs
index 1111a1a17e29..db8ea2bfe48a 100644
--- a/compiler/rustc_incremental/src/persist/fs.rs
+++ b/compiler/rustc_incremental/src/persist/fs.rs
@@ -538,9 +538,13 @@ where
continue;
}
- let timestamp = extract_timestamp_from_session_dir(&directory_name).unwrap_or_else(|_| {
- bug!("unexpected incr-comp session dir: {}", session_dir.display())
- });
+ let timestamp = match extract_timestamp_from_session_dir(&directory_name) {
+ Ok(timestamp) => timestamp,
+ Err(e) => {
+ debug!("unexpected incr-comp session dir: {}: {}", session_dir.display(), e);
+ continue;
+ }
+ };
if timestamp > best_candidate.0 {
best_candidate = (timestamp, Some(session_dir.clone()));
@@ -562,14 +566,14 @@ fn is_session_directory_lock_file(file_name: &str) -> bool {
file_name.starts_with("s-") && file_name.ends_with(LOCK_FILE_EXT)
}
-fn extract_timestamp_from_session_dir(directory_name: &str) -> Result {
+fn extract_timestamp_from_session_dir(directory_name: &str) -> Result {
if !is_session_directory(directory_name) {
- return Err(());
+ return Err("not a directory");
}
let dash_indices: Vec<_> = directory_name.match_indices('-').map(|(idx, _)| idx).collect();
if dash_indices.len() != 3 {
- return Err(());
+ return Err("not three dashes in name");
}
string_to_timestamp(&directory_name[dash_indices[0] + 1..dash_indices[1]])
@@ -581,11 +585,11 @@ fn timestamp_to_string(timestamp: SystemTime) -> String {
base_n::encode(micros as u128, INT_ENCODE_BASE)
}
-fn string_to_timestamp(s: &str) -> Result {
+fn string_to_timestamp(s: &str) -> Result {
let micros_since_unix_epoch = u64::from_str_radix(s, INT_ENCODE_BASE as u32);
if micros_since_unix_epoch.is_err() {
- return Err(());
+ return Err("timestamp not an int");
}
let micros_since_unix_epoch = micros_since_unix_epoch.unwrap();
diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs
index dc41630196b9..a5b2ccce85e1 100644
--- a/compiler/rustc_infer/src/traits/mod.rs
+++ b/compiler/rustc_infer/src/traits/mod.rs
@@ -9,6 +9,7 @@ mod structural_impls;
pub mod util;
use std::cmp;
+use std::hash::{Hash, Hasher};
use hir::def_id::LocalDefId;
use rustc_hir as hir;
@@ -36,7 +37,7 @@ pub use rustc_middle::traits::*;
/// either identifying an `impl` (e.g., `impl Eq for i32`) that
/// satisfies the obligation, or else finding a bound that is in
/// scope. The eventual result is usually a `Selection` (defined below).
-#[derive(Clone, PartialEq, Eq, Hash)]
+#[derive(Clone)]
pub struct Obligation<'tcx, T> {
/// The reason we have to prove this thing.
pub cause: ObligationCause<'tcx>,
@@ -55,6 +56,27 @@ pub struct Obligation<'tcx, T> {
pub recursion_depth: usize,
}
+impl<'tcx, T: PartialEq> PartialEq> for Obligation<'tcx, T> {
+ #[inline]
+ fn eq(&self, other: &Obligation<'tcx, T>) -> bool {
+ // Ignore `cause` and `recursion_depth`. This is a small performance
+ // win for a few crates, and a huge performance win for the crate in
+ // https://github.com/rust-lang/rustc-perf/pull/1680, which greatly
+ // stresses the trait system.
+ self.param_env == other.param_env && self.predicate == other.predicate
+ }
+}
+
+impl Eq for Obligation<'_, T> {}
+
+impl Hash for Obligation<'_, T> {
+ fn hash(&self, state: &mut H) -> () {
+ // See the comment on `Obligation::eq`.
+ self.param_env.hash(state);
+ self.predicate.hash(state);
+ }
+}
+
impl<'tcx, P> From> for solve::Goal<'tcx, P> {
fn from(value: Obligation<'tcx, P>) -> Self {
solve::Goal { param_env: value.param_env, predicate: value.predicate }
diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs
index 43f50a04aadd..2577cabb3f0f 100644
--- a/compiler/rustc_lint/src/reference_casting.rs
+++ b/compiler/rustc_lint/src/reference_casting.rs
@@ -100,7 +100,7 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
fn from_casts<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
// as *mut ...
- let e = if let ExprKind::Cast(e, t) = e.kind
+ let mut e = if let ExprKind::Cast(e, t) = e.kind
&& let ty::RawPtr(TypeAndMut { mutbl: Mutability::Mut, .. }) = cx.typeck_results().node_type(t.hir_id).kind() {
e
// .cast_mut()
@@ -112,23 +112,36 @@ fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>)
return None;
};
- let e = e.peel_blocks();
-
- // as *const ...
- let e = if let ExprKind::Cast(e, t) = e.kind
- && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) = cx.typeck_results().node_type(t.hir_id).kind() {
- e
- // ptr::from_ref()
- } else if let ExprKind::Call(path, [arg]) = e.kind
- && let ExprKind::Path(ref qpath) = path.kind
- && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
- && cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) {
- arg
- } else {
- return None;
- };
-
- Some(e)
+ let mut had_at_least_one_cast = false;
+ loop {
+ e = e.peel_blocks();
+ // as *mut/const ... or as
+ e = if let ExprKind::Cast(expr, t) = e.kind
+ && matches!(cx.typeck_results().node_type(t.hir_id).kind(), ty::RawPtr(_) | ty::Uint(_)) {
+ had_at_least_one_cast = true;
+ expr
+ // .cast(), .cast_mut() or .cast_const()
+ } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
+ && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+ && matches!(
+ cx.tcx.get_diagnostic_name(def_id),
+ Some(sym::ptr_cast | sym::const_ptr_cast | sym::ptr_cast_mut | sym::ptr_cast_const)
+ )
+ {
+ had_at_least_one_cast = true;
+ expr
+ // ptr::from_ref()
+ } else if let ExprKind::Call(path, [arg]) = e.kind
+ && let ExprKind::Path(ref qpath) = path.kind
+ && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+ && cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) {
+ return Some(arg);
+ } else if had_at_least_one_cast {
+ return Some(e);
+ } else {
+ return None;
+ };
+ }
}
fn from_transmute<'tcx>(
diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs
index f1e7b8eb6c74..ba75517d7a66 100644
--- a/compiler/rustc_macros/src/serialize.rs
+++ b/compiler/rustc_macros/src/serialize.rs
@@ -59,14 +59,14 @@ fn decodable_body(
})
.collect();
let message = format!(
- "invalid enum variant tag while decoding `{}`, expected 0..{}",
+ "invalid enum variant tag while decoding `{}`, expected 0..{}, actual {{}}",
ty_name,
variants.len()
);
quote! {
match ::rustc_serialize::Decoder::read_usize(__decoder) {
#match_inner
- _ => panic!(#message),
+ n => panic!(#message, n),
}
}
}
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index e8fd469e1fb8..0da8fe9cca72 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -164,18 +164,15 @@ pub fn provide(providers: &mut Providers) {
tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs)
};
providers.def_span = |tcx, def_id| {
- let def_id = def_id;
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
tcx.hir().opt_span(hir_id).unwrap_or(DUMMY_SP)
};
providers.def_ident_span = |tcx, def_id| {
- let def_id = def_id;
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
tcx.hir().opt_ident_span(hir_id)
};
- providers.fn_arg_names = |tcx, id| {
+ providers.fn_arg_names = |tcx, def_id| {
let hir = tcx.hir();
- let def_id = id;
let hir_id = hir.local_def_id_to_hir_id(def_id);
if let Some(body_id) = hir.maybe_body_owned_by(def_id) {
tcx.arena.alloc_from_iter(hir.body_param_names(body_id))
@@ -190,7 +187,7 @@ pub fn provide(providers: &mut Providers) {
{
idents
} else {
- span_bug!(hir.span(hir_id), "fn_arg_names: unexpected item {:?}", id);
+ span_bug!(hir.span(hir_id), "fn_arg_names: unexpected item {:?}", def_id);
}
};
providers.opt_def_kind = |tcx, def_id| tcx.hir().opt_def_kind(def_id);
diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs
index d7d6e3a0086c..1efb54bdb087 100644
--- a/compiler/rustc_middle/src/mir/coverage.rs
+++ b/compiler/rustc_middle/src/mir/coverage.rs
@@ -96,21 +96,6 @@ pub enum CoverageKind {
Unreachable,
}
-impl CoverageKind {
- pub fn as_operand(&self) -> Operand {
- use CoverageKind::*;
- match *self {
- Counter { id, .. } => Operand::Counter(id),
- Expression { id, .. } => Operand::Expression(id),
- Unreachable => bug!("Unreachable coverage cannot be part of an expression"),
- }
- }
-
- pub fn is_expression(&self) -> bool {
- matches!(self, Self::Expression { .. })
- }
-}
-
impl Debug for CoverageKind {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
use CoverageKind::*;
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index ddb5e248cdc5..9ef3a1b30e49 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1109,10 +1109,6 @@ pub struct VarDebugInfo<'tcx> {
/// originated from (starting from 1). Note, if MIR inlining is enabled, then this is the
/// argument number in the original function before it was inlined.
pub argument_index: Option,
-
- /// The data represents `name` dereferenced `references` times,
- /// and not the direct value.
- pub references: u8,
}
///////////////////////////////////////////////////////////////////////////
diff --git a/compiler/rustc_middle/src/mir/patch.rs b/compiler/rustc_middle/src/mir/patch.rs
index c4c3341f873a..cd74a403ff6b 100644
--- a/compiler/rustc_middle/src/mir/patch.rs
+++ b/compiler/rustc_middle/src/mir/patch.rs
@@ -35,7 +35,7 @@ impl<'tcx> MirPatch<'tcx> {
for (bb, block) in body.basic_blocks.iter_enumerated() {
// Check if we already have a resume block
- if let TerminatorKind::Resume = block.terminator().kind && block.statements.is_empty() {
+ if let TerminatorKind::UnwindResume = block.terminator().kind && block.statements.is_empty() {
result.resume_block = Some(bb);
continue;
}
@@ -50,7 +50,7 @@ impl<'tcx> MirPatch<'tcx> {
}
// Check if we already have a terminate block
- if let TerminatorKind::Terminate = block.terminator().kind && block.statements.is_empty() {
+ if let TerminatorKind::UnwindTerminate = block.terminator().kind && block.statements.is_empty() {
result.terminate_block = Some(bb);
continue;
}
@@ -68,7 +68,7 @@ impl<'tcx> MirPatch<'tcx> {
statements: vec![],
terminator: Some(Terminator {
source_info: SourceInfo::outermost(self.body_span),
- kind: TerminatorKind::Resume,
+ kind: TerminatorKind::UnwindResume,
}),
is_cleanup: true,
});
@@ -102,7 +102,7 @@ impl<'tcx> MirPatch<'tcx> {
statements: vec![],
terminator: Some(Terminator {
source_info: SourceInfo::outermost(self.body_span),
- kind: TerminatorKind::Terminate,
+ kind: TerminatorKind::UnwindTerminate,
}),
is_cleanup: true,
});
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 27e391370923..773056e8a179 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -555,13 +555,8 @@ fn write_scope_tree(
}
let indented_debug_info = format!(
- "{0:1$}debug {2} => {3:&<4$}{5:?};",
- INDENT,
- indent,
- var_debug_info.name,
- "",
- var_debug_info.references as usize,
- var_debug_info.value,
+ "{0:1$}debug {2} => {3:?};",
+ INDENT, indent, var_debug_info.name, var_debug_info.value,
);
if tcx.sess.opts.unstable_opts.mir_include_spans {
diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs
index 20a9e6889e40..a5358687c14c 100644
--- a/compiler/rustc_middle/src/mir/spanview.rs
+++ b/compiler/rustc_middle/src/mir/spanview.rs
@@ -238,45 +238,6 @@ pub fn source_range_no_file(tcx: TyCtxt<'_>, span: Span) -> String {
format!("{}:{}-{}:{}", start.line, start.col.to_usize() + 1, end.line, end.col.to_usize() + 1)
}
-pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str {
- use StatementKind::*;
- match statement.kind {
- Assign(..) => "Assign",
- FakeRead(..) => "FakeRead",
- SetDiscriminant { .. } => "SetDiscriminant",
- Deinit(..) => "Deinit",
- StorageLive(..) => "StorageLive",
- StorageDead(..) => "StorageDead",
- Retag(..) => "Retag",
- PlaceMention(..) => "PlaceMention",
- AscribeUserType(..) => "AscribeUserType",
- Coverage(..) => "Coverage",
- Intrinsic(..) => "Intrinsic",
- ConstEvalCounter => "ConstEvalCounter",
- Nop => "Nop",
- }
-}
-
-pub fn terminator_kind_name(term: &Terminator<'_>) -> &'static str {
- use TerminatorKind::*;
- match term.kind {
- Goto { .. } => "Goto",
- SwitchInt { .. } => "SwitchInt",
- Resume => "Resume",
- Terminate => "Terminate",
- Return => "Return",
- Unreachable => "Unreachable",
- Drop { .. } => "Drop",
- Call { .. } => "Call",
- Assert { .. } => "Assert",
- Yield { .. } => "Yield",
- GeneratorDrop => "GeneratorDrop",
- FalseEdge { .. } => "FalseEdge",
- FalseUnwind { .. } => "FalseUnwind",
- InlineAsm { .. } => "InlineAsm",
- }
-}
-
fn statement_span_viewable<'tcx>(
tcx: TyCtxt<'tcx>,
body_span: Span,
@@ -304,7 +265,7 @@ fn terminator_span_viewable<'tcx>(
if !body_span.contains(span) {
return None;
}
- let id = format!("{}:{}", bb.index(), terminator_kind_name(term));
+ let id = format!("{}:{}", bb.index(), term.kind.name());
let tooltip = tooltip(tcx, &id, span, vec![], &data.terminator);
Some(SpanViewable { bb, span, id, tooltip })
}
@@ -631,7 +592,7 @@ fn tooltip<'tcx>(
"\n{}{}: {}: {:?}",
TOOLTIP_INDENT,
source_range,
- statement_kind_name(&statement),
+ statement.kind.name(),
statement
));
}
@@ -641,7 +602,7 @@ fn tooltip<'tcx>(
"\n{}{}: {}: {:?}",
TOOLTIP_INDENT,
source_range,
- terminator_kind_name(term),
+ term.kind.name(),
term.kind
));
}
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index be27bf75dbd1..e91e822f915a 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -380,6 +380,28 @@ pub enum StatementKind<'tcx> {
Nop,
}
+impl StatementKind<'_> {
+ /// Returns a simple string representation of a `StatementKind` variant, independent of any
+ /// values it might hold (e.g. `StatementKind::Assign` always returns `"Assign"`).
+ pub const fn name(&self) -> &'static str {
+ match self {
+ StatementKind::Assign(..) => "Assign",
+ StatementKind::FakeRead(..) => "FakeRead",
+ StatementKind::SetDiscriminant { .. } => "SetDiscriminant",
+ StatementKind::Deinit(..) => "Deinit",
+ StatementKind::StorageLive(..) => "StorageLive",
+ StatementKind::StorageDead(..) => "StorageDead",
+ StatementKind::Retag(..) => "Retag",
+ StatementKind::PlaceMention(..) => "PlaceMention",
+ StatementKind::AscribeUserType(..) => "AscribeUserType",
+ StatementKind::Coverage(..) => "Coverage",
+ StatementKind::Intrinsic(..) => "Intrinsic",
+ StatementKind::ConstEvalCounter => "ConstEvalCounter",
+ StatementKind::Nop => "Nop",
+ }
+ }
+}
+
#[derive(
Clone,
TyEncodable,
@@ -593,13 +615,13 @@ pub enum TerminatorKind<'tcx> {
///
/// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after
/// deaggregation runs.
- Resume,
+ UnwindResume,
/// Indicates that the landing pad is finished and that the process should terminate.
///
/// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in
/// cleanup blocks.
- Terminate,
+ UnwindTerminate,
/// Returns from the function.
///
@@ -790,8 +812,8 @@ impl TerminatorKind<'_> {
match self {
TerminatorKind::Goto { .. } => "Goto",
TerminatorKind::SwitchInt { .. } => "SwitchInt",
- TerminatorKind::Resume => "Resume",
- TerminatorKind::Terminate => "Terminate",
+ TerminatorKind::UnwindResume => "UnwindResume",
+ TerminatorKind::UnwindTerminate => "UnwindTerminate",
TerminatorKind::Return => "Return",
TerminatorKind::Unreachable => "Unreachable",
TerminatorKind::Drop { .. } => "Drop",
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index 6de843515950..bd87563e2bb4 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -10,6 +10,7 @@ use std::iter;
use std::slice;
pub use super::query::*;
+use super::*;
#[derive(Debug, Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
pub struct SwitchTargets {
@@ -154,8 +155,8 @@ impl<'tcx> TerminatorKind<'tcx> {
| InlineAsm { destination: Some(t), unwind: _, .. } => {
Some(t).into_iter().chain((&[]).into_iter().copied())
}
- Resume
- | Terminate
+ UnwindResume
+ | UnwindTerminate
| GeneratorDrop
| Return
| Unreachable
@@ -196,8 +197,8 @@ impl<'tcx> TerminatorKind<'tcx> {
| InlineAsm { destination: Some(ref mut t), unwind: _, .. } => {
Some(t).into_iter().chain(&mut [])
}
- Resume
- | Terminate
+ UnwindResume
+ | UnwindTerminate
| GeneratorDrop
| Return
| Unreachable
@@ -213,8 +214,8 @@ impl<'tcx> TerminatorKind<'tcx> {
pub fn unwind(&self) -> Option<&UnwindAction> {
match *self {
TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop
@@ -232,8 +233,8 @@ impl<'tcx> TerminatorKind<'tcx> {
pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
match *self {
TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop
@@ -310,8 +311,8 @@ impl<'tcx> TerminatorKind<'tcx> {
SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"),
Return => write!(fmt, "return"),
GeneratorDrop => write!(fmt, "generator_drop"),
- Resume => write!(fmt, "resume"),
- Terminate => write!(fmt, "abort"),
+ UnwindResume => write!(fmt, "resume"),
+ UnwindTerminate => write!(fmt, "abort"),
Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),
Unreachable => write!(fmt, "unreachable"),
Drop { place, .. } => write!(fmt, "drop({place:?})"),
@@ -390,7 +391,7 @@ impl<'tcx> TerminatorKind<'tcx> {
pub fn fmt_successor_labels(&self) -> Vec> {
use self::TerminatorKind::*;
match *self {
- Return | Resume | Terminate | Unreachable | GeneratorDrop => vec![],
+ Return | UnwindResume | UnwindTerminate | Unreachable | GeneratorDrop => vec![],
Goto { .. } => vec!["".into()],
SwitchInt { ref targets, .. } => targets
.values
@@ -430,3 +431,110 @@ impl<'tcx> TerminatorKind<'tcx> {
}
}
}
+
+#[derive(Copy, Clone, Debug)]
+pub enum TerminatorEdges<'mir, 'tcx> {
+ /// For terminators that have no successor, like `return`.
+ None,
+ /// For terminators that a single successor, like `goto`, and `assert` without cleanup block.
+ Single(BasicBlock),
+ /// For terminators that two successors, `assert` with cleanup block and `falseEdge`.
+ Double(BasicBlock, BasicBlock),
+ /// Special action for `Yield`, `Call` and `InlineAsm` terminators.
+ AssignOnReturn {
+ return_: Option,
+ unwind: UnwindAction,
+ place: CallReturnPlaces<'mir, 'tcx>,
+ },
+ /// Special edge for `SwitchInt`.
+ SwitchInt { targets: &'mir SwitchTargets, discr: &'mir Operand<'tcx> },
+}
+
+/// List of places that are written to after a successful (non-unwind) return
+/// from a `Call`, `Yield` or `InlineAsm`.
+#[derive(Copy, Clone, Debug)]
+pub enum CallReturnPlaces<'a, 'tcx> {
+ Call(Place<'tcx>),
+ Yield(Place<'tcx>),
+ InlineAsm(&'a [InlineAsmOperand<'tcx>]),
+}
+
+impl<'tcx> CallReturnPlaces<'_, 'tcx> {
+ pub fn for_each(&self, mut f: impl FnMut(Place<'tcx>)) {
+ match *self {
+ Self::Call(place) | Self::Yield(place) => f(place),
+ Self::InlineAsm(operands) => {
+ for op in operands {
+ match *op {
+ InlineAsmOperand::Out { place: Some(place), .. }
+ | InlineAsmOperand::InOut { out_place: Some(place), .. } => f(place),
+ _ => {}
+ }
+ }
+ }
+ }
+ }
+}
+
+impl<'tcx> Terminator<'tcx> {
+ pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
+ self.kind.edges()
+ }
+}
+
+impl<'tcx> TerminatorKind<'tcx> {
+ pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
+ use TerminatorKind::*;
+ match *self {
+ Return | UnwindResume | UnwindTerminate | GeneratorDrop | Unreachable => {
+ TerminatorEdges::None
+ }
+
+ Goto { target } => TerminatorEdges::Single(target),
+
+ Assert { target, unwind, expected: _, msg: _, cond: _ }
+ | Drop { target, unwind, place: _, replace: _ }
+ | FalseUnwind { real_target: target, unwind } => match unwind {
+ UnwindAction::Cleanup(unwind) => TerminatorEdges::Double(target, unwind),
+ UnwindAction::Continue | UnwindAction::Terminate | UnwindAction::Unreachable => {
+ TerminatorEdges::Single(target)
+ }
+ },
+
+ FalseEdge { real_target, imaginary_target } => {
+ TerminatorEdges::Double(real_target, imaginary_target)
+ }
+
+ Yield { resume: target, drop, resume_arg, value: _ } => {
+ TerminatorEdges::AssignOnReturn {
+ return_: Some(target),
+ unwind: drop.map_or(UnwindAction::Terminate, UnwindAction::Cleanup),
+ place: CallReturnPlaces::Yield(resume_arg),
+ }
+ }
+
+ Call { unwind, destination, target, func: _, args: _, fn_span: _, call_source: _ } => {
+ TerminatorEdges::AssignOnReturn {
+ return_: target,
+ unwind,
+ place: CallReturnPlaces::Call(destination),
+ }
+ }
+
+ InlineAsm {
+ template: _,
+ ref operands,
+ options: _,
+ line_spans: _,
+ destination,
+ unwind,
+ } => TerminatorEdges::AssignOnReturn {
+ return_: destination,
+ unwind,
+ place: CallReturnPlaces::InlineAsm(operands),
+ },
+
+ SwitchInt { ref targets, ref discr } => TerminatorEdges::SwitchInt { targets, discr },
+ }
+ }
+}
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 64bc4fa7926d..b3d3366ae104 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -469,8 +469,8 @@ macro_rules! make_mir_visitor {
self.visit_source_info(source_info);
match kind {
TerminatorKind::Goto { .. } |
- TerminatorKind::Resume |
- TerminatorKind::Terminate |
+ TerminatorKind::UnwindResume |
+ TerminatorKind::UnwindTerminate |
TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable |
TerminatorKind::FalseEdge { .. } |
@@ -840,7 +840,6 @@ macro_rules! make_mir_visitor {
source_info,
value,
argument_index: _,
- references: _,
} = var_debug_info;
self.visit_source_info(source_info);
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index e289ddab1202..94ae0dcb5172 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -388,7 +388,6 @@ rustc_queries! {
}
query shallow_lint_levels_on(key: hir::OwnerId) -> &'tcx rustc_middle::lint::ShallowLintLevelMap {
- eval_always // fetches `resolutions`
arena_cache
desc { |tcx| "looking up lint levels for `{}`", tcx.def_path_str(key) }
}
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index e71482326da7..5db9b775a0f0 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -1,6 +1,7 @@
//! Diagnostics related methods for `Ty`.
use std::borrow::Cow;
+use std::fmt::Write;
use std::ops::ControlFlow;
use crate::ty::{
@@ -335,10 +336,10 @@ pub fn suggest_constraining_type_params<'a>(
// - insert: `, X: Bar`
suggestions.push((
generics.tail_span_for_predicate_suggestion(),
- constraints
- .iter()
- .map(|&(constraint, _)| format!(", {param_name}: {constraint}"))
- .collect::(),
+ constraints.iter().fold(String::new(), |mut string, &(constraint, _)| {
+ write!(string, ", {param_name}: {constraint}").unwrap();
+ string
+ }),
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
));
continue;
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 1347b35556d3..f979ddd00fa0 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -438,7 +438,6 @@ CloneLiftImpls! {
(),
bool,
usize,
- u8,
u16,
u32,
u64,
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index e6baa6242052..0291cdd6c579 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2827,11 +2827,11 @@ impl<'tcx> Ty<'tcx> {
ty::Adt(def, _args) => def.sized_constraint(tcx).skip_binder().is_empty(),
- ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => false,
+ ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) | ty::Bound(..) => false,
ty::Infer(ty::TyVar(_)) => false,
- ty::Bound(..) | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+ ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("`is_trivially_sized` applied to unexpected type: {:?}", self)
}
}
diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
index fe5190900e94..26662f5de459 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
@@ -61,9 +61,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
})
},
@call("mir_call", args) => {
- let destination = self.parse_place(args[0])?;
- let target = self.parse_block(args[1])?;
- self.parse_call(args[2], destination, target)
+ self.parse_call(args)
},
ExprKind::Match { scrutinee, arms, .. } => {
let discr = self.parse_operand(*scrutinee)?;
@@ -109,13 +107,14 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
Ok(SwitchTargets::new(values.into_iter().zip(targets), otherwise))
}
- fn parse_call(
- &self,
- expr_id: ExprId,
- destination: Place<'tcx>,
- target: BasicBlock,
- ) -> PResult> {
- parse_by_kind!(self, expr_id, _, "function call",
+ fn parse_call(&self, args: &[ExprId]) -> PResult> {
+ let (destination, call) = parse_by_kind!(self, args[0], _, "function call",
+ ExprKind::Assign { lhs, rhs } => (*lhs, *rhs),
+ );
+ let destination = self.parse_place(destination)?;
+ let target = self.parse_block(args[1])?;
+
+ parse_by_kind!(self, call, _, "function call",
ExprKind::Call { fun, args, from_hir_call, fn_span, .. } => {
let fun = self.parse_operand(*fun)?;
let args = args
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index ed3ac7cb3ec7..3c450740712c 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -2242,7 +2242,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.var_debug_info.push(VarDebugInfo {
name,
source_info: debug_source_info,
- references: 0,
value: VarDebugInfoContents::Place(for_arm_body.into()),
argument_index: None,
});
@@ -2262,7 +2261,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.var_debug_info.push(VarDebugInfo {
name,
source_info: debug_source_info,
- references: 0,
value: VarDebugInfoContents::Place(ref_for_guard.into()),
argument_index: None,
});
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index c66eba5520e1..2a23a69b5841 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -820,7 +820,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
};
self.var_debug_info.push(VarDebugInfo {
name,
- references: 0,
source_info: SourceInfo::outermost(captured_place.var_ident.span),
value: VarDebugInfoContents::Place(use_place),
argument_index: None,
@@ -851,7 +850,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.var_debug_info.push(VarDebugInfo {
name,
source_info,
- references: 0,
value: VarDebugInfoContents::Place(arg_local.into()),
argument_index: Some(argument_index as u16 + 1),
});
diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs
index a96288a11e58..567e7bfb5bf8 100644
--- a/compiler/rustc_mir_build/src/build/scope.rs
+++ b/compiler/rustc_mir_build/src/build/scope.rs
@@ -685,9 +685,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
drops.add_entry(block, drop_idx);
// `build_drop_trees` doesn't have access to our source_info, so we
- // create a dummy terminator now. `TerminatorKind::Resume` is used
+ // create a dummy terminator now. `TerminatorKind::UnwindResume` is used
// because MIR type checking will panic if it hasn't been overwritten.
- self.cfg.terminate(block, source_info, TerminatorKind::Resume);
+ self.cfg.terminate(block, source_info, TerminatorKind::UnwindResume);
self.cfg.start_new_block().unit()
}
@@ -717,9 +717,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
drops.add_entry(block, drop_idx);
// `build_drop_trees` doesn't have access to our source_info, so we
- // create a dummy terminator now. `TerminatorKind::Resume` is used
+ // create a dummy terminator now. `TerminatorKind::UnwindResume` is used
// because MIR type checking will panic if it hasn't been overwritten.
- self.cfg.terminate(block, source_info, TerminatorKind::Resume);
+ self.cfg.terminate(block, source_info, TerminatorKind::UnwindResume);
}
// Add a dummy `Assign` statement to the CFG, with the span for the source code's `continue`
@@ -1441,7 +1441,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
blocks[ROOT_NODE] = *resume_block;
drops.build_mir::(cfg, &mut blocks);
if let (None, Some(resume)) = (*resume_block, blocks[ROOT_NODE]) {
- cfg.terminate(resume, SourceInfo::outermost(fn_span), TerminatorKind::Resume);
+ cfg.terminate(resume, SourceInfo::outermost(fn_span), TerminatorKind::UnwindResume);
*resume_block = blocks[ROOT_NODE];
}
@@ -1506,8 +1506,8 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
}
TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Yield { .. }
diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs
index 7fb73b5c7b2f..84c80bf41a4d 100644
--- a/compiler/rustc_mir_build/src/lints.rs
+++ b/compiler/rustc_mir_build/src/lints.rs
@@ -186,9 +186,9 @@ impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor ControlFlow::Break(NonRecursive),
diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs
index 804b44a6bf05..8a9e37c5a4fd 100644
--- a/compiler/rustc_mir_dataflow/src/framework/direction.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs
@@ -1,11 +1,10 @@
-use rustc_middle::mir::{self, BasicBlock, Location, SwitchTargets, UnwindAction};
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::mir::{
+ self, BasicBlock, CallReturnPlaces, Location, SwitchTargets, TerminatorEdges, UnwindAction,
+};
use std::ops::RangeInclusive;
use super::visitor::{ResultsVisitable, ResultsVisitor};
-use super::{
- Analysis, CallReturnPlaces, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget,
-};
+use super::{Analysis, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget};
pub trait Direction {
const IS_FORWARD: bool;
@@ -24,15 +23,17 @@ pub trait Direction {
) where
A: Analysis<'tcx>;
- fn apply_effects_in_block<'tcx, A>(
+ fn apply_effects_in_block<'mir, 'tcx, A>(
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
- block_data: &mir::BasicBlockData<'tcx>,
- ) where
+ block_data: &'mir mir::BasicBlockData<'tcx>,
+ statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
+ ) -> TerminatorEdges<'mir, 'tcx>
+ where
A: Analysis<'tcx>;
- fn gen_kill_effects_in_block<'tcx, A>(
+ fn gen_kill_statement_effects_in_block<'tcx, A>(
analysis: &mut A,
trans: &mut GenKillSet,
block: BasicBlock,
@@ -51,10 +52,10 @@ pub trait Direction {
fn join_state_into_successors_of<'tcx, A>(
analysis: &mut A,
- tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
- block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
+ block: BasicBlock,
+ edges: TerminatorEdges<'_, 'tcx>,
propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>;
@@ -66,27 +67,33 @@ pub struct Backward;
impl Direction for Backward {
const IS_FORWARD: bool = false;
- fn apply_effects_in_block<'tcx, A>(
+ fn apply_effects_in_block<'mir, 'tcx, A>(
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
- block_data: &mir::BasicBlockData<'tcx>,
- ) where
+ block_data: &'mir mir::BasicBlockData<'tcx>,
+ statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
+ ) -> TerminatorEdges<'mir, 'tcx>
+ where
A: Analysis<'tcx>,
{
let terminator = block_data.terminator();
let location = Location { block, statement_index: block_data.statements.len() };
analysis.apply_before_terminator_effect(state, terminator, location);
- analysis.apply_terminator_effect(state, terminator, location);
-
- for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
- let location = Location { block, statement_index };
- analysis.apply_before_statement_effect(state, statement, location);
- analysis.apply_statement_effect(state, statement, location);
+ let edges = analysis.apply_terminator_effect(state, terminator, location);
+ if let Some(statement_effect) = statement_effect {
+ statement_effect(block, state)
+ } else {
+ for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
+ let location = Location { block, statement_index };
+ analysis.apply_before_statement_effect(state, statement, location);
+ analysis.apply_statement_effect(state, statement, location);
+ }
}
+ edges
}
- fn gen_kill_effects_in_block<'tcx, A>(
+ fn gen_kill_statement_effects_in_block<'tcx, A>(
analysis: &mut A,
trans: &mut GenKillSet,
block: BasicBlock,
@@ -94,11 +101,6 @@ impl Direction for Backward {
) where
A: GenKillAnalysis<'tcx>,
{
- let terminator = block_data.terminator();
- let location = Location { block, statement_index: block_data.statements.len() };
- analysis.before_terminator_effect(trans, terminator, location);
- analysis.terminator_effect(trans, terminator, location);
-
for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
let location = Location { block, statement_index };
analysis.before_statement_effect(trans, statement, location);
@@ -217,10 +219,10 @@ impl Direction for Backward {
fn join_state_into_successors_of<'tcx, A>(
analysis: &mut A,
- _tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
- (bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
+ bb: BasicBlock,
+ _edges: TerminatorEdges<'_, 'tcx>,
mut propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>,
@@ -254,7 +256,11 @@ impl Direction for Backward {
mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == bb => {
let mut tmp = exit_state.clone();
- analysis.apply_yield_resume_effect(&mut tmp, resume, resume_arg);
+ analysis.apply_call_return_effect(
+ &mut tmp,
+ resume,
+ CallReturnPlaces::Yield(resume_arg),
+ );
propagate(pred, &tmp);
}
@@ -318,27 +324,33 @@ pub struct Forward;
impl Direction for Forward {
const IS_FORWARD: bool = true;
- fn apply_effects_in_block<'tcx, A>(
+ fn apply_effects_in_block<'mir, 'tcx, A>(
analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
- block_data: &mir::BasicBlockData<'tcx>,
- ) where
+ block_data: &'mir mir::BasicBlockData<'tcx>,
+ statement_effect: Option<&dyn Fn(BasicBlock, &mut A::Domain)>,
+ ) -> TerminatorEdges<'mir, 'tcx>
+ where
A: Analysis<'tcx>,
{
- for (statement_index, statement) in block_data.statements.iter().enumerate() {
- let location = Location { block, statement_index };
- analysis.apply_before_statement_effect(state, statement, location);
- analysis.apply_statement_effect(state, statement, location);
+ if let Some(statement_effect) = statement_effect {
+ statement_effect(block, state)
+ } else {
+ for (statement_index, statement) in block_data.statements.iter().enumerate() {
+ let location = Location { block, statement_index };
+ analysis.apply_before_statement_effect(state, statement, location);
+ analysis.apply_statement_effect(state, statement, location);
+ }
}
let terminator = block_data.terminator();
let location = Location { block, statement_index: block_data.statements.len() };
analysis.apply_before_terminator_effect(state, terminator, location);
- analysis.apply_terminator_effect(state, terminator, location);
+ analysis.apply_terminator_effect(state, terminator, location)
}
- fn gen_kill_effects_in_block<'tcx, A>(
+ fn gen_kill_statement_effects_in_block<'tcx, A>(
analysis: &mut A,
trans: &mut GenKillSet,
block: BasicBlock,
@@ -351,11 +363,6 @@ impl Direction for Forward {
analysis.before_statement_effect(trans, statement, location);
analysis.statement_effect(trans, statement, location);
}
-
- let terminator = block_data.terminator();
- let location = Location { block, statement_index: block_data.statements.len() };
- analysis.before_terminator_effect(trans, terminator, location);
- analysis.terminator_effect(trans, terminator, location);
}
fn apply_effects_in_range<'tcx, A>(
@@ -464,86 +471,32 @@ impl Direction for Forward {
fn join_state_into_successors_of<'tcx, A>(
analysis: &mut A,
- _tcx: TyCtxt<'tcx>,
_body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
- (bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
+ bb: BasicBlock,
+ edges: TerminatorEdges<'_, 'tcx>,
mut propagate: impl FnMut(BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>,
{
- use mir::TerminatorKind::*;
- match bb_data.terminator().kind {
- Return | Resume | Terminate | GeneratorDrop | Unreachable => {}
-
- Goto { target } => propagate(target, exit_state),
-
- Assert { target, unwind, expected: _, msg: _, cond: _ }
- | Drop { target, unwind, place: _, replace: _ }
- | FalseUnwind { real_target: target, unwind } => {
- if let UnwindAction::Cleanup(unwind) = unwind {
- propagate(unwind, exit_state);
- }
-
+ match edges {
+ TerminatorEdges::None => {}
+ TerminatorEdges::Single(target) => propagate(target, exit_state),
+ TerminatorEdges::Double(target, unwind) => {
propagate(target, exit_state);
+ propagate(unwind, exit_state);
}
-
- FalseEdge { real_target, imaginary_target } => {
- propagate(real_target, exit_state);
- propagate(imaginary_target, exit_state);
- }
-
- Yield { resume: target, drop, resume_arg, value: _ } => {
- if let Some(drop) = drop {
- propagate(drop, exit_state);
- }
-
- analysis.apply_yield_resume_effect(exit_state, target, resume_arg);
- propagate(target, exit_state);
- }
-
- Call { unwind, destination, target, func: _, args: _, call_source: _, fn_span: _ } => {
+ TerminatorEdges::AssignOnReturn { return_, unwind, place } => {
+ // This must be done *first*, otherwise the unwind path will see the assignments.
if let UnwindAction::Cleanup(unwind) = unwind {
propagate(unwind, exit_state);
}
-
- if let Some(target) = target {
- // N.B.: This must be done *last*, otherwise the unwind path will see the call
- // return effect.
- analysis.apply_call_return_effect(
- exit_state,
- bb,
- CallReturnPlaces::Call(destination),
- );
- propagate(target, exit_state);
+ if let Some(return_) = return_ {
+ analysis.apply_call_return_effect(exit_state, bb, place);
+ propagate(return_, exit_state);
}
}
-
- InlineAsm {
- template: _,
- ref operands,
- options: _,
- line_spans: _,
- destination,
- unwind,
- } => {
- if let UnwindAction::Cleanup(unwind) = unwind {
- propagate(unwind, exit_state);
- }
-
- if let Some(target) = destination {
- // N.B.: This must be done *last*, otherwise the unwind path will see the call
- // return effect.
- analysis.apply_call_return_effect(
- exit_state,
- bb,
- CallReturnPlaces::InlineAsm(operands),
- );
- propagate(target, exit_state);
- }
- }
-
- SwitchInt { ref targets, ref discr } => {
+ TerminatorEdges::SwitchInt { targets, discr } => {
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
exit_state,
targets,
diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs
index c755d7588c25..a29962d7717a 100644
--- a/compiler/rustc_mir_dataflow/src/framework/engine.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs
@@ -144,7 +144,7 @@ where
// gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
// performance in practice. I've tried a few ways to avoid this, but they have downsides. See
// the message for the commit that added this FIXME for more information.
- apply_trans_for_block: Option>,
+ apply_statement_trans_for_block: Option>,
}
impl<'a, 'tcx, A, D, T> Engine<'a, 'tcx, A>
@@ -165,12 +165,17 @@ where
// Otherwise, compute and store the cumulative transfer function for each block.
- let identity = GenKillSet::identity(analysis.bottom_value(body).domain_size());
+ let identity = GenKillSet::identity(analysis.domain_size(body));
let mut trans_for_block = IndexVec::from_elem(identity, &body.basic_blocks);
for (block, block_data) in body.basic_blocks.iter_enumerated() {
let trans = &mut trans_for_block[block];
- A::Direction::gen_kill_effects_in_block(&mut analysis, trans, block, block_data);
+ A::Direction::gen_kill_statement_effects_in_block(
+ &mut analysis,
+ trans,
+ block,
+ block_data,
+ );
}
let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
@@ -199,17 +204,18 @@ where
tcx: TyCtxt<'tcx>,
body: &'a mir::Body<'tcx>,
analysis: A,
- apply_trans_for_block: Option>,
+ apply_statement_trans_for_block: Option>,
) -> Self {
- let bottom_value = analysis.bottom_value(body);
- let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), &body.basic_blocks);
+ let mut entry_sets =
+ IndexVec::from_fn_n(|_| analysis.bottom_value(body), body.basic_blocks.len());
analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
- if A::Direction::IS_BACKWARD && entry_sets[mir::START_BLOCK] != bottom_value {
+ if A::Direction::IS_BACKWARD && entry_sets[mir::START_BLOCK] != analysis.bottom_value(body)
+ {
bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
}
- Engine { analysis, tcx, body, pass_name: None, entry_sets, apply_trans_for_block }
+ Engine { analysis, tcx, body, pass_name: None, entry_sets, apply_statement_trans_for_block }
}
/// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
@@ -231,7 +237,7 @@ where
body,
mut entry_sets,
tcx,
- apply_trans_for_block,
+ apply_statement_trans_for_block,
pass_name,
..
} = self;
@@ -263,19 +269,20 @@ where
state.clone_from(&entry_sets[bb]);
// Apply the block transfer function, using the cached one if it exists.
- match &apply_trans_for_block {
- Some(apply) => apply(bb, &mut state),
- None => {
- A::Direction::apply_effects_in_block(&mut analysis, &mut state, bb, bb_data)
- }
- }
+ let edges = A::Direction::apply_effects_in_block(
+ &mut analysis,
+ &mut state,
+ bb,
+ bb_data,
+ apply_statement_trans_for_block.as_deref(),
+ );
A::Direction::join_state_into_successors_of(
&mut analysis,
- tcx,
body,
&mut state,
- (bb, bb_data),
+ bb,
+ edges,
|target: BasicBlock, state: &A::Domain| {
let set_changed = entry_sets[target].join(state);
if set_changed {
diff --git a/compiler/rustc_mir_dataflow/src/framework/fmt.rs b/compiler/rustc_mir_dataflow/src/framework/fmt.rs
index 6a256fae3ca7..e3a66bd952c5 100644
--- a/compiler/rustc_mir_dataflow/src/framework/fmt.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/fmt.rs
@@ -1,6 +1,7 @@
//! Custom formatting traits used when outputting Graphviz diagrams with the results of a dataflow
//! analysis.
+use super::lattice::MaybeReachable;
use rustc_index::bit_set::{BitSet, ChunkedBitSet, HybridBitSet};
use rustc_index::Idx;
use std::fmt;
@@ -124,6 +125,37 @@ where
}
}
+impl DebugWithContext for MaybeReachable
+where
+ S: DebugWithContext,
+{
+ fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ MaybeReachable::Unreachable => {
+ write!(f, "unreachable")
+ }
+ MaybeReachable::Reachable(set) => set.fmt_with(ctxt, f),
+ }
+ }
+
+ fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match (self, old) {
+ (MaybeReachable::Unreachable, MaybeReachable::Unreachable) => Ok(()),
+ (MaybeReachable::Unreachable, MaybeReachable::Reachable(set)) => {
+ write!(f, "\u{001f}+")?;
+ set.fmt_with(ctxt, f)
+ }
+ (MaybeReachable::Reachable(set), MaybeReachable::Unreachable) => {
+ write!(f, "\u{001f}-")?;
+ set.fmt_with(ctxt, f)
+ }
+ (MaybeReachable::Reachable(this), MaybeReachable::Reachable(old)) => {
+ this.fmt_diff_with(old, ctxt, f)
+ }
+ }
+ }
+}
+
fn fmt_diff(
inserted: &HybridBitSet,
removed: &HybridBitSet,
diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
index e331533c3710..1421d9b45cda 100644
--- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
@@ -269,7 +269,11 @@ where
self.write_row(w, "", "(on yield resume)", |this, w, fmt| {
let state_on_generator_drop = this.results.get().clone();
this.results.apply_custom_effect(|analysis, state| {
- analysis.apply_yield_resume_effect(state, resume, resume_arg);
+ analysis.apply_call_return_effect(
+ state,
+ resume,
+ CallReturnPlaces::Yield(resume_arg),
+ );
});
write!(
diff --git a/compiler/rustc_mir_dataflow/src/framework/lattice.rs b/compiler/rustc_mir_dataflow/src/framework/lattice.rs
index 3952f44ad489..3b89598d2899 100644
--- a/compiler/rustc_mir_dataflow/src/framework/lattice.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/lattice.rs
@@ -187,10 +187,6 @@ impl MeetSemiLattice for ChunkedBitSet {
pub struct Dual(pub T);
impl BitSetExt for Dual> {
- fn domain_size(&self) -> usize {
- self.0.domain_size()
- }
-
fn contains(&self, elem: T) -> bool {
self.0.contains(elem)
}
@@ -276,3 +272,93 @@ impl HasBottom for FlatSet {
impl HasTop for FlatSet {
const TOP: Self = Self::Top;
}
+
+/// Extend a lattice with a bottom value to represent an unreachable execution.
+///
+/// The only useful action on an unreachable state is joining it with a reachable one to make it
+/// reachable. All other actions, gen/kill for instance, are no-ops.
+#[derive(PartialEq, Eq, Debug)]
+pub enum MaybeReachable {
+ Unreachable,
+ Reachable(T),
+}
+
+impl MaybeReachable {
+ pub fn is_reachable(&self) -> bool {
+ matches!(self, MaybeReachable::Reachable(_))
+ }
+}
+
+impl HasBottom for MaybeReachable {
+ const BOTTOM: Self = MaybeReachable::Unreachable;
+}
+
+impl HasTop for MaybeReachable {
+ const TOP: Self = MaybeReachable::Reachable(T::TOP);
+}
+
+impl MaybeReachable {
+ /// Return whether the current state contains the given element. If the state is unreachable,
+ /// it does no contain anything.
+ pub fn contains(&self, elem: T) -> bool
+ where
+ S: BitSetExt,
+ {
+ match self {
+ MaybeReachable::Unreachable => false,
+ MaybeReachable::Reachable(set) => set.contains(elem),
+ }
+ }
+}
+
+impl> BitSetExt for MaybeReachable {
+ fn contains(&self, elem: T) -> bool {
+ self.contains(elem)
+ }
+
+ fn union(&mut self, other: &HybridBitSet) {
+ match self {
+ MaybeReachable::Unreachable => {}
+ MaybeReachable::Reachable(set) => set.union(other),
+ }
+ }
+
+ fn subtract(&mut self, other: &HybridBitSet) {
+ match self {
+ MaybeReachable::Unreachable => {}
+ MaybeReachable::Reachable(set) => set.subtract(other),
+ }
+ }
+}
+
+impl Clone for MaybeReachable {
+ fn clone(&self) -> Self {
+ match self {
+ MaybeReachable::Reachable(x) => MaybeReachable::Reachable(x.clone()),
+ MaybeReachable::Unreachable => MaybeReachable::Unreachable,
+ }
+ }
+
+ fn clone_from(&mut self, source: &Self) {
+ match (&mut *self, source) {
+ (MaybeReachable::Reachable(x), MaybeReachable::Reachable(y)) => {
+ x.clone_from(&y);
+ }
+ _ => *self = source.clone(),
+ }
+ }
+}
+
+impl JoinSemiLattice for MaybeReachable {
+ fn join(&mut self, other: &Self) -> bool {
+ // Unreachable acts as a bottom.
+ match (&mut *self, &other) {
+ (_, MaybeReachable::Unreachable) => false,
+ (MaybeReachable::Unreachable, _) => {
+ *self = other.clone();
+ true
+ }
+ (MaybeReachable::Reachable(this), MaybeReachable::Reachable(other)) => this.join(other),
+ }
+ }
+}
diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs
index 58df9b9a768b..ce30c642fcc9 100644
--- a/compiler/rustc_mir_dataflow/src/framework/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs
@@ -34,7 +34,7 @@ use std::cmp::Ordering;
use rustc_index::bit_set::{BitSet, ChunkedBitSet, HybridBitSet};
use rustc_index::Idx;
-use rustc_middle::mir::{self, BasicBlock, Location};
+use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges};
use rustc_middle::ty::TyCtxt;
mod cursor;
@@ -48,23 +48,18 @@ mod visitor;
pub use self::cursor::{AnalysisResults, ResultsClonedCursor, ResultsCursor, ResultsRefCursor};
pub use self::direction::{Backward, Direction, Forward};
pub use self::engine::{Engine, EntrySets, Results, ResultsCloned};
-pub use self::lattice::{JoinSemiLattice, MeetSemiLattice};
+pub use self::lattice::{JoinSemiLattice, MaybeReachable, MeetSemiLattice};
pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor};
/// Analysis domains are all bitsets of various kinds. This trait holds
/// operations needed by all of them.
pub trait BitSetExt {
- fn domain_size(&self) -> usize;
fn contains(&self, elem: T) -> bool;
fn union(&mut self, other: &HybridBitSet);
fn subtract(&mut self, other: &HybridBitSet);
}
impl BitSetExt for BitSet {
- fn domain_size(&self) -> usize {
- self.domain_size()
- }
-
fn contains(&self, elem: T) -> bool {
self.contains(elem)
}
@@ -79,10 +74,6 @@ impl BitSetExt for BitSet {
}
impl BitSetExt for ChunkedBitSet {
- fn domain_size(&self) -> usize {
- self.domain_size()
- }
-
fn contains(&self, elem: T) -> bool {
self.contains(elem)
}
@@ -172,12 +163,12 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
/// in this function. That should go in `apply_call_return_effect`. For example, in the
/// `InitializedPlaces` analyses, the return place for a function call is not marked as
/// initialized here.
- 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>;
/// Updates the current dataflow state with an effect that occurs immediately *before* the
/// given terminator.
@@ -207,20 +198,6 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
return_places: CallReturnPlaces<'_, 'tcx>,
);
- /// Updates the current dataflow state with the effect of resuming from a `Yield` terminator.
- ///
- /// This is similar to `apply_call_return_effect` in that it only takes place after the
- /// generator is resumed, not when it is dropped.
- ///
- /// By default, no effects happen.
- fn apply_yield_resume_effect(
- &mut self,
- _state: &mut Self::Domain,
- _resume_block: BasicBlock,
- _resume_place: mir::Place<'tcx>,
- ) {
- }
-
/// Updates the current dataflow state with the effect of taking a particular branch in a
/// `SwitchInt` terminator.
///
@@ -295,6 +272,8 @@ where
pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
type Idx: Idx;
+ fn domain_size(&self, body: &mir::Body<'tcx>) -> usize;
+
/// See `Analysis::apply_statement_effect`.
fn statement_effect(
&mut self,
@@ -313,12 +292,12 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
}
/// See `Analysis::apply_terminator_effect`.
- fn terminator_effect(
+ fn terminator_effect<'mir>(
&mut self,
- trans: &mut impl GenKill,
- terminator: &mir::Terminator<'tcx>,
+ trans: &mut Self::Domain,
+ terminator: &'mir mir::Terminator<'tcx>,
location: Location,
- );
+ ) -> TerminatorEdges<'mir, 'tcx>;
/// See `Analysis::apply_before_terminator_effect`.
fn before_terminator_effect(
@@ -339,15 +318,6 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
return_places: CallReturnPlaces<'_, 'tcx>,
);
- /// See `Analysis::apply_yield_resume_effect`.
- fn yield_resume_effect(
- &mut self,
- _trans: &mut impl GenKill,
- _resume_block: BasicBlock,
- _resume_place: mir::Place<'tcx>,
- ) {
- }
-
/// See `Analysis::apply_switch_int_edge_effects`.
fn switch_int_edge_effects>(
&mut self,
@@ -381,13 +351,13 @@ where
self.before_statement_effect(state, statement, location);
}
- fn apply_terminator_effect(
+ fn apply_terminator_effect<'mir>(
&mut self,
state: &mut A::Domain,
- terminator: &mir::Terminator<'tcx>,
+ terminator: &'mir mir::Terminator<'tcx>,
location: Location,
- ) {
- self.terminator_effect(state, terminator, location);
+ ) -> TerminatorEdges<'mir, 'tcx> {
+ self.terminator_effect(state, terminator, location)
}
fn apply_before_terminator_effect(
@@ -410,15 +380,6 @@ where
self.call_return_effect(state, block, return_places);
}
- fn apply_yield_resume_effect(
- &mut self,
- state: &mut A::Domain,
- resume_block: BasicBlock,
- resume_place: mir::Place<'tcx>,
- ) {
- self.yield_resume_effect(state, resume_block, resume_place);
- }
-
fn apply_switch_int_edge_effects(
&mut self,
block: BasicBlock,
@@ -531,6 +492,24 @@ impl GenKill for ChunkedBitSet {
}
}
+impl> GenKill for MaybeReachable {
+ fn gen(&mut self, elem: T) {
+ match self {
+ // If the state is not reachable, adding an element does nothing.
+ MaybeReachable::Unreachable => {}
+ MaybeReachable::Reachable(set) => set.gen(elem),
+ }
+ }
+
+ fn kill(&mut self, elem: T) {
+ match self {
+ // If the state is not reachable, killing an element does nothing.
+ MaybeReachable::Unreachable => {}
+ MaybeReachable::Reachable(set) => set.kill(elem),
+ }
+ }
+}
+
impl GenKill for lattice::Dual> {
fn gen(&mut self, elem: T) {
self.0.insert(elem);
@@ -612,29 +591,5 @@ pub trait SwitchIntEdgeEffects {
fn apply(&mut self, apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget));
}
-/// List of places that are written to after a successful (non-unwind) return
-/// from a `Call` or `InlineAsm`.
-pub enum CallReturnPlaces<'a, 'tcx> {
- Call(mir::Place<'tcx>),
- InlineAsm(&'a [mir::InlineAsmOperand<'tcx>]),
-}
-
-impl<'tcx> CallReturnPlaces<'_, 'tcx> {
- pub fn for_each(&self, mut f: impl FnMut(mir::Place<'tcx>)) {
- match *self {
- Self::Call(place) => f(place),
- Self::InlineAsm(operands) => {
- for op in operands {
- match *op {
- mir::InlineAsmOperand::Out { place: Some(place), .. }
- | mir::InlineAsmOperand::InOut { out_place: Some(place), .. } => f(place),
- _ => {}
- }
- }
- }
- }
- }
-}
-
#[cfg(test)]
mod tests;
diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs
index cb0ec144ef0b..9cce5b26cd30 100644
--- a/compiler/rustc_mir_dataflow/src/framework/tests.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs
@@ -198,14 +198,15 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
assert!(state.insert(idx));
}
- 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> {
let idx = self.effect(Effect::Primary.at_index(location.statement_index));
assert!(state.insert(idx));
+ terminator.edges()
}
fn apply_before_terminator_effect(
diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
index b88ed32b687f..5ed8f20b73f1 100644
--- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
@@ -1,9 +1,9 @@
-use super::*;
-
-use crate::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis};
+use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
+use crate::{AnalysisDomain, GenKill, GenKillAnalysis};
+
/// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points
/// to a given local.
///
@@ -14,7 +14,7 @@ use rustc_middle::mir::*;
pub struct MaybeBorrowedLocals;
impl MaybeBorrowedLocals {
- fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> {
+ pub(super) fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> {
TransferFunction { trans }
}
}
@@ -23,12 +23,12 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeBorrowedLocals {
type Domain = BitSet;
const NAME: &'static str = "maybe_borrowed_locals";
- fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+ fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain {
// bottom = unborrowed
BitSet::new_empty(body.local_decls().len())
}
- fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
+ fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) {
// No locals are aliased on function entry
}
}
@@ -36,35 +36,40 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeBorrowedLocals {
impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
type Idx = Local;
+ fn domain_size(&self, body: &Body<'tcx>) -> usize {
+ body.local_decls.len()
+ }
+
fn statement_effect(
&mut self,
trans: &mut impl GenKill,
- statement: &mir::Statement<'tcx>,
+ statement: &Statement<'tcx>,
location: Location,
) {
self.transfer_function(trans).visit_statement(statement, location);
}
- fn terminator_effect(
+ fn terminator_effect<'mir>(
&mut self,
- trans: &mut impl GenKill,
- terminator: &mir::Terminator<'tcx>,
+ trans: &mut Self::Domain,
+ terminator: &'mir Terminator<'tcx>,
location: Location,
- ) {
+ ) -> TerminatorEdges<'mir, 'tcx> {
self.transfer_function(trans).visit_terminator(terminator, location);
+ terminator.edges()
}
fn call_return_effect(
&mut self,
_trans: &mut impl GenKill,
- _block: mir::BasicBlock,
+ _block: BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
) {
}
}
/// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
-struct TransferFunction<'a, T> {
+pub(super) struct TransferFunction<'a, T> {
trans: &'a mut T,
}
@@ -82,37 +87,37 @@ where
}
}
- fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
+ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
self.super_rvalue(rvalue, location);
match rvalue {
- mir::Rvalue::AddressOf(_, borrowed_place) | mir::Rvalue::Ref(_, _, borrowed_place) => {
+ Rvalue::AddressOf(_, borrowed_place) | Rvalue::Ref(_, _, borrowed_place) => {
if !borrowed_place.is_indirect() {
self.trans.gen(borrowed_place.local);
}
}
- mir::Rvalue::Cast(..)
- | mir::Rvalue::ShallowInitBox(..)
- | mir::Rvalue::Use(..)
- | mir::Rvalue::ThreadLocalRef(..)
- | mir::Rvalue::Repeat(..)
- | mir::Rvalue::Len(..)
- | mir::Rvalue::BinaryOp(..)
- | mir::Rvalue::CheckedBinaryOp(..)
- | mir::Rvalue::NullaryOp(..)
- | mir::Rvalue::UnaryOp(..)
- | mir::Rvalue::Discriminant(..)
- | mir::Rvalue::Aggregate(..)
- | mir::Rvalue::CopyForDeref(..) => {}
+ Rvalue::Cast(..)
+ | Rvalue::ShallowInitBox(..)
+ | Rvalue::Use(..)
+ | Rvalue::ThreadLocalRef(..)
+ | Rvalue::Repeat(..)
+ | Rvalue::Len(..)
+ | Rvalue::BinaryOp(..)
+ | Rvalue::CheckedBinaryOp(..)
+ | Rvalue::NullaryOp(..)
+ | Rvalue::UnaryOp(..)
+ | Rvalue::Discriminant(..)
+ | Rvalue::Aggregate(..)
+ | Rvalue::CopyForDeref(..) => {}
}
}
- fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
+ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
self.super_terminator(terminator, location);
match terminator.kind {
- mir::TerminatorKind::Drop { place: dropped_place, .. } => {
+ TerminatorKind::Drop { place: dropped_place, .. } => {
// Drop terminators may call custom drop glue (`Drop::drop`), which takes `&mut
// self` as a parameter. In the general case, a drop impl could launder that
// reference into the surrounding environment through a raw pointer, thus creating
@@ -126,7 +131,7 @@ where
}
}
- TerminatorKind::Terminate
+ TerminatorKind::UnwindTerminate
| TerminatorKind::Assert { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::FalseEdge { .. }
@@ -134,7 +139,7 @@ where
| TerminatorKind::GeneratorDrop
| TerminatorKind::Goto { .. }
| TerminatorKind::InlineAsm { .. }
- | TerminatorKind::Resume
+ | TerminatorKind::UnwindResume
| TerminatorKind::Return
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::Unreachable
diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
new file mode 100644
index 000000000000..e6d383d626ad
--- /dev/null
+++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
@@ -0,0 +1,778 @@
+use rustc_index::bit_set::{BitSet, ChunkedBitSet};
+use rustc_index::Idx;
+use rustc_middle::mir::{self, Body, CallReturnPlaces, Location, TerminatorEdges};
+use rustc_middle::ty::{self, TyCtxt};
+
+use crate::drop_flag_effects_for_function_entry;
+use crate::drop_flag_effects_for_location;
+use crate::elaborate_drops::DropFlagState;
+use crate::framework::SwitchIntEdgeEffects;
+use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
+use crate::on_lookup_result_bits;
+use crate::MoveDataParamEnv;
+use crate::{drop_flag_effects, on_all_children_bits, on_all_drop_children_bits};
+use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis, MaybeReachable};
+
+/// `MaybeInitializedPlaces` tracks all places that might be
+/// initialized upon reaching a particular point in the control flow
+/// for a function.
+///
+/// For example, in code like the following, we have corresponding
+/// dataflow information shown in the right-hand comments.
+///
+/// ```rust
+/// struct S;
+/// fn foo(pred: bool) { // maybe-init:
+/// // {}
+/// let a = S; let mut b = S; let c; let d; // {a, b}
+///
+/// if pred {
+/// drop(a); // { b}
+/// b = S; // { b}
+///
+/// } else {
+/// drop(b); // {a}
+/// d = S; // {a, d}
+///
+/// } // {a, b, d}
+///
+/// c = S; // {a, b, c, d}
+/// }
+/// ```
+///
+/// To determine whether a place *must* be initialized at a
+/// particular control-flow point, one can take the set-difference
+/// between this data and the data from `MaybeUninitializedPlaces` at the
+/// corresponding control-flow point.
+///
+/// Similarly, at a given `drop` statement, the set-intersection
+/// between this data and `MaybeUninitializedPlaces` yields the set of
+/// places that would require a dynamic drop-flag at that statement.
+pub struct MaybeInitializedPlaces<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ body: &'a Body<'tcx>,
+ mdpe: &'a MoveDataParamEnv<'tcx>,
+ skip_unreachable_unwind: bool,
+}
+
+impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
+ pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
+ MaybeInitializedPlaces { tcx, body, mdpe, skip_unreachable_unwind: false }
+ }
+
+ pub fn skipping_unreachable_unwind(mut self) -> Self {
+ self.skip_unreachable_unwind = true;
+ self
+ }
+
+ pub fn is_unwind_dead(
+ &self,
+ place: mir::Place<'tcx>,
+ state: &MaybeReachable>,
+ ) -> bool {
+ if let LookupResult::Exact(path) = self.move_data().rev_lookup.find(place.as_ref()) {
+ let mut maybe_live = false;
+ on_all_drop_children_bits(self.tcx, self.body, self.mdpe, path, |child| {
+ maybe_live |= state.contains(child);
+ });
+ !maybe_live
+ } else {
+ false
+ }
+ }
+}
+
+impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
+ fn move_data(&self) -> &MoveData<'tcx> {
+ &self.mdpe.move_data
+ }
+}
+
+/// `MaybeUninitializedPlaces` tracks all places that might be
+/// uninitialized upon reaching a particular point in the control flow
+/// for a function.
+///
+/// For example, in code like the following, we have corresponding
+/// dataflow information shown in the right-hand comments.
+///
+/// ```rust
+/// struct S;
+/// fn foo(pred: bool) { // maybe-uninit:
+/// // {a, b, c, d}
+/// let a = S; let mut b = S; let c; let d; // { c, d}
+///
+/// if pred {
+/// drop(a); // {a, c, d}
+/// b = S; // {a, c, d}
+///
+/// } else {
+/// drop(b); // { b, c, d}
+/// d = S; // { b, c }
+///
+/// } // {a, b, c, d}
+///
+/// c = S; // {a, b, d}
+/// }
+/// ```
+///
+/// To determine whether a place *must* be uninitialized at a
+/// particular control-flow point, one can take the set-difference
+/// between this data and the data from `MaybeInitializedPlaces` at the
+/// corresponding control-flow point.
+///
+/// Similarly, at a given `drop` statement, the set-intersection
+/// between this data and `MaybeInitializedPlaces` yields the set of
+/// places that would require a dynamic drop-flag at that statement.
+pub struct MaybeUninitializedPlaces<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ body: &'a Body<'tcx>,
+ mdpe: &'a MoveDataParamEnv<'tcx>,
+
+ mark_inactive_variants_as_uninit: bool,
+ skip_unreachable_unwind: BitSet,
+}
+
+impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
+ pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
+ MaybeUninitializedPlaces {
+ tcx,
+ body,
+ mdpe,
+ mark_inactive_variants_as_uninit: false,
+ skip_unreachable_unwind: BitSet::new_empty(body.basic_blocks.len()),
+ }
+ }
+
+ /// Causes inactive enum variants to be marked as "maybe uninitialized" after a switch on an
+ /// enum discriminant.
+ ///
+ /// This is correct in a vacuum but is not the default because it causes problems in the borrow
+ /// checker, where this information gets propagated along `FakeEdge`s.
+ pub fn mark_inactive_variants_as_uninit(mut self) -> Self {
+ self.mark_inactive_variants_as_uninit = true;
+ self
+ }
+
+ pub fn skipping_unreachable_unwind(
+ mut self,
+ unreachable_unwind: BitSet,
+ ) -> Self {
+ self.skip_unreachable_unwind = unreachable_unwind;
+ self
+ }
+}
+
+impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> {
+ fn move_data(&self) -> &MoveData<'tcx> {
+ &self.mdpe.move_data
+ }
+}
+
+/// `DefinitelyInitializedPlaces` tracks all places that are definitely
+/// initialized upon reaching a particular point in the control flow
+/// for a function.
+///
+/// For example, in code like the following, we have corresponding
+/// dataflow information shown in the right-hand comments.
+///
+/// ```rust
+/// struct S;
+/// fn foo(pred: bool) { // definite-init:
+/// // { }
+/// let a = S; let mut b = S; let c; let d; // {a, b }
+///
+/// if pred {
+/// drop(a); // { b, }
+/// b = S; // { b, }
+///
+/// } else {
+/// drop(b); // {a, }
+/// d = S; // {a, d}
+///
+/// } // { }
+///
+/// c = S; // { c }
+/// }
+/// ```
+///
+/// To determine whether a place *may* be uninitialized at a
+/// particular control-flow point, one can take the set-complement
+/// of this data.
+///
+/// Similarly, at a given `drop` statement, the set-difference between
+/// this data and `MaybeInitializedPlaces` yields the set of places
+/// that would require a dynamic drop-flag at that statement.
+pub struct DefinitelyInitializedPlaces<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ body: &'a Body<'tcx>,
+ mdpe: &'a MoveDataParamEnv<'tcx>,
+}
+
+impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
+ pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
+ DefinitelyInitializedPlaces { tcx, body, mdpe }
+ }
+}
+
+impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
+ fn move_data(&self) -> &MoveData<'tcx> {
+ &self.mdpe.move_data
+ }
+}
+
+/// `EverInitializedPlaces` tracks all places that might have ever been
+/// initialized upon reaching a particular point in the control flow
+/// for a function, without an intervening `StorageDead`.
+///
+/// This dataflow is used to determine if an immutable local variable may
+/// be assigned to.
+///
+/// For example, in code like the following, we have corresponding
+/// dataflow information shown in the right-hand comments.
+///
+/// ```rust
+/// struct S;
+/// fn foo(pred: bool) { // ever-init:
+/// // { }
+/// let a = S; let mut b = S; let c; let d; // {a, b }
+///
+/// if pred {
+/// drop(a); // {a, b, }
+/// b = S; // {a, b, }
+///
+/// } else {
+/// drop(b); // {a, b, }
+/// d = S; // {a, b, d }
+///
+/// } // {a, b, d }
+///
+/// c = S; // {a, b, c, d }
+/// }
+/// ```
+pub struct EverInitializedPlaces<'a, 'tcx> {
+ #[allow(dead_code)]
+ tcx: TyCtxt<'tcx>,
+ body: &'a Body<'tcx>,
+ mdpe: &'a MoveDataParamEnv<'tcx>,
+}
+
+impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> {
+ pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
+ EverInitializedPlaces { tcx, body, mdpe }
+ }
+}
+
+impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'tcx> {
+ fn move_data(&self) -> &MoveData<'tcx> {
+ &self.mdpe.move_data
+ }
+}
+
+impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
+ fn update_bits(
+ trans: &mut impl GenKill,
+ path: MovePathIndex,
+ state: DropFlagState,
+ ) {
+ match state {
+ DropFlagState::Absent => trans.kill(path),
+ DropFlagState::Present => trans.gen(path),
+ }
+ }
+}
+
+impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
+ fn update_bits(
+ trans: &mut impl GenKill,
+ path: MovePathIndex,
+ state: DropFlagState,
+ ) {
+ match state {
+ DropFlagState::Absent => trans.gen(path),
+ DropFlagState::Present => trans.kill(path),
+ }
+ }
+}
+
+impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
+ fn update_bits(
+ trans: &mut impl GenKill,
+ path: MovePathIndex,
+ state: DropFlagState,
+ ) {
+ match state {
+ DropFlagState::Absent => trans.kill(path),
+ DropFlagState::Present => trans.gen(path),
+ }
+ }
+}
+
+impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
+ type Domain = MaybeReachable>;
+ const NAME: &'static str = "maybe_init";
+
+ fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+ // bottom = uninitialized
+ MaybeReachable::Unreachable
+ }
+
+ fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
+ *state =
+ MaybeReachable::Reachable(ChunkedBitSet::new_empty(self.move_data().move_paths.len()));
+ drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
+ assert!(s == DropFlagState::Present);
+ state.gen(path);
+ });
+ }
+}
+
+impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
+ type Idx = MovePathIndex;
+
+ fn domain_size(&self, _: &Body<'tcx>) -> usize {
+ self.move_data().move_paths.len()
+ }
+
+ fn statement_effect(
+ &mut self,
+ trans: &mut impl GenKill,
+ statement: &mir::Statement<'tcx>,
+ location: Location,
+ ) {
+ drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
+ Self::update_bits(trans, path, s)
+ });
+
+ // Mark all places as "maybe init" if they are mutably borrowed. See #90752.
+ if self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration
+ && let Some((_, rvalue)) = statement.kind.as_assign()
+ && let mir::Rvalue::Ref(_, mir::BorrowKind::Mut { .. }, place)
+ // FIXME: Does `&raw const foo` allow mutation? See #90413.
+ | mir::Rvalue::AddressOf(_, place) = rvalue
+ && let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref())
+ {
+ on_all_children_bits(self.tcx, self.body, self.move_data(), mpi, |child| {
+ trans.gen(child);
+ })
+ }
+ }
+
+ fn terminator_effect<'mir>(
+ &mut self,
+ state: &mut Self::Domain,
+ terminator: &'mir mir::Terminator<'tcx>,
+ location: Location,
+ ) -> TerminatorEdges<'mir, 'tcx> {
+ let mut edges = terminator.edges();
+ if self.skip_unreachable_unwind
+ && let mir::TerminatorKind::Drop { target, unwind, place, replace: _ } = terminator.kind
+ && matches!(unwind, mir::UnwindAction::Cleanup(_))
+ && self.is_unwind_dead(place, state)
+ {
+ edges = TerminatorEdges::Single(target);
+ }
+ drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
+ Self::update_bits(state, path, s)
+ });
+ edges
+ }
+
+ fn call_return_effect(
+ &mut self,
+ trans: &mut impl GenKill,
+ _block: mir::BasicBlock,
+ return_places: CallReturnPlaces<'_, 'tcx>,
+ ) {
+ return_places.for_each(|place| {
+ // when a call returns successfully, that means we need to set
+ // the bits for that dest_place to 1 (initialized).
+ on_lookup_result_bits(
+ self.tcx,
+ self.body,
+ self.move_data(),
+ self.move_data().rev_lookup.find(place.as_ref()),
+ |mpi| {
+ trans.gen(mpi);
+ },
+ );
+ });
+ }
+
+ fn switch_int_edge_effects>(
+ &mut self,
+ block: mir::BasicBlock,
+ discr: &mir::Operand<'tcx>,
+ edge_effects: &mut impl SwitchIntEdgeEffects,
+ ) {
+ if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
+ return;
+ }
+
+ let enum_ = discr.place().and_then(|discr| {
+ switch_on_enum_discriminant(self.tcx, &self.body, &self.body[block], discr)
+ });
+
+ let Some((enum_place, enum_def)) = enum_ else {
+ return;
+ };
+
+ let mut discriminants = enum_def.discriminants(self.tcx);
+ edge_effects.apply(|trans, edge| {
+ let Some(value) = edge.value else {
+ return;
+ };
+
+ // MIR building adds discriminants to the `values` array in the same order as they
+ // are yielded by `AdtDef::discriminants`. We rely on this to match each
+ // discriminant in `values` to its corresponding variant in linear time.
+ let (variant, _) = discriminants
+ .find(|&(_, discr)| discr.val == value)
+ .expect("Order of `AdtDef::discriminants` differed from `SwitchInt::values`");
+
+ // Kill all move paths that correspond to variants we know to be inactive along this
+ // particular outgoing edge of a `SwitchInt`.
+ drop_flag_effects::on_all_inactive_variants(
+ self.tcx,
+ self.body,
+ self.move_data(),
+ enum_place,
+ variant,
+ |mpi| trans.kill(mpi),
+ );
+ });
+ }
+}
+
+impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
+ type Domain = ChunkedBitSet;
+
+ const NAME: &'static str = "maybe_uninit";
+
+ fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+ // bottom = initialized (start_block_effect counters this at outset)
+ ChunkedBitSet::new_empty(self.move_data().move_paths.len())
+ }
+
+ // sets on_entry bits for Arg places
+ fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
+ // set all bits to 1 (uninit) before gathering counter-evidence
+ state.insert_all();
+
+ drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
+ assert!(s == DropFlagState::Present);
+ state.remove(path);
+ });
+ }
+}
+
+impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
+ type Idx = MovePathIndex;
+
+ fn domain_size(&self, _: &Body<'tcx>) -> usize {
+ self.move_data().move_paths.len()
+ }
+
+ fn statement_effect(
+ &mut self,
+ trans: &mut impl GenKill,
+ _statement: &mir::Statement<'tcx>,
+ location: Location,
+ ) {
+ drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
+ Self::update_bits(trans, path, s)
+ });
+
+ // Unlike in `MaybeInitializedPlaces` above, we don't need to change the state when a
+ // mutable borrow occurs. Places cannot become uninitialized through a mutable reference.
+ }
+
+ fn terminator_effect<'mir>(
+ &mut self,
+ trans: &mut Self::Domain,
+ terminator: &'mir mir::Terminator<'tcx>,
+ location: Location,
+ ) -> TerminatorEdges<'mir, 'tcx> {
+ drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
+ Self::update_bits(trans, path, s)
+ });
+ if self.skip_unreachable_unwind.contains(location.block) {
+ let mir::TerminatorKind::Drop { target, unwind, .. } = terminator.kind else { bug!() };
+ assert!(matches!(unwind, mir::UnwindAction::Cleanup(_)));
+ TerminatorEdges::Single(target)
+ } else {
+ terminator.edges()
+ }
+ }
+
+ fn call_return_effect(
+ &mut self,
+ trans: &mut impl GenKill,
+ _block: mir::BasicBlock,
+ return_places: CallReturnPlaces<'_, 'tcx>,
+ ) {
+ return_places.for_each(|place| {
+ // when a call returns successfully, that means we need to set
+ // the bits for that dest_place to 0 (initialized).
+ on_lookup_result_bits(
+ self.tcx,
+ self.body,
+ self.move_data(),
+ self.move_data().rev_lookup.find(place.as_ref()),
+ |mpi| {
+ trans.kill(mpi);
+ },
+ );
+ });
+ }
+
+ fn switch_int_edge_effects>(
+ &mut self,
+ block: mir::BasicBlock,
+ discr: &mir::Operand<'tcx>,
+ edge_effects: &mut impl SwitchIntEdgeEffects,
+ ) {
+ if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
+ return;
+ }
+
+ if !self.mark_inactive_variants_as_uninit {
+ return;
+ }
+
+ let enum_ = discr.place().and_then(|discr| {
+ switch_on_enum_discriminant(self.tcx, &self.body, &self.body[block], discr)
+ });
+
+ let Some((enum_place, enum_def)) = enum_ else {
+ return;
+ };
+
+ let mut discriminants = enum_def.discriminants(self.tcx);
+ edge_effects.apply(|trans, edge| {
+ let Some(value) = edge.value else {
+ return;
+ };
+
+ // MIR building adds discriminants to the `values` array in the same order as they
+ // are yielded by `AdtDef::discriminants`. We rely on this to match each
+ // discriminant in `values` to its corresponding variant in linear time.
+ let (variant, _) = discriminants
+ .find(|&(_, discr)| discr.val == value)
+ .expect("Order of `AdtDef::discriminants` differed from `SwitchInt::values`");
+
+ // Mark all move paths that correspond to variants other than this one as maybe
+ // uninitialized (in reality, they are *definitely* uninitialized).
+ drop_flag_effects::on_all_inactive_variants(
+ self.tcx,
+ self.body,
+ self.move_data(),
+ enum_place,
+ variant,
+ |mpi| trans.gen(mpi),
+ );
+ });
+ }
+}
+
+impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
+ /// Use set intersection as the join operator.
+ type Domain = lattice::Dual>;
+
+ const NAME: &'static str = "definite_init";
+
+ fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+ // bottom = initialized (start_block_effect counters this at outset)
+ lattice::Dual(BitSet::new_filled(self.move_data().move_paths.len()))
+ }
+
+ // sets on_entry bits for Arg places
+ fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
+ state.0.clear();
+
+ drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
+ assert!(s == DropFlagState::Present);
+ state.0.insert(path);
+ });
+ }
+}
+
+impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
+ type Idx = MovePathIndex;
+
+ fn domain_size(&self, _: &Body<'tcx>) -> usize {
+ self.move_data().move_paths.len()
+ }
+
+ fn statement_effect(
+ &mut self,
+ trans: &mut impl GenKill,
+ _statement: &mir::Statement<'tcx>,
+ location: Location,
+ ) {
+ drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
+ Self::update_bits(trans, path, s)
+ })
+ }
+
+ fn terminator_effect<'mir>(
+ &mut self,
+ trans: &mut Self::Domain,
+ terminator: &'mir mir::Terminator<'tcx>,
+ location: Location,
+ ) -> TerminatorEdges<'mir, 'tcx> {
+ drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
+ Self::update_bits(trans, path, s)
+ });
+ terminator.edges()
+ }
+
+ fn call_return_effect(
+ &mut self,
+ trans: &mut impl GenKill,
+ _block: mir::BasicBlock,
+ return_places: CallReturnPlaces<'_, 'tcx>,
+ ) {
+ return_places.for_each(|place| {
+ // when a call returns successfully, that means we need to set
+ // the bits for that dest_place to 1 (initialized).
+ on_lookup_result_bits(
+ self.tcx,
+ self.body,
+ self.move_data(),
+ self.move_data().rev_lookup.find(place.as_ref()),
+ |mpi| {
+ trans.gen(mpi);
+ },
+ );
+ });
+ }
+}
+
+impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
+ type Domain = ChunkedBitSet;
+
+ const NAME: &'static str = "ever_init";
+
+ fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+ // bottom = no initialized variables by default
+ ChunkedBitSet::new_empty(self.move_data().inits.len())
+ }
+
+ fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
+ for arg_init in 0..body.arg_count {
+ state.insert(InitIndex::new(arg_init));
+ }
+ }
+}
+
+impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
+ type Idx = InitIndex;
+
+ fn domain_size(&self, _: &Body<'tcx>) -> usize {
+ self.move_data().inits.len()
+ }
+
+ #[instrument(skip(self, trans), level = "debug")]
+ fn statement_effect(
+ &mut self,
+ trans: &mut impl GenKill,
+ stmt: &mir::Statement<'tcx>,
+ location: Location,
+ ) {
+ let move_data = self.move_data();
+ let init_path_map = &move_data.init_path_map;
+ let init_loc_map = &move_data.init_loc_map;
+ let rev_lookup = &move_data.rev_lookup;
+
+ debug!("initializes move_indexes {:?}", &init_loc_map[location]);
+ trans.gen_all(init_loc_map[location].iter().copied());
+
+ if let mir::StatementKind::StorageDead(local) = stmt.kind {
+ // End inits for StorageDead, so that an immutable variable can
+ // be reinitialized on the next iteration of the loop.
+ let move_path_index = rev_lookup.find_local(local);
+ debug!("clears the ever initialized status of {:?}", init_path_map[move_path_index]);
+ trans.kill_all(init_path_map[move_path_index].iter().copied());
+ }
+ }
+
+ #[instrument(skip(self, trans, terminator), level = "debug")]
+ fn terminator_effect<'mir>(
+ &mut self,
+ trans: &mut Self::Domain,
+ terminator: &'mir mir::Terminator<'tcx>,
+ location: Location,
+ ) -> TerminatorEdges<'mir, 'tcx> {
+ let (body, move_data) = (self.body, self.move_data());
+ let term = body[location.block].terminator();
+ let init_loc_map = &move_data.init_loc_map;
+ debug!(?term);
+ debug!("initializes move_indexes {:?}", init_loc_map[location]);
+ trans.gen_all(
+ init_loc_map[location]
+ .iter()
+ .filter(|init_index| {
+ move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
+ })
+ .copied(),
+ );
+ terminator.edges()
+ }
+
+ fn call_return_effect(
+ &mut self,
+ trans: &mut impl GenKill,
+ block: mir::BasicBlock,
+ _return_places: CallReturnPlaces<'_, 'tcx>,
+ ) {
+ let move_data = self.move_data();
+ let init_loc_map = &move_data.init_loc_map;
+
+ let call_loc = self.body.terminator_loc(block);
+ for init_index in &init_loc_map[call_loc] {
+ trans.gen(*init_index);
+ }
+ }
+}
+
+/// Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt` is
+/// an enum discriminant.
+///
+/// We expect such blocks to have a call to `discriminant` as their last statement like so:
+///
+/// ```text
+/// ...
+/// _42 = discriminant(_1)
+/// SwitchInt(_42, ..)
+/// ```
+///
+/// If the basic block matches this pattern, this function returns the place corresponding to the
+/// enum (`_1` in the example above) as well as the `AdtDef` of that enum.
+fn switch_on_enum_discriminant<'mir, 'tcx>(
+ tcx: TyCtxt<'tcx>,
+ body: &'mir mir::Body<'tcx>,
+ block: &'mir mir::BasicBlockData<'tcx>,
+ switch_on: mir::Place<'tcx>,
+) -> Option<(mir::Place<'tcx>, ty::AdtDef<'tcx>)> {
+ for statement in block.statements.iter().rev() {
+ match &statement.kind {
+ mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated)))
+ if *lhs == switch_on =>
+ {
+ match discriminated.ty(body, tcx).ty.kind() {
+ ty::Adt(def, _) => return Some((*discriminated, *def)),
+
+ // `Rvalue::Discriminant` is also used to get the active yield point for a
+ // generator, but we do not need edge-specific effects in that case. This may
+ // change in the future.
+ ty::Generator(..) => return None,
+
+ t => bug!("`discriminant` called on unexpected type {:?}", t),
+ }
+ }
+ mir::StatementKind::Coverage(_) => continue,
+ _ => return None,
+ }
+ }
+ None
+}
diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
index 34e0834a68b3..5aa73c7a9069 100644
--- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
@@ -1,8 +1,10 @@
use rustc_index::bit_set::{BitSet, ChunkedBitSet};
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
-use rustc_middle::mir::{self, Local, Location, Place, StatementKind};
+use rustc_middle::mir::{
+ self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges,
+};
-use crate::{Analysis, AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKillAnalysis};
+use crate::{Analysis, AnalysisDomain, Backward, GenKill, GenKillAnalysis};
/// A [live-variable dataflow analysis][liveness].
///
@@ -43,6 +45,10 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals {
impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
type Idx = Local;
+ fn domain_size(&self, body: &mir::Body<'tcx>) -> usize {
+ body.local_decls.len()
+ }
+
fn statement_effect(
&mut self,
trans: &mut impl GenKill,
@@ -52,13 +58,14 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
TransferFunction(trans).visit_statement(statement, location);
}
- fn terminator_effect(
+ fn terminator_effect<'mir>(
&mut self,
- trans: &mut impl GenKill,
- terminator: &mir::Terminator<'tcx>,
+ trans: &mut Self::Domain,
+ terminator: &'mir mir::Terminator<'tcx>,
location: Location,
- ) {
+ ) -> TerminatorEdges<'mir, 'tcx> {
TransferFunction(trans).visit_terminator(terminator, location);
+ terminator.edges()
}
fn call_return_effect(
@@ -67,24 +74,19 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
- return_places.for_each(|place| {
- if let Some(local) = place.as_local() {
- trans.kill(local);
- }
- });
- }
-
- fn yield_resume_effect(
- &mut self,
- trans: &mut impl GenKill,
- _resume_block: mir::BasicBlock,
- resume_place: mir::Place<'tcx>,
- ) {
- YieldResumeEffect(trans).visit_place(
- &resume_place,
- PlaceContext::MutatingUse(MutatingUseContext::Yield),
- Location::START,
- )
+ if let CallReturnPlaces::Yield(resume_place) = return_places {
+ YieldResumeEffect(trans).visit_place(
+ &resume_place,
+ PlaceContext::MutatingUse(MutatingUseContext::Yield),
+ Location::START,
+ )
+ } else {
+ return_places.for_each(|place| {
+ if let Some(local) = place.as_local() {
+ trans.kill(local);
+ }
+ });
+ }
}
}
@@ -97,7 +99,7 @@ where
fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
if let PlaceContext::MutatingUse(MutatingUseContext::Yield) = context {
// The resume place is evaluated and assigned to only after generator resumes, so its
- // effect is handled separately in `yield_resume_effect`.
+ // effect is handled separately in `call_resume_effect`.
return;
}
@@ -283,13 +285,14 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
TransferFunction(trans).visit_statement(statement, location);
}
- fn apply_terminator_effect(
+ fn apply_terminator_effect<'mir>(
&mut self,
trans: &mut Self::Domain,
- terminator: &mir::Terminator<'tcx>,
+ terminator: &'mir mir::Terminator<'tcx>,
location: Location,
- ) {
+ ) -> TerminatorEdges<'mir, 'tcx> {
TransferFunction(trans).visit_terminator(terminator, location);
+ terminator.edges()
}
fn apply_call_return_effect(
@@ -298,23 +301,18 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
- return_places.for_each(|place| {
- if let Some(local) = place.as_local() {
- trans.remove(local);
- }
- });
- }
-
- fn apply_yield_resume_effect(
- &mut self,
- trans: &mut Self::Domain,
- _resume_block: mir::BasicBlock,
- resume_place: mir::Place<'tcx>,
- ) {
- YieldResumeEffect(trans).visit_place(
- &resume_place,
- PlaceContext::MutatingUse(MutatingUseContext::Yield),
- Location::START,
- )
+ if let CallReturnPlaces::Yield(resume_place) = return_places {
+ YieldResumeEffect(trans).visit_place(
+ &resume_place,
+ PlaceContext::MutatingUse(MutatingUseContext::Yield),
+ Location::START,
+ )
+ } else {
+ return_places.for_each(|place| {
+ if let Some(local) = place.as_local() {
+ trans.remove(local);
+ }
+ });
+ }
}
}
diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs
index 7ddd01e34aaa..f8db18fc1f8d 100644
--- a/compiler/rustc_mir_dataflow/src/impls/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs
@@ -2,768 +2,18 @@
//! bitvectors attached to each basic block, represented via a
//! zero-sized structure.
-use rustc_index::bit_set::{BitSet, ChunkedBitSet};
-use rustc_index::Idx;
-use rustc_middle::mir::visit::{MirVisitable, Visitor};
-use rustc_middle::mir::{self, Body, Location};
-use rustc_middle::ty::{self, TyCtxt};
-
-use crate::drop_flag_effects_for_function_entry;
-use crate::drop_flag_effects_for_location;
-use crate::elaborate_drops::DropFlagState;
-use crate::framework::{CallReturnPlaces, SwitchIntEdgeEffects};
-use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
-use crate::on_lookup_result_bits;
-use crate::MoveDataParamEnv;
-use crate::{drop_flag_effects, on_all_children_bits};
-use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis};
-
mod borrowed_locals;
+mod initialized;
mod liveness;
mod storage_liveness;
pub use self::borrowed_locals::borrowed_locals;
pub use self::borrowed_locals::MaybeBorrowedLocals;
+pub use self::initialized::{
+ DefinitelyInitializedPlaces, EverInitializedPlaces, MaybeInitializedPlaces,
+ MaybeUninitializedPlaces,
+};
pub use self::liveness::MaybeLiveLocals;
pub use self::liveness::MaybeTransitiveLiveLocals;
pub use self::liveness::TransferFunction as LivenessTransferFunction;
pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive};
-
-/// `MaybeInitializedPlaces` tracks all places that might be
-/// initialized upon reaching a particular point in the control flow
-/// for a function.
-///
-/// For example, in code like the following, we have corresponding
-/// dataflow information shown in the right-hand comments.
-///
-/// ```rust
-/// struct S;
-/// fn foo(pred: bool) { // maybe-init:
-/// // {}
-/// let a = S; let mut b = S; let c; let d; // {a, b}
-///
-/// if pred {
-/// drop(a); // { b}
-/// b = S; // { b}
-///
-/// } else {
-/// drop(b); // {a}
-/// d = S; // {a, d}
-///
-/// } // {a, b, d}
-///
-/// c = S; // {a, b, c, d}
-/// }
-/// ```
-///
-/// To determine whether a place *must* be initialized at a
-/// particular control-flow point, one can take the set-difference
-/// between this data and the data from `MaybeUninitializedPlaces` at the
-/// corresponding control-flow point.
-///
-/// Similarly, at a given `drop` statement, the set-intersection
-/// between this data and `MaybeUninitializedPlaces` yields the set of
-/// places that would require a dynamic drop-flag at that statement.
-pub struct MaybeInitializedPlaces<'a, 'tcx> {
- tcx: TyCtxt<'tcx>,
- body: &'a Body<'tcx>,
- mdpe: &'a MoveDataParamEnv<'tcx>,
-}
-
-impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
- pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
- MaybeInitializedPlaces { tcx, body, mdpe }
- }
-}
-
-impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
- fn move_data(&self) -> &MoveData<'tcx> {
- &self.mdpe.move_data
- }
-}
-
-/// `MaybeUninitializedPlaces` tracks all places that might be
-/// uninitialized upon reaching a particular point in the control flow
-/// for a function.
-///
-/// For example, in code like the following, we have corresponding
-/// dataflow information shown in the right-hand comments.
-///
-/// ```rust
-/// struct S;
-/// fn foo(pred: bool) { // maybe-uninit:
-/// // {a, b, c, d}
-/// let a = S; let mut b = S; let c; let d; // { c, d}
-///
-/// if pred {
-/// drop(a); // {a, c, d}
-/// b = S; // {a, c, d}
-///
-/// } else {
-/// drop(b); // { b, c, d}
-/// d = S; // { b, c }
-///
-/// } // {a, b, c, d}
-///
-/// c = S; // {a, b, d}
-/// }
-/// ```
-///
-/// To determine whether a place *must* be uninitialized at a
-/// particular control-flow point, one can take the set-difference
-/// between this data and the data from `MaybeInitializedPlaces` at the
-/// corresponding control-flow point.
-///
-/// Similarly, at a given `drop` statement, the set-intersection
-/// between this data and `MaybeInitializedPlaces` yields the set of
-/// places that would require a dynamic drop-flag at that statement.
-pub struct MaybeUninitializedPlaces<'a, 'tcx> {
- tcx: TyCtxt<'tcx>,
- body: &'a Body<'tcx>,
- mdpe: &'a MoveDataParamEnv<'tcx>,
-
- mark_inactive_variants_as_uninit: bool,
-}
-
-impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
- pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
- MaybeUninitializedPlaces { tcx, body, mdpe, mark_inactive_variants_as_uninit: false }
- }
-
- /// Causes inactive enum variants to be marked as "maybe uninitialized" after a switch on an
- /// enum discriminant.
- ///
- /// This is correct in a vacuum but is not the default because it causes problems in the borrow
- /// checker, where this information gets propagated along `FakeEdge`s.
- pub fn mark_inactive_variants_as_uninit(mut self) -> Self {
- self.mark_inactive_variants_as_uninit = true;
- self
- }
-}
-
-impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> {
- fn move_data(&self) -> &MoveData<'tcx> {
- &self.mdpe.move_data
- }
-}
-
-/// `DefinitelyInitializedPlaces` tracks all places that are definitely
-/// initialized upon reaching a particular point in the control flow
-/// for a function.
-///
-/// For example, in code like the following, we have corresponding
-/// dataflow information shown in the right-hand comments.
-///
-/// ```rust
-/// struct S;
-/// fn foo(pred: bool) { // definite-init:
-/// // { }
-/// let a = S; let mut b = S; let c; let d; // {a, b }
-///
-/// if pred {
-/// drop(a); // { b, }
-/// b = S; // { b, }
-///
-/// } else {
-/// drop(b); // {a, }
-/// d = S; // {a, d}
-///
-/// } // { }
-///
-/// c = S; // { c }
-/// }
-/// ```
-///
-/// To determine whether a place *may* be uninitialized at a
-/// particular control-flow point, one can take the set-complement
-/// of this data.
-///
-/// Similarly, at a given `drop` statement, the set-difference between
-/// this data and `MaybeInitializedPlaces` yields the set of places
-/// that would require a dynamic drop-flag at that statement.
-pub struct DefinitelyInitializedPlaces<'a, 'tcx> {
- tcx: TyCtxt<'tcx>,
- body: &'a Body<'tcx>,
- mdpe: &'a MoveDataParamEnv<'tcx>,
-}
-
-impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
- pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
- DefinitelyInitializedPlaces { tcx, body, mdpe }
- }
-}
-
-impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
- fn move_data(&self) -> &MoveData<'tcx> {
- &self.mdpe.move_data
- }
-}
-
-/// `EverInitializedPlaces` tracks all places that might have ever been
-/// initialized upon reaching a particular point in the control flow
-/// for a function, without an intervening `StorageDead`.
-///
-/// This dataflow is used to determine if an immutable local variable may
-/// be assigned to.
-///
-/// For example, in code like the following, we have corresponding
-/// dataflow information shown in the right-hand comments.
-///
-/// ```rust
-/// struct S;
-/// fn foo(pred: bool) { // ever-init:
-/// // { }
-/// let a = S; let mut b = S; let c; let d; // {a, b }
-///
-/// if pred {
-/// drop(a); // {a, b, }
-/// b = S; // {a, b, }
-///
-/// } else {
-/// drop(b); // {a, b, }
-/// d = S; // {a, b, d }
-///
-/// } // {a, b, d }
-///
-/// c = S; // {a, b, c, d }
-/// }
-/// ```
-pub struct EverInitializedPlaces<'a, 'tcx> {
- #[allow(dead_code)]
- tcx: TyCtxt<'tcx>,
- body: &'a Body<'tcx>,
- mdpe: &'a MoveDataParamEnv<'tcx>,
-}
-
-impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> {
- pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
- EverInitializedPlaces { tcx, body, mdpe }
- }
-}
-
-impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'tcx> {
- fn move_data(&self) -> &MoveData<'tcx> {
- &self.mdpe.move_data
- }
-}
-
-impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
- fn update_bits(
- trans: &mut impl GenKill,
- path: MovePathIndex,
- state: DropFlagState,
- ) {
- match state {
- DropFlagState::Absent => trans.kill(path),
- DropFlagState::Present => trans.gen(path),
- }
- }
-}
-
-impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
- fn update_bits(
- trans: &mut impl GenKill,
- path: MovePathIndex,
- state: DropFlagState,
- ) {
- match state {
- DropFlagState::Absent => trans.gen(path),
- DropFlagState::Present => trans.kill(path),
- }
- }
-}
-
-impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
- fn update_bits(
- trans: &mut impl GenKill,
- path: MovePathIndex,
- state: DropFlagState,
- ) {
- match state {
- DropFlagState::Absent => trans.kill(path),
- DropFlagState::Present => trans.gen(path),
- }
- }
-}
-
-impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
- type Domain = ChunkedBitSet;
- const NAME: &'static str = "maybe_init";
-
- fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
- // bottom = uninitialized
- ChunkedBitSet::new_empty(self.move_data().move_paths.len())
- }
-
- fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
- drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
- assert!(s == DropFlagState::Present);
- state.insert(path);
- });
- }
-}
-
-impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
- type Idx = MovePathIndex;
-
- fn statement_effect(
- &mut self,
- trans: &mut impl GenKill,
- statement: &mir::Statement<'tcx>,
- location: Location,
- ) {
- drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
- Self::update_bits(trans, path, s)
- });
-
- if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
- return;
- }
-
- // Mark all places as "maybe init" if they are mutably borrowed. See #90752.
- for_each_mut_borrow(statement, location, |place| {
- let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref()) else {
- return;
- };
- on_all_children_bits(self.tcx, self.body, self.move_data(), mpi, |child| {
- trans.gen(child);
- })
- })
- }
-
- fn terminator_effect(
- &mut self,
- trans: &mut impl GenKill,
- terminator: &mir::Terminator<'tcx>,
- location: Location,
- ) {
- drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
- Self::update_bits(trans, path, s)
- });
-
- if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
- return;
- }
-
- for_each_mut_borrow(terminator, location, |place| {
- let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref()) else {
- return;
- };
- on_all_children_bits(self.tcx, self.body, self.move_data(), mpi, |child| {
- trans.gen(child);
- })
- })
- }
-
- fn call_return_effect(
- &mut self,
- trans: &mut impl GenKill,
- _block: mir::BasicBlock,
- return_places: CallReturnPlaces<'_, 'tcx>,
- ) {
- return_places.for_each(|place| {
- // when a call returns successfully, that means we need to set
- // the bits for that dest_place to 1 (initialized).
- on_lookup_result_bits(
- self.tcx,
- self.body,
- self.move_data(),
- self.move_data().rev_lookup.find(place.as_ref()),
- |mpi| {
- trans.gen(mpi);
- },
- );
- });
- }
-
- fn switch_int_edge_effects>(
- &mut self,
- block: mir::BasicBlock,
- discr: &mir::Operand<'tcx>,
- edge_effects: &mut impl SwitchIntEdgeEffects,
- ) {
- if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
- return;
- }
-
- let enum_ = discr.place().and_then(|discr| {
- switch_on_enum_discriminant(self.tcx, &self.body, &self.body[block], discr)
- });
-
- let Some((enum_place, enum_def)) = enum_ else {
- return;
- };
-
- let mut discriminants = enum_def.discriminants(self.tcx);
- edge_effects.apply(|trans, edge| {
- let Some(value) = edge.value else {
- return;
- };
-
- // MIR building adds discriminants to the `values` array in the same order as they
- // are yielded by `AdtDef::discriminants`. We rely on this to match each
- // discriminant in `values` to its corresponding variant in linear time.
- let (variant, _) = discriminants
- .find(|&(_, discr)| discr.val == value)
- .expect("Order of `AdtDef::discriminants` differed from `SwitchInt::values`");
-
- // Kill all move paths that correspond to variants we know to be inactive along this
- // particular outgoing edge of a `SwitchInt`.
- drop_flag_effects::on_all_inactive_variants(
- self.tcx,
- self.body,
- self.move_data(),
- enum_place,
- variant,
- |mpi| trans.kill(mpi),
- );
- });
- }
-}
-
-impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
- type Domain = ChunkedBitSet;
-
- const NAME: &'static str = "maybe_uninit";
-
- fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
- // bottom = initialized (start_block_effect counters this at outset)
- ChunkedBitSet::new_empty(self.move_data().move_paths.len())
- }
-
- // sets on_entry bits for Arg places
- fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
- // set all bits to 1 (uninit) before gathering counter-evidence
- state.insert_all();
-
- drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
- assert!(s == DropFlagState::Present);
- state.remove(path);
- });
- }
-}
-
-impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
- type Idx = MovePathIndex;
-
- fn statement_effect(
- &mut self,
- trans: &mut impl GenKill,
- _statement: &mir::Statement<'tcx>,
- location: Location,
- ) {
- drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
- Self::update_bits(trans, path, s)
- });
-
- // Unlike in `MaybeInitializedPlaces` above, we don't need to change the state when a
- // mutable borrow occurs. Places cannot become uninitialized through a mutable reference.
- }
-
- fn terminator_effect(
- &mut self,
- trans: &mut impl GenKill,
- _terminator: &mir::Terminator<'tcx>,
- location: Location,
- ) {
- drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
- Self::update_bits(trans, path, s)
- });
- }
-
- fn call_return_effect(
- &mut self,
- trans: &mut impl GenKill,
- _block: mir::BasicBlock,
- return_places: CallReturnPlaces<'_, 'tcx>,
- ) {
- return_places.for_each(|place| {
- // when a call returns successfully, that means we need to set
- // the bits for that dest_place to 0 (initialized).
- on_lookup_result_bits(
- self.tcx,
- self.body,
- self.move_data(),
- self.move_data().rev_lookup.find(place.as_ref()),
- |mpi| {
- trans.kill(mpi);
- },
- );
- });
- }
-
- fn switch_int_edge_effects>(
- &mut self,
- block: mir::BasicBlock,
- discr: &mir::Operand<'tcx>,
- edge_effects: &mut impl SwitchIntEdgeEffects,
- ) {
- if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
- return;
- }
-
- if !self.mark_inactive_variants_as_uninit {
- return;
- }
-
- let enum_ = discr.place().and_then(|discr| {
- switch_on_enum_discriminant(self.tcx, &self.body, &self.body[block], discr)
- });
-
- let Some((enum_place, enum_def)) = enum_ else {
- return;
- };
-
- let mut discriminants = enum_def.discriminants(self.tcx);
- edge_effects.apply(|trans, edge| {
- let Some(value) = edge.value else {
- return;
- };
-
- // MIR building adds discriminants to the `values` array in the same order as they
- // are yielded by `AdtDef::discriminants`. We rely on this to match each
- // discriminant in `values` to its corresponding variant in linear time.
- let (variant, _) = discriminants
- .find(|&(_, discr)| discr.val == value)
- .expect("Order of `AdtDef::discriminants` differed from `SwitchInt::values`");
-
- // Mark all move paths that correspond to variants other than this one as maybe
- // uninitialized (in reality, they are *definitely* uninitialized).
- drop_flag_effects::on_all_inactive_variants(
- self.tcx,
- self.body,
- self.move_data(),
- enum_place,
- variant,
- |mpi| trans.gen(mpi),
- );
- });
- }
-}
-
-impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
- /// Use set intersection as the join operator.
- type Domain = lattice::Dual>;
-
- const NAME: &'static str = "definite_init";
-
- fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
- // bottom = initialized (start_block_effect counters this at outset)
- lattice::Dual(BitSet::new_filled(self.move_data().move_paths.len()))
- }
-
- // sets on_entry bits for Arg places
- fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
- state.0.clear();
-
- drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
- assert!(s == DropFlagState::Present);
- state.0.insert(path);
- });
- }
-}
-
-impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
- type Idx = MovePathIndex;
-
- fn statement_effect(
- &mut self,
- trans: &mut impl GenKill,
- _statement: &mir::Statement<'tcx>,
- location: Location,
- ) {
- drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
- Self::update_bits(trans, path, s)
- })
- }
-
- fn terminator_effect(
- &mut self,
- trans: &mut impl GenKill,
- _terminator: &mir::Terminator<'tcx>,
- location: Location,
- ) {
- drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
- Self::update_bits(trans, path, s)
- })
- }
-
- fn call_return_effect(
- &mut self,
- trans: &mut impl GenKill,
- _block: mir::BasicBlock,
- return_places: CallReturnPlaces<'_, 'tcx>,
- ) {
- return_places.for_each(|place| {
- // when a call returns successfully, that means we need to set
- // the bits for that dest_place to 1 (initialized).
- on_lookup_result_bits(
- self.tcx,
- self.body,
- self.move_data(),
- self.move_data().rev_lookup.find(place.as_ref()),
- |mpi| {
- trans.gen(mpi);
- },
- );
- });
- }
-}
-
-impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
- type Domain = ChunkedBitSet;
-
- const NAME: &'static str = "ever_init";
-
- fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
- // bottom = no initialized variables by default
- ChunkedBitSet::new_empty(self.move_data().inits.len())
- }
-
- fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
- for arg_init in 0..body.arg_count {
- state.insert(InitIndex::new(arg_init));
- }
- }
-}
-
-impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
- type Idx = InitIndex;
-
- #[instrument(skip(self, trans), level = "debug")]
- fn statement_effect(
- &mut self,
- trans: &mut impl GenKill,
- stmt: &mir::Statement<'tcx>,
- location: Location,
- ) {
- let move_data = self.move_data();
- let init_path_map = &move_data.init_path_map;
- let init_loc_map = &move_data.init_loc_map;
- let rev_lookup = &move_data.rev_lookup;
-
- debug!("initializes move_indexes {:?}", &init_loc_map[location]);
- trans.gen_all(init_loc_map[location].iter().copied());
-
- if let mir::StatementKind::StorageDead(local) = stmt.kind {
- // End inits for StorageDead, so that an immutable variable can
- // be reinitialized on the next iteration of the loop.
- let move_path_index = rev_lookup.find_local(local);
- debug!("clears the ever initialized status of {:?}", init_path_map[move_path_index]);
- trans.kill_all(init_path_map[move_path_index].iter().copied());
- }
- }
-
- #[instrument(skip(self, trans, _terminator), level = "debug")]
- fn terminator_effect(
- &mut self,
- trans: &mut impl GenKill,
- _terminator: &mir::Terminator<'tcx>,
- location: Location,
- ) {
- let (body, move_data) = (self.body, self.move_data());
- let term = body[location.block].terminator();
- let init_loc_map = &move_data.init_loc_map;
- debug!(?term);
- debug!("initializes move_indexes {:?}", init_loc_map[location]);
- trans.gen_all(
- init_loc_map[location]
- .iter()
- .filter(|init_index| {
- move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
- })
- .copied(),
- );
- }
-
- fn call_return_effect(
- &mut self,
- trans: &mut impl GenKill,
- block: mir::BasicBlock,
- _return_places: CallReturnPlaces<'_, 'tcx>,
- ) {
- let move_data = self.move_data();
- let init_loc_map = &move_data.init_loc_map;
-
- let call_loc = self.body.terminator_loc(block);
- for init_index in &init_loc_map[call_loc] {
- trans.gen(*init_index);
- }
- }
-}
-
-/// Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt` is
-/// an enum discriminant.
-///
-/// We expect such blocks to have a call to `discriminant` as their last statement like so:
-///
-/// ```text
-/// ...
-/// _42 = discriminant(_1)
-/// SwitchInt(_42, ..)
-/// ```
-///
-/// If the basic block matches this pattern, this function returns the place corresponding to the
-/// enum (`_1` in the example above) as well as the `AdtDef` of that enum.
-fn switch_on_enum_discriminant<'mir, 'tcx>(
- tcx: TyCtxt<'tcx>,
- body: &'mir mir::Body<'tcx>,
- block: &'mir mir::BasicBlockData<'tcx>,
- switch_on: mir::Place<'tcx>,
-) -> Option<(mir::Place<'tcx>, ty::AdtDef<'tcx>)> {
- for statement in block.statements.iter().rev() {
- match &statement.kind {
- mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated)))
- if *lhs == switch_on =>
- {
- match discriminated.ty(body, tcx).ty.kind() {
- ty::Adt(def, _) => return Some((*discriminated, *def)),
-
- // `Rvalue::Discriminant` is also used to get the active yield point for a
- // generator, but we do not need edge-specific effects in that case. This may
- // change in the future.
- ty::Generator(..) => return None,
-
- t => bug!("`discriminant` called on unexpected type {:?}", t),
- }
- }
- mir::StatementKind::Coverage(_) => continue,
- _ => return None,
- }
- }
- None
-}
-
-struct OnMutBorrow(F);
-
-impl<'tcx, F> Visitor<'tcx> for OnMutBorrow
-where
- F: FnMut(&mir::Place<'tcx>),
-{
- fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
- // FIXME: Does `&raw const foo` allow mutation? See #90413.
- match rvalue {
- mir::Rvalue::Ref(_, mir::BorrowKind::Mut { .. }, place)
- | mir::Rvalue::AddressOf(_, place) => (self.0)(place),
-
- _ => {}
- }
-
- self.super_rvalue(rvalue, location)
- }
-}
-
-/// Calls `f` for each mutable borrow or raw reference in the program.
-///
-/// This DOES NOT call `f` for a shared borrow of a type with interior mutability. That's okay for
-/// initializedness, because we cannot move from an `UnsafeCell` (outside of `core::cell`), but
-/// other analyses will likely need to check for `!Freeze`.
-fn for_each_mut_borrow<'tcx>(
- mir: &impl MirVisitable<'tcx>,
- location: Location,
- f: impl FnMut(&mir::Place<'tcx>),
-) {
- let mut vis = OnMutBorrow(f);
-
- mir.apply(location, &mut vis);
-}
diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
index 666c8d50a8a8..531390c2f077 100644
--- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
@@ -1,10 +1,12 @@
-pub use super::*;
-
-use crate::{CallReturnPlaces, GenKill, ResultsClonedCursor};
+use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
+
use std::borrow::Cow;
+use super::MaybeBorrowedLocals;
+use crate::{GenKill, ResultsClonedCursor};
+
#[derive(Clone)]
pub struct MaybeStorageLive<'a> {
always_live_locals: Cow<'a, BitSet>,
@@ -27,12 +29,12 @@ impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> {
const NAME: &'static str = "maybe_storage_live";
- fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+ fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain {
// bottom = dead
BitSet::new_empty(body.local_decls.len())
}
- fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) {
+ fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) {
assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size());
for local in self.always_live_locals.iter() {
on_entry.insert(local);
@@ -47,10 +49,14 @@ impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> {
impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
type Idx = Local;
+ fn domain_size(&self, body: &Body<'tcx>) -> usize {
+ body.local_decls.len()
+ }
+
fn statement_effect(
&mut self,
trans: &mut impl GenKill,
- stmt: &mir::Statement<'tcx>,
+ stmt: &Statement<'tcx>,
_: Location,
) {
match stmt.kind {
@@ -60,13 +66,14 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
}
}
- fn terminator_effect(
+ fn terminator_effect<'mir>(
&mut self,
- _trans: &mut impl GenKill,
- _: &mir::Terminator<'tcx>,
+ _trans: &mut Self::Domain,
+ terminator: &'mir Terminator<'tcx>,
_: Location,
- ) {
+ ) -> TerminatorEdges<'mir, 'tcx> {
// Terminators have no effect
+ terminator.edges()
}
fn call_return_effect(
@@ -95,12 +102,12 @@ impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeStorageDead {
const NAME: &'static str = "maybe_storage_dead";
- fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+ fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain {
// bottom = live
BitSet::new_empty(body.local_decls.len())
}
- fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) {
+ fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) {
assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size());
// Do not iterate on return place and args, as they are trivially always live.
for local in body.vars_and_temps_iter() {
@@ -114,10 +121,14 @@ impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeStorageDead {
impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
type Idx = Local;
+ fn domain_size(&self, body: &Body<'tcx>) -> usize {
+ body.local_decls.len()
+ }
+
fn statement_effect(
&mut self,
trans: &mut impl GenKill,
- stmt: &mir::Statement<'tcx>,
+ stmt: &Statement<'tcx>,
_: Location,
) {
match stmt.kind {
@@ -127,13 +138,14 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
}
}
- fn terminator_effect(
+ fn terminator_effect<'mir>(
&mut self,
- _trans: &mut impl GenKill,
- _: &mir::Terminator<'tcx>,
+ _: &mut Self::Domain,
+ terminator: &'mir Terminator<'tcx>,
_: Location,
- ) {
+ ) -> TerminatorEdges<'mir, 'tcx> {
// Terminators have no effect
+ terminator.edges()
}
fn call_return_effect(
@@ -172,12 +184,12 @@ impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
const NAME: &'static str = "requires_storage";
- fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+ fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain {
// bottom = dead
BitSet::new_empty(body.local_decls.len())
}
- fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) {
+ fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) {
// The resume argument is live on function entry (we don't care about
// the `self` argument)
for arg in body.args_iter().skip(1) {
@@ -189,10 +201,14 @@ impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
type Idx = Local;
+ fn domain_size(&self, body: &Body<'tcx>) -> usize {
+ body.local_decls.len()
+ }
+
fn before_statement_effect(
&mut self,
trans: &mut impl GenKill,
- stmt: &mir::Statement<'tcx>,
+ stmt: &Statement<'tcx>,
loc: Location,
) {
// If a place is borrowed in a statement, it needs storage for that statement.
@@ -225,7 +241,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
fn statement_effect(
&mut self,
trans: &mut impl GenKill,
- _: &mir::Statement<'tcx>,
+ _: &Statement<'tcx>,
loc: Location,
) {
// If we move from a place then it only stops needing storage *after*
@@ -236,11 +252,14 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
fn before_terminator_effect(
&mut self,
trans: &mut impl GenKill,
- terminator: &mir::Terminator<'tcx>,
+ terminator: &Terminator<'tcx>,
loc: Location,
) {
// If a place is borrowed in a terminator, it needs storage for that terminator.
- self.borrowed_locals.mut_analysis().terminator_effect(trans, terminator, loc);
+ self.borrowed_locals
+ .mut_analysis()
+ .transfer_function(trans)
+ .visit_terminator(terminator, loc);
match &terminator.kind {
TerminatorKind::Call { destination, .. } => {
@@ -272,26 +291,26 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
// Nothing to do for these. Match exhaustively so this fails to compile when new
// variants are added.
- TerminatorKind::Terminate
+ TerminatorKind::UnwindTerminate
| TerminatorKind::Assert { .. }
| TerminatorKind::Drop { .. }
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
+ | TerminatorKind::UnwindResume
| TerminatorKind::Return
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::Unreachable => {}
}
}
- fn terminator_effect(
+ fn terminator_effect<'t>(
&mut self,
- trans: &mut impl GenKill,
- terminator: &mir::Terminator<'tcx>,
+ trans: &mut Self::Domain,
+ terminator: &'t Terminator<'tcx>,
loc: Location,
- ) {
+ ) -> TerminatorEdges<'t, 'tcx> {
match terminator.kind {
// For call terminators the destination requires storage for the call
// and after the call returns successfully, but not after a panic.
@@ -309,20 +328,21 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
// Nothing to do for these. Match exhaustively so this fails to compile when new
// variants are added.
TerminatorKind::Yield { .. }
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Assert { .. }
| TerminatorKind::Drop { .. }
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
+ | TerminatorKind::UnwindResume
| TerminatorKind::Return
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::Unreachable => {}
}
self.check_for_move(trans, loc);
+ terminator.edges()
}
fn call_return_effect(
@@ -333,15 +353,6 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
) {
return_places.for_each(|place| trans.gen(place.local));
}
-
- fn yield_resume_effect(
- &mut self,
- trans: &mut impl GenKill,
- _resume_block: BasicBlock,
- resume_place: mir::Place<'tcx>,
- ) {
- trans.gen(resume_place.local);
- }
}
impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> {
diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs
index 900d438f8d53..0cdbee19d2c8 100644
--- a/compiler/rustc_mir_dataflow/src/lib.rs
+++ b/compiler/rustc_mir_dataflow/src/lib.rs
@@ -28,8 +28,8 @@ pub use self::drop_flag_effects::{
};
pub use self::framework::{
fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, AnalysisResults, Backward,
- CallReturnPlaces, CloneAnalysis, Direction, Engine, Forward, GenKill, GenKillAnalysis,
- JoinSemiLattice, Results, ResultsCloned, ResultsClonedCursor, ResultsCursor, ResultsRefCursor,
+ CloneAnalysis, Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice,
+ MaybeReachable, Results, ResultsCloned, ResultsClonedCursor, ResultsCursor, ResultsRefCursor,
ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects,
};
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index 5052de991840..4adf3dec61be 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -370,8 +370,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
// this that could possibly access the return place, this doesn't
// need recording.
| TerminatorKind::Return
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::GeneratorDrop
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. } => {}
diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs
index 17bb8fc37ad1..1eea8eef0adf 100644
--- a/compiler/rustc_mir_dataflow/src/value_analysis.rs
+++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs
@@ -47,8 +47,7 @@ use rustc_target::abi::{FieldIdx, VariantIdx};
use crate::lattice::{HasBottom, HasTop};
use crate::{
- fmt::DebugWithContext, Analysis, AnalysisDomain, CallReturnPlaces, JoinSemiLattice,
- SwitchIntEdgeEffects,
+ fmt::DebugWithContext, Analysis, AnalysisDomain, JoinSemiLattice, SwitchIntEdgeEffects,
};
pub trait ValueAnalysis<'tcx> {
@@ -242,11 +241,19 @@ pub trait ValueAnalysis<'tcx> {
/// The effect of a successful function call return should not be
/// applied here, see [`Analysis::apply_terminator_effect`].
- fn handle_terminator(&self, terminator: &Terminator<'tcx>, state: &mut State) {
+ fn handle_terminator<'mir>(
+ &self,
+ terminator: &'mir Terminator<'tcx>,
+ state: &mut State,
+ ) -> TerminatorEdges<'mir, 'tcx> {
self.super_terminator(terminator, state)
}
- fn super_terminator(&self, terminator: &Terminator<'tcx>, state: &mut State) {
+ fn super_terminator<'mir>(
+ &self,
+ terminator: &'mir Terminator<'tcx>,
+ state: &mut State,
+ ) -> TerminatorEdges<'mir, 'tcx> {
match &terminator.kind {
TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } => {
// Effect is applied by `handle_call_return`.
@@ -258,10 +265,12 @@ pub trait ValueAnalysis<'tcx> {
// They would have an effect, but are not allowed in this phase.
bug!("encountered disallowed terminator");
}
+ TerminatorKind::SwitchInt { discr, targets } => {
+ return self.handle_switch_int(discr, targets, state);
+ }
TerminatorKind::Goto { .. }
- | TerminatorKind::SwitchInt { .. }
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Assert { .. }
@@ -271,6 +280,7 @@ pub trait ValueAnalysis<'tcx> {
// These terminators have no effect on the analysis.
}
}
+ terminator.edges()
}
fn handle_call_return(
@@ -291,19 +301,22 @@ pub trait ValueAnalysis<'tcx> {
})
}
- fn handle_switch_int(
+ fn handle_switch_int<'mir>(
&self,
- discr: &Operand<'tcx>,
- apply_edge_effects: &mut impl SwitchIntEdgeEffects>,
- ) {
- self.super_switch_int(discr, apply_edge_effects)
+ discr: &'mir Operand<'tcx>,
+ targets: &'mir SwitchTargets,
+ state: &mut State,
+ ) -> TerminatorEdges<'mir, 'tcx> {
+ self.super_switch_int(discr, targets, state)
}
- fn super_switch_int(
+ fn super_switch_int<'mir>(
&self,
- _discr: &Operand<'tcx>,
- _apply_edge_effects: &mut impl SwitchIntEdgeEffects>,
- ) {
+ discr: &'mir Operand<'tcx>,
+ targets: &'mir SwitchTargets,
+ _state: &mut State,
+ ) -> TerminatorEdges<'mir, 'tcx> {
+ TerminatorEdges::SwitchInt { discr, targets }
}
fn wrap(self) -> ValueAnalysisWrapper
@@ -353,14 +366,16 @@ where
}
}
- fn apply_terminator_effect(
+ fn apply_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
- terminator: &Terminator<'tcx>,
+ terminator: &'mir Terminator<'tcx>,
_location: Location,
- ) {
+ ) -> TerminatorEdges<'mir, 'tcx> {
if state.is_reachable() {
- self.0.handle_terminator(terminator, state);
+ self.0.handle_terminator(terminator, state)
+ } else {
+ TerminatorEdges::None
}
}
@@ -368,7 +383,7 @@ where
&mut self,
state: &mut Self::Domain,
_block: BasicBlock,
- return_places: crate::CallReturnPlaces<'_, 'tcx>,
+ return_places: CallReturnPlaces<'_, 'tcx>,
) {
if state.is_reachable() {
self.0.handle_call_return(return_places, state)
@@ -378,11 +393,9 @@ where
fn apply_switch_int_edge_effects(
&mut self,
_block: BasicBlock,
- discr: &Operand<'tcx>,
- apply_edge_effects: &mut impl SwitchIntEdgeEffects,
+ _discr: &Operand<'tcx>,
+ _apply_edge_effects: &mut impl SwitchIntEdgeEffects,
) {
- // FIXME: Dataflow framework provides no access to current state here.
- self.0.handle_switch_int(discr, apply_edge_effects)
}
}
diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs
index 58e9786ec1a5..e72db1a59a05 100644
--- a/compiler/rustc_mir_transform/src/check_unsafety.rs
+++ b/compiler/rustc_mir_transform/src/check_unsafety.rs
@@ -57,8 +57,8 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
| TerminatorKind::Yield { .. }
| TerminatorKind::Assert { .. }
| TerminatorKind::GeneratorDrop
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::FalseEdge { .. }
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 7529ed8186bd..a793b384d813 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -180,6 +180,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
throw_machine_stop_str!("calling functions isn't supported in ConstProp")
}
+ fn panic_nounwind(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _msg: &str) -> InterpResult<'tcx> {
+ throw_machine_stop_str!("panicking isn't supported in ConstProp")
+ }
+
fn find_mir_or_eval_fn(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_instance: ty::Instance<'tcx>,
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index ac07c25763ba..4f8ca916d5b4 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -678,8 +678,8 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
}
// None of these have Operands to const-propagate.
TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }
diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs
index d1f2f0c76c83..3d442e5dca9f 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters.rs
@@ -14,36 +14,76 @@ use rustc_index::bit_set::BitSet;
use rustc_index::IndexVec;
use rustc_middle::mir::coverage::*;
+use std::fmt::{self, Debug};
+
+/// The coverage counter or counter expression associated with a particular
+/// BCB node or BCB edge.
+#[derive(Clone)]
+pub(super) enum BcbCounter {
+ Counter { id: CounterId },
+ Expression { id: ExpressionId, lhs: Operand, op: Op, rhs: Operand },
+}
+
+impl BcbCounter {
+ fn is_expression(&self) -> bool {
+ matches!(self, Self::Expression { .. })
+ }
+
+ pub(super) fn as_operand(&self) -> Operand {
+ match *self {
+ BcbCounter::Counter { id, .. } => Operand::Counter(id),
+ BcbCounter::Expression { id, .. } => Operand::Expression(id),
+ }
+ }
+}
+
+impl Debug for BcbCounter {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()),
+ Self::Expression { id, lhs, op, rhs } => write!(
+ fmt,
+ "Expression({:?}) = {:?} {} {:?}",
+ id.index(),
+ lhs,
+ match op {
+ Op::Add => "+",
+ Op::Subtract => "-",
+ },
+ rhs,
+ ),
+ }
+ }
+}
+
/// Generates and stores coverage counter and coverage expression information
/// associated with nodes/edges in the BCB graph.
pub(super) struct CoverageCounters {
- function_source_hash: u64,
next_counter_id: CounterId,
next_expression_id: ExpressionId,
/// Coverage counters/expressions that are associated with individual BCBs.
- bcb_counters: IndexVec>,
+ bcb_counters: IndexVec>,
/// Coverage counters/expressions that are associated with the control-flow
/// edge between two BCBs.
- bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), CoverageKind>,
+ bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>,
/// Tracks which BCBs have a counter associated with some incoming edge.
/// Only used by debug assertions, to verify that BCBs with incoming edge
/// counters do not have their own physical counters (expressions are allowed).
bcb_has_incoming_edge_counters: BitSet,
/// Expression nodes that are not directly associated with any particular
/// BCB/edge, but are needed as operands to more complex expressions.
- /// These are always `CoverageKind::Expression`.
- pub(super) intermediate_expressions: Vec,
+ /// These are always [`BcbCounter::Expression`].
+ pub(super) intermediate_expressions: Vec,
pub debug_counters: DebugCounters,
}
impl CoverageCounters {
- pub(super) fn new(function_source_hash: u64, basic_coverage_blocks: &CoverageGraph) -> Self {
+ pub(super) fn new(basic_coverage_blocks: &CoverageGraph) -> Self {
let num_bcbs = basic_coverage_blocks.num_nodes();
Self {
- function_source_hash,
next_counter_id: CounterId::START,
next_expression_id: ExpressionId::START,
@@ -57,12 +97,12 @@ impl CoverageCounters {
}
/// Activate the `DebugCounters` data structures, to provide additional debug formatting
- /// features when formatting `CoverageKind` (counter) values.
+ /// features when formatting [`BcbCounter`] (counter) values.
pub fn enable_debug(&mut self) {
self.debug_counters.enable();
}
- /// Makes `CoverageKind` `Counter`s and `Expressions` for the `BasicCoverageBlock`s directly or
+ /// Makes [`BcbCounter`] `Counter`s and `Expressions` for the `BasicCoverageBlock`s directly or
/// indirectly associated with `CoverageSpans`, and accumulates additional `Expression`s
/// representing intermediate values.
pub fn make_bcb_counters(
@@ -73,14 +113,11 @@ impl CoverageCounters {
MakeBcbCounters::new(self, basic_coverage_blocks).make_bcb_counters(coverage_spans)
}
- fn make_counter(&mut self, debug_block_label_fn: F) -> CoverageKind
+ fn make_counter(&mut self, debug_block_label_fn: F) -> BcbCounter
where
F: Fn() -> Option,
{
- let counter = CoverageKind::Counter {
- function_source_hash: self.function_source_hash,
- id: self.next_counter(),
- };
+ let counter = BcbCounter::Counter { id: self.next_counter() };
if self.debug_counters.is_enabled() {
self.debug_counters.add_counter(&counter, (debug_block_label_fn)());
}
@@ -93,19 +130,19 @@ impl CoverageCounters {
op: Op,
rhs: Operand,
debug_block_label_fn: F,
- ) -> CoverageKind
+ ) -> BcbCounter
where
F: Fn() -> Option,
{
let id = self.next_expression();
- let expression = CoverageKind::Expression { id, lhs, op, rhs };
+ let expression = BcbCounter::Expression { id, lhs, op, rhs };
if self.debug_counters.is_enabled() {
self.debug_counters.add_counter(&expression, (debug_block_label_fn)());
}
expression
}
- pub fn make_identity_counter(&mut self, counter_operand: Operand) -> CoverageKind {
+ pub fn make_identity_counter(&mut self, counter_operand: Operand) -> BcbCounter {
let some_debug_block_label = if self.debug_counters.is_enabled() {
self.debug_counters.some_block_label(counter_operand).cloned()
} else {
@@ -134,7 +171,7 @@ impl CoverageCounters {
fn set_bcb_counter(
&mut self,
bcb: BasicCoverageBlock,
- counter_kind: CoverageKind,
+ counter_kind: BcbCounter,
) -> Result {
debug_assert!(
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
@@ -158,7 +195,7 @@ impl CoverageCounters {
&mut self,
from_bcb: BasicCoverageBlock,
to_bcb: BasicCoverageBlock,
- counter_kind: CoverageKind,
+ counter_kind: BcbCounter,
) -> Result {
if level_enabled!(tracing::Level::DEBUG) {
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
@@ -183,17 +220,17 @@ impl CoverageCounters {
}
}
- pub(super) fn bcb_counter(&self, bcb: BasicCoverageBlock) -> Option<&CoverageKind> {
+ pub(super) fn bcb_counter(&self, bcb: BasicCoverageBlock) -> Option<&BcbCounter> {
self.bcb_counters[bcb].as_ref()
}
- pub(super) fn take_bcb_counter(&mut self, bcb: BasicCoverageBlock) -> Option {
+ pub(super) fn take_bcb_counter(&mut self, bcb: BasicCoverageBlock) -> Option {
self.bcb_counters[bcb].take()
}
pub(super) fn drain_bcb_counters(
&mut self,
- ) -> impl Iterator- + '_ {
+ ) -> impl Iterator
- + '_ {
self.bcb_counters
.iter_enumerated_mut()
.filter_map(|(bcb, counter)| Some((bcb, counter.take()?)))
@@ -201,7 +238,7 @@ impl CoverageCounters {
pub(super) fn drain_bcb_edge_counters(
&mut self,
- ) -> impl Iterator
- + '_ {
+ ) -> impl Iterator
- + '_ {
self.bcb_edge_counters.drain()
}
}
@@ -653,7 +690,7 @@ impl<'a> MakeBcbCounters<'a> {
self.branch_counter(branch).is_none()
}
- fn branch_counter(&self, branch: &BcbBranch) -> Option<&CoverageKind> {
+ fn branch_counter(&self, branch: &BcbBranch) -> Option<&BcbCounter> {
let to_bcb = branch.target_bcb;
if let Some(from_bcb) = branch.edge_from_bcb {
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
@@ -675,7 +712,7 @@ impl<'a> MakeBcbCounters<'a> {
}
#[inline]
- fn format_counter(&self, counter_kind: &CoverageKind) -> String {
+ fn format_counter(&self, counter_kind: &BcbCounter) -> String {
self.coverage_counters.debug_counters.format_counter(counter_kind)
}
}
diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs
index d2c0c4ba0692..af616c498fd3 100644
--- a/compiler/rustc_mir_transform/src/coverage/debug.rs
+++ b/compiler/rustc_mir_transform/src/coverage/debug.rs
@@ -108,7 +108,7 @@
//! recursively, generating labels with nested operations, enclosed in parentheses
//! (for example: `bcb2 + (bcb0 - bcb1)`).
-use super::counters::CoverageCounters;
+use super::counters::{BcbCounter, CoverageCounters};
use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
use super::spans::CoverageSpan;
@@ -199,9 +199,9 @@ impl DebugOptions {
fn bool_option_val(option: &str, some_strval: Option<&str>) -> bool {
if let Some(val) = some_strval {
- if vec!["yes", "y", "on", "true"].contains(&val) {
+ if ["yes", "y", "on", "true"].contains(&val) {
true
- } else if vec!["no", "n", "off", "false"].contains(&val) {
+ } else if ["no", "n", "off", "false"].contains(&val) {
false
} else {
bug!(
@@ -247,11 +247,11 @@ impl Default for ExpressionFormat {
}
}
-/// If enabled, this struct maintains a map from `CoverageKind` IDs (as `Operand`) to
-/// the `CoverageKind` data and optional label (normally, the counter's associated
+/// If enabled, this struct maintains a map from `BcbCounter` IDs (as `Operand`) to
+/// the `BcbCounter` data and optional label (normally, the counter's associated
/// `BasicCoverageBlock` format string, if any).
///
-/// Use `format_counter` to convert one of these `CoverageKind` counters to a debug output string,
+/// Use `format_counter` to convert one of these `BcbCounter` counters to a debug output string,
/// as directed by the `DebugOptions`. This allows the format of counter labels in logs and dump
/// files (including the `CoverageGraph` graphviz file) to be changed at runtime, via environment
/// variable.
@@ -276,7 +276,7 @@ impl DebugCounters {
self.some_counters.is_some()
}
- pub fn add_counter(&mut self, counter_kind: &CoverageKind, some_block_label: Option) {
+ pub fn add_counter(&mut self, counter_kind: &BcbCounter, some_block_label: Option) {
if let Some(counters) = &mut self.some_counters {
let id = counter_kind.as_operand();
counters
@@ -291,21 +291,20 @@ impl DebugCounters {
})
}
- pub fn format_counter(&self, counter_kind: &CoverageKind) -> String {
+ pub fn format_counter(&self, counter_kind: &BcbCounter) -> String {
match *counter_kind {
- CoverageKind::Counter { .. } => {
+ BcbCounter::Counter { .. } => {
format!("Counter({})", self.format_counter_kind(counter_kind))
}
- CoverageKind::Expression { .. } => {
+ BcbCounter::Expression { .. } => {
format!("Expression({})", self.format_counter_kind(counter_kind))
}
- CoverageKind::Unreachable { .. } => "Unreachable".to_owned(),
}
}
- fn format_counter_kind(&self, counter_kind: &CoverageKind) -> String {
+ fn format_counter_kind(&self, counter_kind: &BcbCounter) -> String {
let counter_format = &debug_options().counter_format;
- if let CoverageKind::Expression { id, lhs, op, rhs } = *counter_kind {
+ if let BcbCounter::Expression { id, lhs, op, rhs } = *counter_kind {
if counter_format.operation {
return format!(
"{}{} {} {}",
@@ -346,7 +345,7 @@ impl DebugCounters {
}
if let Some(counters) = &self.some_counters {
if let Some(DebugCounter { counter_kind, some_block_label }) = counters.get(&operand) {
- if let CoverageKind::Expression { .. } = counter_kind {
+ if let BcbCounter::Expression { .. } = counter_kind {
if let Some(label) = some_block_label && debug_options().counter_format.block {
return format!(
"{}:({})",
@@ -366,12 +365,12 @@ impl DebugCounters {
/// A non-public support class to `DebugCounters`.
#[derive(Debug)]
struct DebugCounter {
- counter_kind: CoverageKind,
+ counter_kind: BcbCounter,
some_block_label: Option,
}
impl DebugCounter {
- fn new(counter_kind: CoverageKind, some_block_label: Option) -> Self {
+ fn new(counter_kind: BcbCounter, some_block_label: Option) -> Self {
Self { counter_kind, some_block_label }
}
}
@@ -380,9 +379,9 @@ impl DebugCounter {
/// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes.
pub(super) struct GraphvizData {
some_bcb_to_coverage_spans_with_counters:
- Option>>,
- some_bcb_to_dependency_counters: Option>>,
- some_edge_to_counter: Option>,
+ Option>>,
+ some_bcb_to_dependency_counters: Option>>,
+ some_edge_to_counter: Option>,
}
impl GraphvizData {
@@ -409,7 +408,7 @@ impl GraphvizData {
&mut self,
bcb: BasicCoverageBlock,
coverage_span: &CoverageSpan,
- counter_kind: &CoverageKind,
+ counter_kind: &BcbCounter,
) {
if let Some(bcb_to_coverage_spans_with_counters) =
self.some_bcb_to_coverage_spans_with_counters.as_mut()
@@ -424,7 +423,7 @@ impl GraphvizData {
pub fn get_bcb_coverage_spans_with_counters(
&self,
bcb: BasicCoverageBlock,
- ) -> Option<&[(CoverageSpan, CoverageKind)]> {
+ ) -> Option<&[(CoverageSpan, BcbCounter)]> {
if let Some(bcb_to_coverage_spans_with_counters) =
self.some_bcb_to_coverage_spans_with_counters.as_ref()
{
@@ -437,7 +436,7 @@ impl GraphvizData {
pub fn add_bcb_dependency_counter(
&mut self,
bcb: BasicCoverageBlock,
- counter_kind: &CoverageKind,
+ counter_kind: &BcbCounter,
) {
if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_mut() {
bcb_to_dependency_counters
@@ -447,7 +446,7 @@ impl GraphvizData {
}
}
- pub fn get_bcb_dependency_counters(&self, bcb: BasicCoverageBlock) -> Option<&[CoverageKind]> {
+ pub fn get_bcb_dependency_counters(&self, bcb: BasicCoverageBlock) -> Option<&[BcbCounter]> {
if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_ref() {
bcb_to_dependency_counters.get(&bcb).map(Deref::deref)
} else {
@@ -459,7 +458,7 @@ impl GraphvizData {
&mut self,
from_bcb: BasicCoverageBlock,
to_bb: BasicBlock,
- counter_kind: &CoverageKind,
+ counter_kind: &BcbCounter,
) {
if let Some(edge_to_counter) = self.some_edge_to_counter.as_mut() {
edge_to_counter
@@ -472,7 +471,7 @@ impl GraphvizData {
&self,
from_bcb: BasicCoverageBlock,
to_bb: BasicBlock,
- ) -> Option<&CoverageKind> {
+ ) -> Option<&BcbCounter> {
if let Some(edge_to_counter) = self.some_edge_to_counter.as_ref() {
edge_to_counter.get(&(from_bcb, to_bb))
} else {
@@ -488,7 +487,7 @@ impl GraphvizData {
pub(super) struct UsedExpressions {
some_used_expression_operands: Option>>,
some_unused_expressions:
- Option, BasicCoverageBlock)>>,
+ Option, BasicCoverageBlock)>>,
}
impl UsedExpressions {
@@ -506,16 +505,16 @@ impl UsedExpressions {
self.some_used_expression_operands.is_some()
}
- pub fn add_expression_operands(&mut self, expression: &CoverageKind) {
+ pub fn add_expression_operands(&mut self, expression: &BcbCounter) {
if let Some(used_expression_operands) = self.some_used_expression_operands.as_mut() {
- if let CoverageKind::Expression { id, lhs, rhs, .. } = *expression {
+ if let BcbCounter::Expression { id, lhs, rhs, .. } = *expression {
used_expression_operands.entry(lhs).or_insert_with(Vec::new).push(id);
used_expression_operands.entry(rhs).or_insert_with(Vec::new).push(id);
}
}
}
- pub fn expression_is_used(&self, expression: &CoverageKind) -> bool {
+ pub fn expression_is_used(&self, expression: &BcbCounter) -> bool {
if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() {
used_expression_operands.contains_key(&expression.as_operand())
} else {
@@ -525,7 +524,7 @@ impl UsedExpressions {
pub fn add_unused_expression_if_not_found(
&mut self,
- expression: &CoverageKind,
+ expression: &BcbCounter,
edge_from_bcb: Option,
target_bcb: BasicCoverageBlock,
) {
@@ -540,11 +539,11 @@ impl UsedExpressions {
}
}
- /// Return the list of unused counters (if any) as a tuple with the counter (`CoverageKind`),
+ /// Return the list of unused counters (if any) as a tuple with the counter (`BcbCounter`),
/// optional `from_bcb` (if it was an edge counter), and `target_bcb`.
pub fn get_unused_expressions(
&self,
- ) -> Vec<(CoverageKind, Option, BasicCoverageBlock)> {
+ ) -> Vec<(BcbCounter, Option, BasicCoverageBlock)> {
if let Some(unused_expressions) = self.some_unused_expressions.as_ref() {
unused_expressions.clone()
} else {
@@ -560,7 +559,7 @@ impl UsedExpressions {
bcb_counters_without_direct_coverage_spans: &[(
Option,
BasicCoverageBlock,
- CoverageKind,
+ BcbCounter,
)],
) {
if self.is_enabled() {
@@ -662,7 +661,7 @@ pub(super) fn dump_coverage_graphviz<'tcx>(
basic_coverage_blocks: &CoverageGraph,
coverage_counters: &CoverageCounters,
graphviz_data: &GraphvizData,
- intermediate_expressions: &[CoverageKind],
+ intermediate_expressions: &[BcbCounter],
debug_used_expressions: &UsedExpressions,
) {
let debug_counters = &coverage_counters.debug_counters;
@@ -743,9 +742,9 @@ fn bcb_to_string_sections<'tcx>(
coverage_counters: &CoverageCounters,
bcb: BasicCoverageBlock,
bcb_data: &BasicCoverageBlockData,
- some_coverage_spans_with_counters: Option<&[(CoverageSpan, CoverageKind)]>,
- some_dependency_counters: Option<&[CoverageKind]>,
- some_intermediate_expressions: Option<&[CoverageKind]>,
+ some_coverage_spans_with_counters: Option<&[(CoverageSpan, BcbCounter)]>,
+ some_dependency_counters: Option<&[BcbCounter]>,
+ some_intermediate_expressions: Option<&[BcbCounter]>,
) -> Vec {
let debug_counters = &coverage_counters.debug_counters;
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index 59b01ffec0f1..d3d4fcd3a527 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -116,7 +116,7 @@ impl CoverageGraph {
match term.kind {
TerminatorKind::Return { .. }
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Yield { .. }
| TerminatorKind::SwitchInt { .. } => {
// The `bb` has more than one _outgoing_ edge, or exits the function. Save the
@@ -146,7 +146,7 @@ impl CoverageGraph {
// is as intended. (See Issue #78544 for a possible future option to support
// coverage in test programs that panic.)
TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
+ | TerminatorKind::UnwindResume
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }
| TerminatorKind::Call { .. }
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index e08b6d6f6e8d..8c9eae508b4a 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -8,9 +8,9 @@ mod spans;
#[cfg(test)]
mod tests;
-use counters::CoverageCounters;
-use graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
-use spans::{CoverageSpan, CoverageSpans};
+use self::counters::{BcbCounter, CoverageCounters};
+use self::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
+use self::spans::{CoverageSpan, CoverageSpans};
use crate::MirPass;
@@ -106,6 +106,7 @@ struct Instrumentor<'a, 'tcx> {
source_file: Lrc,
fn_sig_span: Span,
body_span: Span,
+ function_source_hash: u64,
basic_coverage_blocks: CoverageGraph,
coverage_counters: CoverageCounters,
}
@@ -137,7 +138,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
let function_source_hash = hash_mir_source(tcx, hir_body);
let basic_coverage_blocks = CoverageGraph::from_mir(mir_body);
- let coverage_counters = CoverageCounters::new(function_source_hash, &basic_coverage_blocks);
+ let coverage_counters = CoverageCounters::new(&basic_coverage_blocks);
Self {
pass_name,
@@ -146,6 +147,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
source_file,
fn_sig_span,
body_span,
+ function_source_hash,
basic_coverage_blocks,
coverage_counters,
}
@@ -270,8 +272,11 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
////////////////////////////////////////////////////
// Finally, inject the intermediate expressions collected along the way.
- for intermediate_expression in self.coverage_counters.intermediate_expressions.drain(..) {
- inject_intermediate_expression(self.mir_body, intermediate_expression);
+ for intermediate_expression in &self.coverage_counters.intermediate_expressions {
+ inject_intermediate_expression(
+ self.mir_body,
+ self.make_mir_coverage_kind(intermediate_expression),
+ );
}
}
@@ -309,19 +314,14 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
};
graphviz_data.add_bcb_coverage_span_with_counter(bcb, &covspan, &counter_kind);
- debug!(
- "Calling make_code_region(file_name={}, source_file={:?}, span={}, body_span={})",
- file_name,
- self.source_file,
- source_map.span_to_diagnostic_string(span),
- source_map.span_to_diagnostic_string(body_span)
- );
+ let code_region =
+ make_code_region(source_map, file_name, &self.source_file, span, body_span);
inject_statement(
self.mir_body,
- counter_kind,
+ self.make_mir_coverage_kind(&counter_kind),
self.bcb_leader_bb(bcb),
- Some(make_code_region(source_map, file_name, &self.source_file, span, body_span)),
+ Some(code_region),
);
}
}
@@ -367,7 +367,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
);
match counter_kind {
- CoverageKind::Counter { .. } => {
+ BcbCounter::Counter { .. } => {
let inject_to_bb = if let Some(from_bcb) = edge_from_bcb {
// The MIR edge starts `from_bb` (the outgoing / last BasicBlock in
// `from_bcb`) and ends at `to_bb` (the incoming / first BasicBlock in the
@@ -400,12 +400,17 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
target_bb
};
- inject_statement(self.mir_body, counter_kind, inject_to_bb, None);
+ inject_statement(
+ self.mir_body,
+ self.make_mir_coverage_kind(&counter_kind),
+ inject_to_bb,
+ None,
+ );
}
- CoverageKind::Expression { .. } => {
- inject_intermediate_expression(self.mir_body, counter_kind)
- }
- _ => bug!("CoverageKind should be a counter"),
+ BcbCounter::Expression { .. } => inject_intermediate_expression(
+ self.mir_body,
+ self.make_mir_coverage_kind(&counter_kind),
+ ),
}
}
}
@@ -426,9 +431,20 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
}
#[inline]
- fn format_counter(&self, counter_kind: &CoverageKind) -> String {
+ fn format_counter(&self, counter_kind: &BcbCounter) -> String {
self.coverage_counters.debug_counters.format_counter(counter_kind)
}
+
+ fn make_mir_coverage_kind(&self, counter_kind: &BcbCounter) -> CoverageKind {
+ match *counter_kind {
+ BcbCounter::Counter { id } => {
+ CoverageKind::Counter { function_source_hash: self.function_source_hash, id }
+ }
+ BcbCounter::Expression { id, lhs, op, rhs } => {
+ CoverageKind::Expression { id, lhs, op, rhs }
+ }
+ }
+ }
}
fn inject_edge_counter_basic_block(
@@ -498,6 +514,14 @@ fn make_code_region(
span: Span,
body_span: Span,
) -> CodeRegion {
+ debug!(
+ "Called make_code_region(file_name={}, source_file={:?}, span={}, body_span={})",
+ file_name,
+ source_file,
+ source_map.span_to_diagnostic_string(span),
+ source_map.span_to_diagnostic_string(body_span)
+ );
+
let (start_line, mut start_col) = source_file.lookup_file_pos(span.lo());
let (end_line, end_col) = if span.hi() == span.lo() {
let (end_line, mut end_col) = (start_line, start_col);
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index deebf5345bac..6fabaca524a0 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -867,8 +867,8 @@ pub(super) fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option id,
+ counters::BcbCounter::Counter { id, .. } => id,
_ => panic!("expected a Counter"),
}
.as_u32()
@@ -695,7 +694,7 @@ fn test_make_bcb_counters() {
assert_eq!(
1, // bcb2 has a `Counter` with id = 1
match coverage_counters.bcb_counter(bcb2).expect("should have a counter") {
- CoverageKind::Counter { id, .. } => id,
+ counters::BcbCounter::Counter { id, .. } => id,
_ => panic!("expected a Counter"),
}
.as_u32()
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index 7d7588fcaecd..3a1ef3e7d64a 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -13,9 +13,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_mir_dataflow::value_analysis::{
Map, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
};
-use rustc_mir_dataflow::{
- lattice::FlatSet, Analysis, Results, ResultsVisitor, SwitchIntEdgeEffects,
-};
+use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor};
use rustc_span::DUMMY_SP;
use rustc_target::abi::{Align, FieldIdx, VariantIdx};
@@ -249,49 +247,27 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
.unwrap_or(FlatSet::Top)
}
- fn handle_switch_int(
+ fn handle_switch_int<'mir>(
&self,
- discr: &Operand<'tcx>,
- apply_edge_effects: &mut impl SwitchIntEdgeEffects>,
- ) {
- // FIXME: The dataflow framework only provides the state if we call `apply()`, which makes
- // this more inefficient than it has to be.
- let mut discr_value = None;
- let mut handled = false;
- apply_edge_effects.apply(|state, target| {
- let discr_value = match discr_value {
- Some(value) => value,
- None => {
- let value = match self.handle_operand(discr, state) {
- ValueOrPlace::Value(value) => value,
- ValueOrPlace::Place(place) => state.get_idx(place, self.map()),
- };
- let result = match value {
- FlatSet::Top => FlatSet::Top,
- FlatSet::Elem(ScalarTy(scalar, _)) => {
- let int = scalar.assert_int();
- FlatSet::Elem(int.assert_bits(int.size()))
- }
- FlatSet::Bottom => FlatSet::Bottom,
- };
- discr_value = Some(result);
- result
- }
- };
-
- let FlatSet::Elem(choice) = discr_value else {
- // Do nothing if we don't know which branch will be taken.
- return;
- };
-
- if target.value.map(|n| n == choice).unwrap_or(!handled) {
- // Branch is taken. Has no effect on state.
- handled = true;
- } else {
- // Branch is not taken.
- state.mark_unreachable();
+ discr: &'mir Operand<'tcx>,
+ targets: &'mir SwitchTargets,
+ state: &mut State,
+ ) -> TerminatorEdges<'mir, 'tcx> {
+ let value = match self.handle_operand(discr, state) {
+ ValueOrPlace::Value(value) => value,
+ ValueOrPlace::Place(place) => state.get_idx(place, self.map()),
+ };
+ match value {
+ // We are branching on uninitialized data, this is UB, treat it as unreachable.
+ // This allows the set of visited edges to grow monotonically with the lattice.
+ FlatSet::Bottom => TerminatorEdges::None,
+ FlatSet::Elem(ScalarTy(scalar, _)) => {
+ let int = scalar.assert_int();
+ let choice = int.assert_bits(int.size());
+ TerminatorEdges::Single(targets.target_for_value(choice))
}
- })
+ FlatSet::Top => TerminatorEdges::SwitchInt { discr, targets },
+ }
}
}
@@ -565,6 +541,13 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
unimplemented!()
}
+ fn panic_nounwind(
+ _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ _msg: &str,
+ ) -> interpret::InterpResult<'tcx> {
+ unimplemented!()
+ }
+
fn call_intrinsic(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_instance: ty::Instance<'tcx>,
diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs
index 60ca3dfb2dab..79645310a398 100644
--- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs
+++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs
@@ -203,7 +203,12 @@ pub fn deduced_param_attrs<'tcx>(
body.local_decls.iter().skip(1).take(body.arg_count).enumerate().map(
|(arg_index, local_decl)| DeducedParamAttrs {
read_only: !deduce_read_only.mutable_args.contains(arg_index)
- && local_decl.ty.is_freeze(tcx, param_env),
+ // We must normalize here to reveal opaques and normalize
+ // their substs, otherwise we'll see exponential blow-up in
+ // compile times: #113372
+ && tcx
+ .normalize_erasing_regions(param_env, local_decl.ty)
+ .is_freeze(tcx, param_env),
},
),
);
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index b73b72c3192e..041f7c7221e3 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -647,8 +647,8 @@ impl WriteInfo {
}
}
TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::Unreachable { .. } => (),
TerminatorKind::Drop { .. } => {
diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs
index 43757a9ea353..a80ae480089b 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs
@@ -48,6 +48,7 @@ use std::fmt;
pub struct ElaborateDrops;
impl<'tcx> MirPass<'tcx> for ElaborateDrops {
+ #[instrument(level = "trace", skip(self, tcx, body))]
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
debug!("elaborate_drops({:?} @ {:?})", body.source, body.span);
@@ -65,23 +66,23 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
};
let elaborate_patch = {
let env = MoveDataParamEnv { move_data, param_env };
- remove_dead_unwinds(tcx, body, &env);
- let inits = MaybeInitializedPlaces::new(tcx, body, &env)
+ let mut inits = MaybeInitializedPlaces::new(tcx, body, &env)
+ .skipping_unreachable_unwind()
.into_engine(tcx, body)
.pass_name("elaborate_drops")
.iterate_to_fixpoint()
.into_results_cursor(body);
+ let dead_unwinds = compute_dead_unwinds(&body, &mut inits);
let uninits = MaybeUninitializedPlaces::new(tcx, body, &env)
.mark_inactive_variants_as_uninit()
+ .skipping_unreachable_unwind(dead_unwinds)
.into_engine(tcx, body)
.pass_name("elaborate_drops")
.iterate_to_fixpoint()
.into_results_cursor(body);
- let reachable = traversal::reachable_as_bitset(body);
-
let drop_flags = IndexVec::from_elem(None, &env.move_data.move_paths);
ElaborateDropsCtxt {
tcx,
@@ -90,7 +91,6 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
init_data: InitializationData { inits, uninits },
drop_flags,
patch: MirPatch::new(body),
- reachable,
}
.elaborate()
};
@@ -99,65 +99,30 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
}
}
-/// Removes unwind edges which are known to be unreachable, because they are in `drop` terminators
+/// Records unwind edges which are known to be unreachable, because they are in `drop` terminators
/// that can't drop anything.
-fn remove_dead_unwinds<'tcx>(
- tcx: TyCtxt<'tcx>,
- body: &mut Body<'tcx>,
- env: &MoveDataParamEnv<'tcx>,
-) {
- debug!("remove_dead_unwinds({:?})", body.span);
+#[instrument(level = "trace", skip(body, flow_inits), ret)]
+fn compute_dead_unwinds<'mir, 'tcx>(
+ body: &'mir Body<'tcx>,
+ flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
+) -> BitSet {
// We only need to do this pass once, because unwind edges can only
// reach cleanup blocks, which can't have unwind edges themselves.
- let mut dead_unwinds = Vec::new();
- let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &env)
- .into_engine(tcx, body)
- .pass_name("remove_dead_unwinds")
- .iterate_to_fixpoint()
- .into_results_cursor(body);
+ let mut dead_unwinds = BitSet::new_empty(body.basic_blocks.len());
for (bb, bb_data) in body.basic_blocks.iter_enumerated() {
- let place = match bb_data.terminator().kind {
- TerminatorKind::Drop { place, unwind: UnwindAction::Cleanup(_), .. } => place,
- _ => continue,
- };
-
- debug!("remove_dead_unwinds @ {:?}: {:?}", bb, bb_data);
-
- let LookupResult::Exact(path) = env.move_data.rev_lookup.find(place.as_ref()) else {
- debug!("remove_dead_unwinds: has parent; skipping");
+ let TerminatorKind::Drop { place, unwind: UnwindAction::Cleanup(_), .. } =
+ bb_data.terminator().kind
+ else {
continue;
};
flow_inits.seek_before_primary_effect(body.terminator_loc(bb));
- debug!(
- "remove_dead_unwinds @ {:?}: path({:?})={:?}; init_data={:?}",
- bb,
- place,
- path,
- flow_inits.get()
- );
-
- let mut maybe_live = false;
- on_all_drop_children_bits(tcx, body, &env, path, |child| {
- maybe_live |= flow_inits.contains(child);
- });
-
- debug!("remove_dead_unwinds @ {:?}: maybe_live={}", bb, maybe_live);
- if !maybe_live {
- dead_unwinds.push(bb);
+ if flow_inits.analysis().is_unwind_dead(place, flow_inits.get()) {
+ dead_unwinds.insert(bb);
}
}
- if dead_unwinds.is_empty() {
- return;
- }
-
- let basic_blocks = body.basic_blocks.as_mut();
- for &bb in dead_unwinds.iter() {
- if let Some(unwind) = basic_blocks[bb].terminator_mut().unwind_mut() {
- *unwind = UnwindAction::Unreachable;
- }
- }
+ dead_unwinds
}
struct InitializationData<'mir, 'tcx> {
@@ -290,7 +255,6 @@ struct ElaborateDropsCtxt<'a, 'tcx> {
init_data: InitializationData<'a, 'tcx>,
drop_flags: IndexVec>,
patch: MirPatch<'tcx>,
- reachable: BitSet,
}
impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
@@ -330,9 +294,6 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
fn collect_drop_flags(&mut self) {
for (bb, data) in self.body.basic_blocks.iter_enumerated() {
- if !self.reachable.contains(bb) {
- continue;
- }
let terminator = data.terminator();
let place = match terminator.kind {
TerminatorKind::Drop { ref place, .. } => place,
@@ -384,9 +345,6 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
fn elaborate_drops(&mut self) {
for (bb, data) in self.body.basic_blocks.iter_enumerated() {
- if !self.reachable.contains(bb) {
- continue;
- }
let loc = Location { block: bb, statement_index: data.statements.len() };
let terminator = data.terminator();
@@ -465,9 +423,6 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
fn drop_flags_for_fn_rets(&mut self) {
for (bb, data) in self.body.basic_blocks.iter_enumerated() {
- if !self.reachable.contains(bb) {
- continue;
- }
if let TerminatorKind::Call {
destination,
target: Some(tgt),
@@ -506,9 +461,6 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
// clobbered before they are read.
for (bb, data) in self.body.basic_blocks.iter_enumerated() {
- if !self.reachable.contains(bb) {
- continue;
- }
debug!("drop_flags_for_locs({:?})", data);
for i in 0..(data.statements.len() + 1) {
debug!("drop_flag_for_locs: stmt {}", i);
@@ -518,7 +470,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
// drop elaboration should handle that by itself
continue;
}
- TerminatorKind::Resume => {
+ TerminatorKind::UnwindResume => {
// It is possible for `Resume` to be patched
// (in particular it can be patched to be replaced with
// a Goto; see `MirPatch::new`).
diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs
index 797a1a86846f..96077322575e 100644
--- a/compiler/rustc_mir_transform/src/generator.rs
+++ b/compiler/rustc_mir_transform/src/generator.rs
@@ -50,8 +50,10 @@
//! For generators with state 1 (returned) and state 2 (poisoned) it does nothing.
//! Otherwise it drops all the values in scope at the last suspension point.
+use crate::abort_unwinding_calls;
use crate::deref_separator::deref_finder;
use crate::errors;
+use crate::pass_manager as pm;
use crate::simplify;
use crate::MirPass;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -64,6 +66,7 @@ use rustc_index::{Idx, IndexVec};
use rustc_middle::mir::dump_mir;
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
use rustc_middle::mir::*;
+use rustc_middle::ty::InstanceDef;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
use rustc_middle::ty::{GeneratorArgs, GenericArgsRef};
use rustc_mir_dataflow::impls::{
@@ -1147,7 +1150,25 @@ fn create_generator_drop_shim<'tcx>(
// unrelated code from the resume part of the function
simplify::remove_dead_blocks(tcx, &mut body);
+ // Update the body's def to become the drop glue.
+ // This needs to be updated before the AbortUnwindingCalls pass.
+ let gen_instance = body.source.instance;
+ let drop_in_place = tcx.require_lang_item(LangItem::DropInPlace, None);
+ let drop_instance = InstanceDef::DropGlue(drop_in_place, Some(gen_ty));
+ body.source.instance = drop_instance;
+
+ pm::run_passes_no_validate(
+ tcx,
+ &mut body,
+ &[&abort_unwinding_calls::AbortUnwindingCalls],
+ None,
+ );
+
+ // Temporary change MirSource to generator's instance so that dump_mir produces more sensible
+ // filename.
+ body.source.instance = gen_instance;
dump_mir(tcx, false, "generator_drop", &0, &body, |_, _| Ok(()));
+ body.source.instance = drop_instance;
body
}
@@ -1218,7 +1239,7 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool {
// These never unwind.
TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop
@@ -1227,7 +1248,7 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool {
// Resume will *continue* unwinding, but if there's no other unwinding terminator it
// will never be reached.
- TerminatorKind::Resume => {}
+ TerminatorKind::UnwindResume => {}
TerminatorKind::Yield { .. } => {
unreachable!("`can_unwind` called before generator transform")
@@ -1258,14 +1279,14 @@ fn create_generator_resume_function<'tcx>(
let source_info = SourceInfo::outermost(body.span);
let poison_block = body.basic_blocks_mut().push(BasicBlockData {
statements: vec![transform.set_discr(VariantIdx::new(POISONED), source_info)],
- terminator: Some(Terminator { source_info, kind: TerminatorKind::Resume }),
+ terminator: Some(Terminator { source_info, kind: TerminatorKind::UnwindResume }),
is_cleanup: true,
});
for (idx, block) in body.basic_blocks_mut().iter_enumerated_mut() {
let source_info = block.terminator().source_info;
- if let TerminatorKind::Resume = block.terminator().kind {
+ if let TerminatorKind::UnwindResume = block.terminator().kind {
// An existing `Resume` terminator is redirected to jump to our dedicated
// "poisoning block" above.
if idx != poison_block {
@@ -1317,6 +1338,8 @@ fn create_generator_resume_function<'tcx>(
// unrelated code from the drop part of the function
simplify::remove_dead_blocks(tcx, body);
+ pm::run_passes_no_validate(tcx, body, &[&abort_unwinding_calls::AbortUnwindingCalls], None);
+
dump_mir(tcx, false, "generator_resume", &0, body, |_, _| Ok(()));
}
@@ -1735,8 +1758,8 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
TerminatorKind::Call { .. }
| TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index fc9e18378d5a..734e93783d10 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -839,7 +839,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
self.cost += LANDINGPAD_PENALTY;
}
}
- TerminatorKind::Resume => self.cost += RESUME_PENALTY,
+ TerminatorKind::UnwindResume => self.cost += RESUME_PENALTY,
TerminatorKind::InlineAsm { unwind, .. } => {
self.cost += INSTR_COST;
if let UnwindAction::Cleanup(_) = unwind {
@@ -1017,15 +1017,15 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> {
TerminatorKind::Unreachable
}
}
- TerminatorKind::Resume => {
+ TerminatorKind::UnwindResume => {
terminator.kind = match self.cleanup_block {
UnwindAction::Cleanup(tgt) => TerminatorKind::Goto { target: tgt },
- UnwindAction::Continue => TerminatorKind::Resume,
+ UnwindAction::Continue => TerminatorKind::UnwindResume,
UnwindAction::Unreachable => TerminatorKind::Unreachable,
- UnwindAction::Terminate => TerminatorKind::Terminate,
+ UnwindAction::Terminate => TerminatorKind::UnwindTerminate,
};
}
- TerminatorKind::Terminate => {}
+ TerminatorKind::UnwindTerminate => {}
TerminatorKind::Unreachable => {}
TerminatorKind::FalseEdge { ref mut real_target, ref mut imaginary_target } => {
*real_target = self.map_block(*real_target);
diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs
index c17c791f9c3f..49a940b57799 100644
--- a/compiler/rustc_mir_transform/src/ref_prop.rs
+++ b/compiler/rustc_mir_transform/src/ref_prop.rs
@@ -265,7 +265,6 @@ fn compute_replacement<'tcx>(
targets,
storage_to_remove,
allowed_replacements,
- fully_replacable_locals,
any_replacement: false,
};
@@ -346,7 +345,6 @@ struct Replacer<'tcx> {
storage_to_remove: BitSet,
allowed_replacements: FxHashSet<(Local, Location)>,
any_replacement: bool,
- fully_replacable_locals: BitSet,
}
impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
@@ -366,12 +364,6 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
if let Some((&PlaceElem::Deref, rest)) = target.projection.split_last() {
*place = Place::from(target.local).project_deeper(rest, self.tcx);
self.any_replacement = true;
- } else if self.fully_replacable_locals.contains(place.local)
- && let Some(references) = debuginfo.references.checked_add(1)
- {
- debuginfo.references = references;
- *place = target;
- self.any_replacement = true;
} else {
break
}
diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
index 4941c9edce30..5782adbb3ffa 100644
--- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
+++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
@@ -6,8 +6,8 @@ use rustc_middle::ty::TyCtxt;
use rustc_target::spec::PanicStrategy;
/// A pass that removes noop landing pads and replaces jumps to them with
-/// `None`. This is important because otherwise LLVM generates terrible
-/// code for these.
+/// `UnwindAction::Continue`. This is important because otherwise LLVM generates
+/// terrible code for these.
pub struct RemoveNoopLandingPads;
impl<'tcx> MirPass<'tcx> for RemoveNoopLandingPads {
@@ -63,7 +63,7 @@ impl RemoveNoopLandingPads {
let terminator = body[bb].terminator();
match terminator.kind {
TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
+ | TerminatorKind::UnwindResume
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. } => {
@@ -72,7 +72,7 @@ impl RemoveNoopLandingPads {
TerminatorKind::GeneratorDrop
| TerminatorKind::Yield { .. }
| TerminatorKind::Return
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Unreachable
| TerminatorKind::Call { .. }
| TerminatorKind::Assert { .. }
@@ -84,7 +84,17 @@ impl RemoveNoopLandingPads {
fn remove_nop_landing_pads(&self, body: &mut Body<'_>) {
debug!("body: {:#?}", body);
- // make sure there's a resume block
+ // Skip the pass if there are no blocks with a resume terminator.
+ let has_resume = body
+ .basic_blocks
+ .iter_enumerated()
+ .any(|(_bb, block)| matches!(block.terminator().kind, TerminatorKind::UnwindResume));
+ if !has_resume {
+ debug!("remove_noop_landing_pads: no resume block in MIR");
+ return;
+ }
+
+ // make sure there's a resume block without any statements
let resume_block = {
let mut patch = MirPatch::new(body);
let resume_block = patch.resume_block();
diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
index 6f9edd07d736..263849747981 100644
--- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
@@ -4,7 +4,9 @@ use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, VariantDef};
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex};
-use rustc_mir_dataflow::{self, move_path_children_matching, Analysis, MoveDataParamEnv};
+use rustc_mir_dataflow::{
+ self, move_path_children_matching, Analysis, MaybeReachable, MoveDataParamEnv,
+};
use rustc_target::abi::FieldIdx;
use crate::MirPass;
@@ -41,6 +43,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUninitDrops {
let TerminatorKind::Drop { place, .. } = &terminator.kind else { continue };
maybe_inits.seek_before_primary_effect(body.terminator_loc(bb));
+ let MaybeReachable::Reachable(maybe_inits) = maybe_inits.get() else { continue };
// If there's no move path for the dropped place, it's probably a `Deref`. Let it alone.
let LookupResult::Exact(mpi) = mdpe.move_data.rev_lookup.find(place.as_ref()) else {
@@ -50,7 +53,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUninitDrops {
let should_keep = is_needs_drop_and_init(
tcx,
param_env,
- maybe_inits.get(),
+ maybe_inits,
&mdpe.move_data,
place.ty(body, tcx).ty,
mpi,
diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs
index 1d8e54cdca09..de1b80585d15 100644
--- a/compiler/rustc_mir_transform/src/separate_const_switch.rs
+++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs
@@ -108,13 +108,13 @@ pub fn separate_const_switch(body: &mut Body<'_>) -> usize {
}
// The following terminators are not allowed
- TerminatorKind::Resume
+ TerminatorKind::UnwindResume
| TerminatorKind::Drop { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::Assert { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Yield { .. }
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::InlineAsm { .. }
@@ -165,8 +165,8 @@ pub fn separate_const_switch(body: &mut Body<'_>) -> usize {
});
}
- TerminatorKind::Resume
- | TerminatorKind::Terminate
+ TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index 5e8ba4f544c9..046eed2dd193 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -71,8 +71,17 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
// of this function. Is this intentional?
if let Some(ty::Generator(gen_def_id, args, _)) = ty.map(Ty::kind) {
let body = tcx.optimized_mir(*gen_def_id).generator_drop().unwrap();
- let body = EarlyBinder::bind(body.clone()).instantiate(tcx, args);
+ let mut body = EarlyBinder::bind(body.clone()).instantiate(tcx, args);
debug!("make_shim({:?}) = {:?}", instance, body);
+
+ // Run empty passes to mark phase change and perform validation.
+ pm::run_passes(
+ tcx,
+ &mut body,
+ &[],
+ Some(MirPhase::Runtime(RuntimePhase::Optimized)),
+ );
+
return body;
}
@@ -574,7 +583,7 @@ impl<'tcx> CloneShimBuilder<'tcx> {
I: IntoIterator
- >,
{
self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false);
- let unwind = self.block(vec![], TerminatorKind::Resume, true);
+ let unwind = self.block(vec![], TerminatorKind::UnwindResume, true);
let target = self.block(vec![], TerminatorKind::Return, false);
let _final_cleanup_block = self.clone_fields(dest, src, target, unwind, tys);
@@ -588,7 +597,7 @@ impl<'tcx> CloneShimBuilder<'tcx> {
args: GeneratorArgs<'tcx>,
) {
self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false);
- let unwind = self.block(vec![], TerminatorKind::Resume, true);
+ let unwind = self.block(vec![], TerminatorKind::UnwindResume, true);
// This will get overwritten with a switch once we know the target blocks
let switch = self.block(vec![], TerminatorKind::Unreachable, false);
let unwind = self.clone_fields(dest, src, switch, unwind, args.upvar_tys());
@@ -845,7 +854,7 @@ fn build_call_shim<'tcx>(
);
// BB #4 - resume
- block(&mut blocks, vec![], TerminatorKind::Resume, true);
+ block(&mut blocks, vec![], TerminatorKind::UnwindResume, true);
}
let mut body =
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 55b14ce1c3ee..cd7908e75e20 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -776,7 +776,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
self.output.push(create_fn_mono_item(tcx, instance, source));
}
}
- mir::TerminatorKind::Terminate { .. } => {
+ mir::TerminatorKind::UnwindTerminate { .. } => {
let instance = Instance::mono(
tcx,
tcx.require_lang_item(LangItem::PanicCannotUnwind, Some(source)),
@@ -787,7 +787,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
}
mir::TerminatorKind::Goto { .. }
| mir::TerminatorKind::SwitchInt { .. }
- | mir::TerminatorKind::Resume
+ | mir::TerminatorKind::UnwindResume
| mir::TerminatorKind::Return
| mir::TerminatorKind::Unreachable => {}
mir::TerminatorKind::GeneratorDrop
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 6888127f36c0..34cc0998c9b8 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -310,8 +310,8 @@ parse_inclusive_range_no_end = inclusive range with no end
.suggestion_open_range = use `..` instead
.note = inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
-parse_incorrect_braces_trait_bounds = incorrect braces around trait bounds
- .suggestion = remove the parentheses
+parse_incorrect_parens_trait_bounds = incorrect parentheses around trait bounds
+parse_incorrect_parens_trait_bounds_sugg = fix the parentheses
parse_incorrect_semicolon =
expected item, found `;`
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 26f38c9156aa..e0b1e3678e41 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2636,21 +2636,24 @@ pub(crate) struct MissingPlusBounds {
}
#[derive(Diagnostic)]
-#[diag(parse_incorrect_braces_trait_bounds)]
-pub(crate) struct IncorrectBracesTraitBounds {
+#[diag(parse_incorrect_parens_trait_bounds)]
+pub(crate) struct IncorrectParensTraitBounds {
#[primary_span]
pub span: Vec,
#[subdiagnostic]
- pub sugg: IncorrectBracesTraitBoundsSugg,
+ pub sugg: IncorrectParensTraitBoundsSugg,
}
#[derive(Subdiagnostic)]
-#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
-pub(crate) struct IncorrectBracesTraitBoundsSugg {
+#[multipart_suggestion(
+ parse_incorrect_parens_trait_bounds_sugg,
+ applicability = "machine-applicable"
+)]
+pub(crate) struct IncorrectParensTraitBoundsSugg {
#[suggestion_part(code = " ")]
- pub l: Span,
- #[suggestion_part(code = "")]
- pub r: Span,
+ pub wrong_span: Span,
+ #[suggestion_part(code = "(")]
+ pub new_span: Span,
}
#[derive(Diagnostic)]
diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
index 446472d12945..b659c40b2336 100644
--- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
+++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
@@ -80,20 +80,14 @@ pub(crate) fn emit_unescape_error(
let sugg = sugg.unwrap_or_else(|| {
let prefix = mode.prefix_noraw();
let mut escaped = String::with_capacity(lit.len());
- let mut chrs = lit.chars().peekable();
- while let Some(first) = chrs.next() {
- match (first, chrs.peek()) {
- ('\\', Some('"')) => {
- escaped.push('\\');
- escaped.push('"');
- chrs.next();
- }
- ('"', _) => {
- escaped.push('\\');
- escaped.push('"')
- }
- (c, _) => escaped.push(c),
- };
+ let mut in_escape = false;
+ for c in lit.chars() {
+ match c {
+ '\\' => in_escape = !in_escape,
+ '"' if !in_escape => escaped.push('\\'),
+ _ => in_escape = false,
+ }
+ escaped.push(c);
}
let sugg = format!("{prefix}\"{escaped}\"");
MoreThanOneCharSugg::Quotes {
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index e409c7c67817..9ae3ef6172c7 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -22,6 +22,7 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty
use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind};
use rustc_ast_pretty::pprust;
+use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{
AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
PResult, StashKey,
@@ -193,13 +194,7 @@ impl<'a> Parser<'a> {
self.expected_tokens.push(TokenType::Operator);
while let Some(op) = self.check_assoc_op() {
- // Adjust the span for interpolated LHS to point to the `$lhs` token
- // and not to what it refers to.
- let lhs_span = match self.prev_token.kind {
- TokenKind::Interpolated(..) => self.prev_token.span,
- _ => lhs.span,
- };
-
+ let lhs_span = self.interpolated_or_expr_span(&lhs);
let cur_op_span = self.token.span;
let restrictions = if op.node.is_assign_like() {
self.restrictions & Restrictions::NO_STRUCT_LITERAL
@@ -626,8 +621,8 @@ impl<'a> Parser<'a> {
fn parse_expr_prefix_common(&mut self, lo: Span) -> PResult<'a, (Span, P)> {
self.bump();
- let expr = self.parse_expr_prefix(None);
- let (span, expr) = self.interpolated_or_expr_span(expr)?;
+ let expr = self.parse_expr_prefix(None)?;
+ let span = self.interpolated_or_expr_span(&expr);
Ok((lo.to(span), expr))
}
@@ -702,20 +697,12 @@ impl<'a> Parser<'a> {
self.parse_expr_unary(lo, UnOp::Not)
}
- /// Returns the span of expr, if it was not interpolated or the span of the interpolated token.
- fn interpolated_or_expr_span(
- &self,
- expr: PResult<'a, P