mirror of https://github.com/rust-lang/rust.git
Auto merge of #99014 - Dylan-DPC:rollup-n84y0jk, r=Dylan-DPC
Rollup of 8 pull requests Successful merges: - #96856 (Fix ProjectionElem validation) - #97711 (Improve soundness of rustc_arena) - #98507 (Finishing touches for `#[expect]` (RFC 2383)) - #98692 (rustdoc: Cleanup more FIXMEs) - #98901 (incr: cache dwarf objects in work products) - #98930 (Make MIR basic blocks field public) - #98973 (Remove (unused) inherent impl anchors) - #98981 ( Edit `rustc_mir_dataflow::framework` documentation ) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
3e51277fe6
|
@ -19,6 +19,7 @@
|
|||
#![feature(rustc_attrs)]
|
||||
#![cfg_attr(test, feature(test))]
|
||||
#![feature(strict_provenance)]
|
||||
#![feature(ptr_const_cast)]
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
@ -27,7 +28,7 @@ use std::cell::{Cell, RefCell};
|
|||
use std::cmp;
|
||||
use std::marker::{PhantomData, Send};
|
||||
use std::mem::{self, MaybeUninit};
|
||||
use std::ptr;
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::slice;
|
||||
|
||||
#[inline(never)]
|
||||
|
@ -55,15 +56,24 @@ pub struct TypedArena<T> {
|
|||
|
||||
struct ArenaChunk<T = u8> {
|
||||
/// The raw storage for the arena chunk.
|
||||
storage: Box<[MaybeUninit<T>]>,
|
||||
storage: NonNull<[MaybeUninit<T>]>,
|
||||
/// The number of valid entries in the chunk.
|
||||
entries: usize,
|
||||
}
|
||||
|
||||
unsafe impl<#[may_dangle] T> Drop for ArenaChunk<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { Box::from_raw(self.storage.as_mut()) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ArenaChunk<T> {
|
||||
#[inline]
|
||||
unsafe fn new(capacity: usize) -> ArenaChunk<T> {
|
||||
ArenaChunk { storage: Box::new_uninit_slice(capacity), entries: 0 }
|
||||
ArenaChunk {
|
||||
storage: NonNull::new(Box::into_raw(Box::new_uninit_slice(capacity))).unwrap(),
|
||||
entries: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroys this arena chunk.
|
||||
|
@ -72,14 +82,15 @@ impl<T> ArenaChunk<T> {
|
|||
// The branch on needs_drop() is an -O1 performance optimization.
|
||||
// Without the branch, dropping TypedArena<u8> takes linear time.
|
||||
if mem::needs_drop::<T>() {
|
||||
ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut self.storage[..len]));
|
||||
let slice = &mut *(self.storage.as_mut());
|
||||
ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut slice[..len]));
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a pointer to the first allocated object.
|
||||
#[inline]
|
||||
fn start(&mut self) -> *mut T {
|
||||
MaybeUninit::slice_as_mut_ptr(&mut self.storage)
|
||||
self.storage.as_ptr() as *mut T
|
||||
}
|
||||
|
||||
// Returns a pointer to the end of the allocated space.
|
||||
|
@ -90,7 +101,7 @@ impl<T> ArenaChunk<T> {
|
|||
// A pointer as large as possible for zero-sized elements.
|
||||
ptr::invalid_mut(!0)
|
||||
} else {
|
||||
self.start().add(self.storage.len())
|
||||
self.start().add((*self.storage.as_ptr()).len())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -274,7 +285,7 @@ impl<T> TypedArena<T> {
|
|||
// If the previous chunk's len is less than HUGE_PAGE
|
||||
// bytes, then this chunk will be least double the previous
|
||||
// chunk's size.
|
||||
new_cap = last_chunk.storage.len().min(HUGE_PAGE / elem_size / 2);
|
||||
new_cap = (*last_chunk.storage.as_ptr()).len().min(HUGE_PAGE / elem_size / 2);
|
||||
new_cap *= 2;
|
||||
} else {
|
||||
new_cap = PAGE / elem_size;
|
||||
|
@ -382,7 +393,7 @@ impl DroplessArena {
|
|||
// If the previous chunk's len is less than HUGE_PAGE
|
||||
// bytes, then this chunk will be least double the previous
|
||||
// chunk's size.
|
||||
new_cap = last_chunk.storage.len().min(HUGE_PAGE / 2);
|
||||
new_cap = (*last_chunk.storage.as_ptr()).len().min(HUGE_PAGE / 2);
|
||||
new_cap *= 2;
|
||||
} else {
|
||||
new_cap = PAGE;
|
||||
|
|
|
@ -79,7 +79,11 @@ fn test_arena_alloc_nested() {
|
|||
#[test]
|
||||
pub fn test_copy() {
|
||||
let arena = TypedArena::default();
|
||||
for _ in 0..100000 {
|
||||
#[cfg(not(miri))]
|
||||
const N: usize = 100000;
|
||||
#[cfg(miri)]
|
||||
const N: usize = 1000;
|
||||
for _ in 0..N {
|
||||
arena.alloc(Point { x: 1, y: 2, z: 3 });
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +110,11 @@ struct Noncopy {
|
|||
#[test]
|
||||
pub fn test_noncopy() {
|
||||
let arena = TypedArena::default();
|
||||
for _ in 0..100000 {
|
||||
#[cfg(not(miri))]
|
||||
const N: usize = 100000;
|
||||
#[cfg(miri)]
|
||||
const N: usize = 1000;
|
||||
for _ in 0..N {
|
||||
arena.alloc(Noncopy { string: "hello world".to_string(), array: vec![1, 2, 3, 4, 5] });
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +122,11 @@ pub fn test_noncopy() {
|
|||
#[test]
|
||||
pub fn test_typed_arena_zero_sized() {
|
||||
let arena = TypedArena::default();
|
||||
for _ in 0..100000 {
|
||||
#[cfg(not(miri))]
|
||||
const N: usize = 100000;
|
||||
#[cfg(miri)]
|
||||
const N: usize = 1000;
|
||||
for _ in 0..N {
|
||||
arena.alloc(());
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +136,11 @@ pub fn test_typed_arena_clear() {
|
|||
let mut arena = TypedArena::default();
|
||||
for _ in 0..10 {
|
||||
arena.clear();
|
||||
for _ in 0..10000 {
|
||||
#[cfg(not(miri))]
|
||||
const N: usize = 10000;
|
||||
#[cfg(miri)]
|
||||
const N: usize = 100;
|
||||
for _ in 0..N {
|
||||
arena.alloc(Point { x: 1, y: 2, z: 3 });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1628,7 +1628,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
location: Location,
|
||||
) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
|
||||
if location.statement_index == 0 {
|
||||
let predecessors = body.predecessors()[location.block].to_vec();
|
||||
let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
|
||||
Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
|
||||
} else {
|
||||
Either::Right(std::iter::once(Location {
|
||||
|
|
|
@ -26,7 +26,7 @@ pub(super) fn generate_invalidates<'tcx>(
|
|||
|
||||
if let Some(all_facts) = all_facts {
|
||||
let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
|
||||
let dominators = body.dominators();
|
||||
let dominators = body.basic_blocks.dominators();
|
||||
let mut ig = InvalidationGenerator {
|
||||
all_facts,
|
||||
borrow_set,
|
||||
|
|
|
@ -334,7 +334,7 @@ fn do_mir_borrowck<'a, 'tcx>(
|
|||
};
|
||||
}
|
||||
|
||||
let dominators = body.dominators();
|
||||
let dominators = body.basic_blocks.dominators();
|
||||
|
||||
let mut mbcx = MirBorrowckCtxt {
|
||||
infcx,
|
||||
|
|
|
@ -258,7 +258,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
|
|||
|
||||
let block = self.cx.elements.to_location(block_start).block;
|
||||
self.stack.extend(
|
||||
self.cx.body.predecessors()[block]
|
||||
self.cx.body.basic_blocks.predecessors()[block]
|
||||
.iter()
|
||||
.map(|&pred_bb| self.cx.body.terminator_loc(pred_bb))
|
||||
.map(|pred_loc| self.cx.elements.point_from_location(pred_loc)),
|
||||
|
@ -354,7 +354,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
|
|||
}
|
||||
|
||||
let body = self.cx.body;
|
||||
for &pred_block in body.predecessors()[block].iter() {
|
||||
for &pred_block in body.basic_blocks.predecessors()[block].iter() {
|
||||
debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,);
|
||||
|
||||
// Check whether the variable is (at least partially)
|
||||
|
|
|
@ -66,7 +66,11 @@ fn emit_module(
|
|||
let work_product = if backend_config.disable_incr_cache {
|
||||
None
|
||||
} else {
|
||||
rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(tcx.sess, &name, &tmp_file)
|
||||
rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(
|
||||
tcx.sess,
|
||||
&name,
|
||||
&[("o", &tmp_file)],
|
||||
)
|
||||
};
|
||||
|
||||
ModuleCodegenResult(
|
||||
|
@ -82,7 +86,10 @@ fn reuse_workproduct_for_cgu(
|
|||
) -> CompiledModule {
|
||||
let work_product = cgu.previous_work_product(tcx);
|
||||
let obj_out = tcx.output_filenames(()).temp_path(OutputType::Object, Some(cgu.name().as_str()));
|
||||
let source_file = rustc_incremental::in_incr_comp_dir_sess(&tcx.sess, &work_product.saved_file);
|
||||
let source_file = rustc_incremental::in_incr_comp_dir_sess(
|
||||
&tcx.sess,
|
||||
&work_product.saved_files.get("o").expect("no saved object file in work product"),
|
||||
);
|
||||
if let Err(err) = rustc_fs_util::link_or_copy(&source_file, &obj_out) {
|
||||
tcx.sess.err(&format!(
|
||||
"unable to copy {} to {}: {}",
|
||||
|
|
|
@ -151,11 +151,23 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
|
|||
return;
|
||||
}
|
||||
|
||||
let remove_temps_from_module = |module: &CompiledModule| {
|
||||
if let Some(ref obj) = module.object {
|
||||
ensure_removed(sess.diagnostic(), obj);
|
||||
}
|
||||
};
|
||||
let maybe_remove_temps_from_module =
|
||||
|preserve_objects: bool, preserve_dwarf_objects: bool, module: &CompiledModule| {
|
||||
if !preserve_objects {
|
||||
if let Some(ref obj) = module.object {
|
||||
ensure_removed(sess.diagnostic(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
if !preserve_dwarf_objects {
|
||||
if let Some(ref dwo_obj) = module.dwarf_object {
|
||||
ensure_removed(sess.diagnostic(), dwo_obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let remove_temps_from_module =
|
||||
|module: &CompiledModule| maybe_remove_temps_from_module(false, false, module);
|
||||
|
||||
// Otherwise, always remove the metadata and allocator module temporaries.
|
||||
if let Some(ref metadata_module) = codegen_results.metadata_module {
|
||||
|
@ -177,15 +189,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
|
|||
debug!(?preserve_objects, ?preserve_dwarf_objects);
|
||||
|
||||
for module in &codegen_results.modules {
|
||||
if !preserve_objects {
|
||||
remove_temps_from_module(module);
|
||||
}
|
||||
|
||||
if !preserve_dwarf_objects {
|
||||
if let Some(ref obj) = module.dwarf_object {
|
||||
ensure_removed(sess.diagnostic(), obj);
|
||||
}
|
||||
}
|
||||
maybe_remove_temps_from_module(preserve_objects, preserve_dwarf_objects, module);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -649,6 +653,7 @@ fn link_dwarf_object<'a>(
|
|||
sess.struct_err("linking dwarf objects with thorin failed")
|
||||
.note(&format!("{:?}", e))
|
||||
.emit();
|
||||
sess.abort_if_errors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -494,12 +494,18 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir(
|
|||
let _timer = sess.timer("copy_all_cgu_workproducts_to_incr_comp_cache_dir");
|
||||
|
||||
for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) {
|
||||
if let Some(path) = &module.object {
|
||||
if let Some((id, product)) =
|
||||
copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, path)
|
||||
{
|
||||
work_products.insert(id, product);
|
||||
}
|
||||
let mut files = Vec::new();
|
||||
if let Some(object_file_path) = &module.object {
|
||||
files.push(("o", object_file_path.as_path()));
|
||||
}
|
||||
if let Some(dwarf_object_file_path) = &module.dwarf_object {
|
||||
files.push(("dwo", dwarf_object_file_path.as_path()));
|
||||
}
|
||||
|
||||
if let Some((id, product)) =
|
||||
copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, files.as_slice())
|
||||
{
|
||||
work_products.insert(id, product);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -856,29 +862,50 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
|
|||
assert!(module_config.emit_obj != EmitObj::None);
|
||||
|
||||
let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap();
|
||||
let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name));
|
||||
let source_file = in_incr_comp_dir(&incr_comp_session_dir, &module.source.saved_file);
|
||||
debug!(
|
||||
"copying pre-existing module `{}` from {:?} to {}",
|
||||
module.name,
|
||||
source_file,
|
||||
obj_out.display()
|
||||
|
||||
let load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| {
|
||||
let source_file = in_incr_comp_dir(&incr_comp_session_dir, saved_path);
|
||||
debug!(
|
||||
"copying pre-existing module `{}` from {:?} to {}",
|
||||
module.name,
|
||||
source_file,
|
||||
output_path.display()
|
||||
);
|
||||
match link_or_copy(&source_file, &output_path) {
|
||||
Ok(_) => Some(output_path),
|
||||
Err(err) => {
|
||||
let diag_handler = cgcx.create_diag_handler();
|
||||
diag_handler.err(&format!(
|
||||
"unable to copy {} to {}: {}",
|
||||
source_file.display(),
|
||||
output_path.display(),
|
||||
err
|
||||
));
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let object = load_from_incr_comp_dir(
|
||||
cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)),
|
||||
&module.source.saved_files.get("o").expect("no saved object file in work product"),
|
||||
);
|
||||
if let Err(err) = link_or_copy(&source_file, &obj_out) {
|
||||
let diag_handler = cgcx.create_diag_handler();
|
||||
diag_handler.err(&format!(
|
||||
"unable to copy {} to {}: {}",
|
||||
source_file.display(),
|
||||
obj_out.display(),
|
||||
err
|
||||
));
|
||||
}
|
||||
let dwarf_object =
|
||||
module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| {
|
||||
let dwarf_obj_out = cgcx
|
||||
.output_filenames
|
||||
.split_dwarf_path(cgcx.split_debuginfo, cgcx.split_dwarf_kind, Some(&module.name))
|
||||
.expect(
|
||||
"saved dwarf object in work product but `split_dwarf_path` returned `None`",
|
||||
);
|
||||
load_from_incr_comp_dir(dwarf_obj_out, &saved_dwarf_object_file)
|
||||
});
|
||||
|
||||
WorkItemResult::Compiled(CompiledModule {
|
||||
name: module.name,
|
||||
kind: ModuleKind::Regular,
|
||||
object: Some(obj_out),
|
||||
dwarf_object: None,
|
||||
object,
|
||||
dwarf_object,
|
||||
bytecode: None,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
fx: &FunctionCx<'a, 'tcx, Bx>,
|
||||
) -> BitSet<mir::Local> {
|
||||
let mir = fx.mir;
|
||||
let dominators = mir.dominators();
|
||||
let dominators = mir.basic_blocks.dominators();
|
||||
let locals = mir
|
||||
.local_decls
|
||||
.iter()
|
||||
|
|
|
@ -856,7 +856,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
|
|||
literal: ConstantKind::from_const(_const, tcx),
|
||||
}))
|
||||
};
|
||||
let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut();
|
||||
let blocks = self.source.basic_blocks.as_mut();
|
||||
let local_decls = &mut self.source.local_decls;
|
||||
let loc = candidate.location;
|
||||
let statement = &mut blocks[loc.block].statements[loc.statement_index];
|
||||
match statement.kind {
|
||||
|
@ -865,7 +866,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
|
|||
Rvalue::Ref(ref mut region, borrow_kind, ref mut place),
|
||||
)) => {
|
||||
// Use the underlying local for this (necessarily interior) borrow.
|
||||
let ty = local_decls.local_decls()[place.local].ty;
|
||||
let ty = local_decls[place.local].ty;
|
||||
let span = statement.source_info.span;
|
||||
|
||||
let ref_ty = tcx.mk_ref(
|
||||
|
|
|
@ -12,6 +12,7 @@ use rustc_middle::mir::{
|
|||
Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK,
|
||||
};
|
||||
use rustc_middle::ty::fold::BottomUpFolder;
|
||||
use rustc_middle::ty::subst::Subst;
|
||||
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable, TypeVisitable};
|
||||
use rustc_mir_dataflow::impls::MaybeStorageLive;
|
||||
use rustc_mir_dataflow::storage::always_live_locals;
|
||||
|
@ -275,7 +276,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
};
|
||||
|
||||
match parent_ty.ty.kind() {
|
||||
let kind = match parent_ty.ty.kind() {
|
||||
&ty::Opaque(def_id, substs) => {
|
||||
self.tcx.bound_type_of(def_id).subst(self.tcx, substs).kind()
|
||||
}
|
||||
kind => kind,
|
||||
};
|
||||
|
||||
match kind {
|
||||
ty::Tuple(fields) => {
|
||||
let Some(f_ty) = fields.get(f.as_usize()) else {
|
||||
fail_out_of_bounds(self, location);
|
||||
|
@ -299,12 +307,39 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
};
|
||||
check_equal(self, location, f_ty);
|
||||
}
|
||||
ty::Generator(_, substs, _) => {
|
||||
let substs = substs.as_generator();
|
||||
let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else {
|
||||
fail_out_of_bounds(self, location);
|
||||
return;
|
||||
&ty::Generator(def_id, substs, _) => {
|
||||
let f_ty = if let Some(var) = parent_ty.variant_index {
|
||||
let gen_body = if def_id == self.body.source.def_id() {
|
||||
self.body
|
||||
} else {
|
||||
self.tcx.optimized_mir(def_id)
|
||||
};
|
||||
|
||||
let Some(layout) = gen_body.generator_layout() else {
|
||||
self.fail(location, format!("No generator layout for {:?}", parent_ty));
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(&local) = layout.variant_fields[var].get(f) else {
|
||||
fail_out_of_bounds(self, location);
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(&f_ty) = layout.field_tys.get(local) else {
|
||||
self.fail(location, format!("Out of bounds local {:?} for {:?}", local, parent_ty));
|
||||
return;
|
||||
};
|
||||
|
||||
f_ty
|
||||
} else {
|
||||
let Some(f_ty) = substs.as_generator().prefix_tys().nth(f.index()) else {
|
||||
fail_out_of_bounds(self, location);
|
||||
return;
|
||||
};
|
||||
|
||||
f_ty
|
||||
};
|
||||
|
||||
check_equal(self, location, f_ty);
|
||||
}
|
||||
_ => {
|
||||
|
@ -328,6 +363,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
{
|
||||
self.fail(location, format!("{:?}, has deref at the wrong place", place));
|
||||
}
|
||||
|
||||
self.super_place(place, cntxt, location);
|
||||
}
|
||||
|
||||
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
|
|
|
@ -161,19 +161,13 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
|
|||
Decodable::decode(&mut work_product_decoder);
|
||||
|
||||
for swp in work_products {
|
||||
let mut all_files_exist = true;
|
||||
let path = in_incr_comp_dir_sess(sess, &swp.work_product.saved_file);
|
||||
if !path.exists() {
|
||||
all_files_exist = false;
|
||||
|
||||
if sess.opts.debugging_opts.incremental_info {
|
||||
eprintln!(
|
||||
"incremental: could not find file for work \
|
||||
product: {}",
|
||||
path.display()
|
||||
);
|
||||
let all_files_exist = swp.work_product.saved_files.iter().all(|(_, path)| {
|
||||
let exists = in_incr_comp_dir_sess(sess, path).exists();
|
||||
if !exists && sess.opts.debugging_opts.incremental_info {
|
||||
eprintln!("incremental: could not find file for work product: {path}",);
|
||||
}
|
||||
}
|
||||
exists
|
||||
});
|
||||
|
||||
if all_files_exist {
|
||||
debug!("reconcile_work_products: all files for {:?} exist", swp);
|
||||
|
|
|
@ -108,16 +108,17 @@ pub fn save_work_product_index(
|
|||
for (id, wp) in previous_work_products.iter() {
|
||||
if !new_work_products.contains_key(id) {
|
||||
work_product::delete_workproduct_files(sess, wp);
|
||||
debug_assert!(!in_incr_comp_dir_sess(sess, &wp.saved_file).exists());
|
||||
debug_assert!(
|
||||
!wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we did not delete one of the current work-products:
|
||||
debug_assert!({
|
||||
new_work_products
|
||||
.iter()
|
||||
.map(|(_, wp)| in_incr_comp_dir_sess(sess, &wp.saved_file))
|
||||
.all(|path| path.exists())
|
||||
new_work_products.iter().all(|(_, wp)| {
|
||||
wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists())
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//! [work products]: WorkProduct
|
||||
|
||||
use crate::persist::fs::*;
|
||||
use rustc_data_structures::stable_map::FxHashMap;
|
||||
use rustc_fs_util::link_or_copy;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_session::Session;
|
||||
|
@ -13,38 +14,41 @@ use std::path::Path;
|
|||
pub fn copy_cgu_workproduct_to_incr_comp_cache_dir(
|
||||
sess: &Session,
|
||||
cgu_name: &str,
|
||||
path: &Path,
|
||||
files: &[(&'static str, &Path)],
|
||||
) -> Option<(WorkProductId, WorkProduct)> {
|
||||
debug!("copy_cgu_workproduct_to_incr_comp_cache_dir({:?},{:?})", cgu_name, path);
|
||||
debug!(?cgu_name, ?files);
|
||||
sess.opts.incremental.as_ref()?;
|
||||
|
||||
let file_name = format!("{}.o", cgu_name);
|
||||
let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name);
|
||||
let saved_file = match link_or_copy(path, &path_in_incr_dir) {
|
||||
Ok(_) => file_name,
|
||||
Err(err) => {
|
||||
sess.warn(&format!(
|
||||
"error copying object file `{}` to incremental directory as `{}`: {}",
|
||||
path.display(),
|
||||
path_in_incr_dir.display(),
|
||||
err
|
||||
));
|
||||
return None;
|
||||
let mut saved_files = FxHashMap::default();
|
||||
for (ext, path) in files {
|
||||
let file_name = format!("{cgu_name}.{ext}");
|
||||
let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name);
|
||||
match link_or_copy(path, &path_in_incr_dir) {
|
||||
Ok(_) => {
|
||||
let _ = saved_files.insert(ext.to_string(), file_name);
|
||||
}
|
||||
Err(err) => {
|
||||
sess.warn(&format!(
|
||||
"error copying object file `{}` to incremental directory as `{}`: {}",
|
||||
path.display(),
|
||||
path_in_incr_dir.display(),
|
||||
err
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let work_product = WorkProduct { cgu_name: cgu_name.to_string(), saved_file };
|
||||
}
|
||||
|
||||
let work_product = WorkProduct { cgu_name: cgu_name.to_string(), saved_files };
|
||||
debug!(?work_product);
|
||||
let work_product_id = WorkProductId::from_cgu_name(cgu_name);
|
||||
Some((work_product_id, work_product))
|
||||
}
|
||||
|
||||
/// Removes files for a given work product.
|
||||
pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) {
|
||||
let path = in_incr_comp_dir_sess(sess, &work_product.saved_file);
|
||||
match std_fs::remove_file(&path) {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
for (_, path) in &work_product.saved_files {
|
||||
let path = in_incr_comp_dir_sess(sess, path);
|
||||
if let Err(err) = std_fs::remove_file(&path) {
|
||||
sess.warn(&format!(
|
||||
"file-system error deleting outdated file `{}`: {}",
|
||||
path.display(),
|
||||
|
|
|
@ -34,7 +34,7 @@ use rustc_middle::middle::stability;
|
|||
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools, Ty, TyCtxt};
|
||||
use rustc_session::lint::BuiltinLintDiagnostics;
|
||||
use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId};
|
||||
use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::lev_distance::find_best_match_for_name;
|
||||
|
@ -906,6 +906,29 @@ pub trait LintContext: Sized {
|
|||
) {
|
||||
self.lookup(lint, None as Option<Span>, decorate);
|
||||
}
|
||||
|
||||
/// This returns the lint level for the given lint at the current location.
|
||||
fn get_lint_level(&self, lint: &'static Lint) -> Level;
|
||||
|
||||
/// This function can be used to manually fulfill an expectation. This can
|
||||
/// be used for lints which contain several spans, and should be suppressed,
|
||||
/// if either location was marked with an expectation.
|
||||
///
|
||||
/// Note that this function should only be called for [`LintExpectationId`]s
|
||||
/// retrieved from the current lint pass. Buffered or manually created ids can
|
||||
/// cause ICEs.
|
||||
fn fulfill_expectation(&self, expectation: LintExpectationId) {
|
||||
// We need to make sure that submitted expectation ids are correctly fulfilled suppressed
|
||||
// and stored between compilation sessions. To not manually do these steps, we simply create
|
||||
// a dummy diagnostic and emit is as usual, which will be suppressed and stored like a normal
|
||||
// expected lint diagnostic.
|
||||
self.sess()
|
||||
.struct_expect(
|
||||
"this is a dummy diagnostic, to submit and store an expectation",
|
||||
expectation,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> EarlyContext<'a> {
|
||||
|
@ -953,6 +976,10 @@ impl LintContext for LateContext<'_> {
|
|||
None => self.tcx.struct_lint_node(lint, hir_id, decorate),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_lint_level(&self, lint: &'static Lint) -> Level {
|
||||
self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs).0
|
||||
}
|
||||
}
|
||||
|
||||
impl LintContext for EarlyContext<'_> {
|
||||
|
@ -975,6 +1002,10 @@ impl LintContext for EarlyContext<'_> {
|
|||
) {
|
||||
self.builder.struct_lint(lint, span.map(|s| s.into()), decorate)
|
||||
}
|
||||
|
||||
fn get_lint_level(&self, lint: &'static Lint) -> Level {
|
||||
self.builder.lint_level(lint).0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateContext<'tcx> {
|
||||
|
|
|
@ -520,6 +520,11 @@ declare_lint! {
|
|||
/// The `expect` attribute can be removed if this is intended behavior otherwise
|
||||
/// it should be investigated why the expected lint is no longer issued.
|
||||
///
|
||||
/// In rare cases, the expectation might be emitted at a different location than
|
||||
/// shown in the shown code snippet. In most cases, the `#[expect]` attribute
|
||||
/// works when added to the outer scope. A few lints can only be expected
|
||||
/// on a crate level.
|
||||
///
|
||||
/// Part of RFC 2383. The progress is being tracked in [#54503]
|
||||
///
|
||||
/// [#54503]: https://github.com/rust-lang/rust/issues/54503
|
||||
|
|
|
@ -232,6 +232,13 @@ impl Level {
|
|||
Level::Deny | Level::Forbid => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
|
||||
match self {
|
||||
Level::Expect(id) | Level::ForceWarn(Some(id)) => Some(*id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Specification of a single lint.
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
use crate::mir::graph_cyclic_cache::GraphIsCyclicCache;
|
||||
use crate::mir::predecessors::{PredecessorCache, Predecessors};
|
||||
use crate::mir::switch_sources::{SwitchSourceCache, SwitchSources};
|
||||
use crate::mir::traversal::PostorderCache;
|
||||
use crate::mir::{BasicBlock, BasicBlockData, Successors, START_BLOCK};
|
||||
|
||||
use rustc_data_structures::graph;
|
||||
use rustc_data_structures::graph::dominators::{dominators, Dominators};
|
||||
use rustc_index::vec::IndexVec;
|
||||
|
||||
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct BasicBlocks<'tcx> {
|
||||
basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
|
||||
predecessor_cache: PredecessorCache,
|
||||
switch_source_cache: SwitchSourceCache,
|
||||
is_cyclic: GraphIsCyclicCache,
|
||||
postorder_cache: PostorderCache,
|
||||
}
|
||||
|
||||
impl<'tcx> BasicBlocks<'tcx> {
|
||||
#[inline]
|
||||
pub fn new(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
|
||||
BasicBlocks {
|
||||
basic_blocks,
|
||||
predecessor_cache: PredecessorCache::new(),
|
||||
switch_source_cache: SwitchSourceCache::new(),
|
||||
is_cyclic: GraphIsCyclicCache::new(),
|
||||
postorder_cache: PostorderCache::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if control-flow graph contains a cycle reachable from the `START_BLOCK`.
|
||||
#[inline]
|
||||
pub fn is_cfg_cyclic(&self) -> bool {
|
||||
self.is_cyclic.is_cyclic(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn dominators(&self) -> Dominators<BasicBlock> {
|
||||
dominators(&self)
|
||||
}
|
||||
|
||||
/// Returns predecessors for each basic block.
|
||||
#[inline]
|
||||
pub fn predecessors(&self) -> &Predecessors {
|
||||
self.predecessor_cache.compute(&self.basic_blocks)
|
||||
}
|
||||
|
||||
/// Returns basic blocks in a postorder.
|
||||
#[inline]
|
||||
pub fn postorder(&self) -> &[BasicBlock] {
|
||||
self.postorder_cache.compute(&self.basic_blocks)
|
||||
}
|
||||
|
||||
/// `switch_sources()[&(target, switch)]` returns a list of switch
|
||||
/// values that lead to a `target` block from a `switch` block.
|
||||
#[inline]
|
||||
pub fn switch_sources(&self) -> &SwitchSources {
|
||||
self.switch_source_cache.compute(&self.basic_blocks)
|
||||
}
|
||||
|
||||
/// Returns mutable reference to basic blocks. Invalidates CFG cache.
|
||||
#[inline]
|
||||
pub fn as_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
|
||||
self.invalidate_cfg_cache();
|
||||
&mut self.basic_blocks
|
||||
}
|
||||
|
||||
/// Get mutable access to basic blocks without invalidating the CFG cache.
|
||||
///
|
||||
/// By calling this method instead of e.g. [`BasicBlocks::as_mut`] you promise not to change
|
||||
/// the CFG. This means that
|
||||
///
|
||||
/// 1) The number of basic blocks remains unchanged
|
||||
/// 2) The set of successors of each terminator remains unchanged.
|
||||
/// 3) For each `TerminatorKind::SwitchInt`, the `targets` remains the same and the terminator
|
||||
/// kind is not changed.
|
||||
///
|
||||
/// If any of these conditions cannot be upheld, you should call [`BasicBlocks::invalidate_cfg_cache`].
|
||||
#[inline]
|
||||
pub fn as_mut_preserves_cfg(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
|
||||
&mut self.basic_blocks
|
||||
}
|
||||
|
||||
/// Invalidates cached information about the CFG.
|
||||
///
|
||||
/// You will only ever need this if you have also called [`BasicBlocks::as_mut_preserves_cfg`].
|
||||
/// All other methods that allow you to mutate the basic blocks also call this method
|
||||
/// themselves, thereby avoiding any risk of accidentaly cache invalidation.
|
||||
pub fn invalidate_cfg_cache(&mut self) {
|
||||
self.predecessor_cache.invalidate();
|
||||
self.switch_source_cache.invalidate();
|
||||
self.is_cyclic.invalidate();
|
||||
self.postorder_cache.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> std::ops::Deref for BasicBlocks<'tcx> {
|
||||
type Target = IndexVec<BasicBlock, BasicBlockData<'tcx>>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
|
||||
&self.basic_blocks
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> graph::DirectedGraph for BasicBlocks<'tcx> {
|
||||
type Node = BasicBlock;
|
||||
}
|
||||
|
||||
impl<'tcx> graph::WithNumNodes for BasicBlocks<'tcx> {
|
||||
#[inline]
|
||||
fn num_nodes(&self) -> usize {
|
||||
self.basic_blocks.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> graph::WithStartNode for BasicBlocks<'tcx> {
|
||||
#[inline]
|
||||
fn start_node(&self) -> Self::Node {
|
||||
START_BLOCK
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> graph::WithSuccessors for BasicBlocks<'tcx> {
|
||||
#[inline]
|
||||
fn successors(&self, node: Self::Node) -> <Self as graph::GraphSuccessors<'_>>::Iter {
|
||||
self.basic_blocks[node].terminator().successors()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> graph::GraphSuccessors<'b> for BasicBlocks<'a> {
|
||||
type Item = BasicBlock;
|
||||
type Iter = Successors<'b>;
|
||||
}
|
||||
|
||||
impl<'tcx, 'graph> graph::GraphPredecessors<'graph> for BasicBlocks<'tcx> {
|
||||
type Item = BasicBlock;
|
||||
type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicBlock>>;
|
||||
}
|
||||
|
||||
impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> {
|
||||
#[inline]
|
||||
fn predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter {
|
||||
self.predecessors()[node].iter().copied()
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
use crate::mir::interpret::{
|
||||
AllocRange, ConstAllocation, ConstValue, GlobalAlloc, LitToConstInput, Scalar,
|
||||
};
|
||||
use crate::mir::traversal::PostorderCache;
|
||||
use crate::mir::visit::MirVisitable;
|
||||
use crate::ty::codec::{TyDecoder, TyEncoder};
|
||||
use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
|
||||
|
@ -27,8 +26,7 @@ use rustc_target::abi::{Size, VariantIdx};
|
|||
use polonius_engine::Atom;
|
||||
pub use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::graph::dominators::{dominators, Dominators};
|
||||
use rustc_data_structures::graph::{self, GraphSuccessors};
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_index::bit_set::BitMatrix;
|
||||
use rustc_index::vec::{Idx, IndexVec};
|
||||
use rustc_serialize::{Decodable, Encodable};
|
||||
|
@ -43,11 +41,10 @@ use std::fmt::{self, Debug, Display, Formatter, Write};
|
|||
use std::ops::{ControlFlow, Index, IndexMut};
|
||||
use std::{iter, mem};
|
||||
|
||||
use self::graph_cyclic_cache::GraphIsCyclicCache;
|
||||
use self::predecessors::{PredecessorCache, Predecessors};
|
||||
pub use self::query::*;
|
||||
use self::switch_sources::{SwitchSourceCache, SwitchSources};
|
||||
pub use basic_blocks::BasicBlocks;
|
||||
|
||||
mod basic_blocks;
|
||||
pub mod coverage;
|
||||
mod generic_graph;
|
||||
pub mod generic_graphviz;
|
||||
|
@ -189,7 +186,7 @@ pub struct GeneratorInfo<'tcx> {
|
|||
pub struct Body<'tcx> {
|
||||
/// A list of basic blocks. References to basic block use a newtyped index type [`BasicBlock`]
|
||||
/// that indexes into this vector.
|
||||
basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
|
||||
pub basic_blocks: BasicBlocks<'tcx>,
|
||||
|
||||
/// Records how far through the "desugaring and optimization" process this particular
|
||||
/// MIR has traversed. This is particularly useful when inlining, since in that context
|
||||
|
@ -257,11 +254,6 @@ pub struct Body<'tcx> {
|
|||
/// potentially allow things like `[u8; std::mem::size_of::<T>() * 0]` due to this.
|
||||
pub is_polymorphic: bool,
|
||||
|
||||
predecessor_cache: PredecessorCache,
|
||||
switch_source_cache: SwitchSourceCache,
|
||||
is_cyclic: GraphIsCyclicCache,
|
||||
postorder_cache: PostorderCache,
|
||||
|
||||
pub tainted_by_errors: Option<ErrorGuaranteed>,
|
||||
}
|
||||
|
||||
|
@ -289,7 +281,7 @@ impl<'tcx> Body<'tcx> {
|
|||
let mut body = Body {
|
||||
phase: MirPhase::Built,
|
||||
source,
|
||||
basic_blocks,
|
||||
basic_blocks: BasicBlocks::new(basic_blocks),
|
||||
source_scopes,
|
||||
generator: generator_kind.map(|generator_kind| {
|
||||
Box::new(GeneratorInfo {
|
||||
|
@ -307,10 +299,6 @@ impl<'tcx> Body<'tcx> {
|
|||
span,
|
||||
required_consts: Vec::new(),
|
||||
is_polymorphic: false,
|
||||
predecessor_cache: PredecessorCache::new(),
|
||||
switch_source_cache: SwitchSourceCache::new(),
|
||||
is_cyclic: GraphIsCyclicCache::new(),
|
||||
postorder_cache: PostorderCache::new(),
|
||||
tainted_by_errors,
|
||||
};
|
||||
body.is_polymorphic = body.has_param_types_or_consts();
|
||||
|
@ -326,7 +314,7 @@ impl<'tcx> Body<'tcx> {
|
|||
let mut body = Body {
|
||||
phase: MirPhase::Built,
|
||||
source: MirSource::item(CRATE_DEF_ID.to_def_id()),
|
||||
basic_blocks,
|
||||
basic_blocks: BasicBlocks::new(basic_blocks),
|
||||
source_scopes: IndexVec::new(),
|
||||
generator: None,
|
||||
local_decls: IndexVec::new(),
|
||||
|
@ -337,10 +325,6 @@ impl<'tcx> Body<'tcx> {
|
|||
required_consts: Vec::new(),
|
||||
var_debug_info: Vec::new(),
|
||||
is_polymorphic: false,
|
||||
predecessor_cache: PredecessorCache::new(),
|
||||
switch_source_cache: SwitchSourceCache::new(),
|
||||
is_cyclic: GraphIsCyclicCache::new(),
|
||||
postorder_cache: PostorderCache::new(),
|
||||
tainted_by_errors: None,
|
||||
};
|
||||
body.is_polymorphic = body.has_param_types_or_consts();
|
||||
|
@ -354,74 +338,7 @@ impl<'tcx> Body<'tcx> {
|
|||
|
||||
#[inline]
|
||||
pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
|
||||
// Because the user could mutate basic block terminators via this reference, we need to
|
||||
// invalidate the caches.
|
||||
//
|
||||
// FIXME: Use a finer-grained API for this, so only transformations that alter terminators
|
||||
// invalidate the caches.
|
||||
self.invalidate_cfg_cache();
|
||||
&mut self.basic_blocks
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn basic_blocks_and_local_decls_mut(
|
||||
&mut self,
|
||||
) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
|
||||
self.invalidate_cfg_cache();
|
||||
(&mut self.basic_blocks, &mut self.local_decls)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn basic_blocks_local_decls_mut_and_var_debug_info(
|
||||
&mut self,
|
||||
) -> (
|
||||
&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>,
|
||||
&mut LocalDecls<'tcx>,
|
||||
&mut Vec<VarDebugInfo<'tcx>>,
|
||||
) {
|
||||
self.invalidate_cfg_cache();
|
||||
(&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
|
||||
}
|
||||
|
||||
/// Get mutable access to parts of the Body without invalidating the CFG cache.
|
||||
///
|
||||
/// By calling this method instead of eg [`Body::basic_blocks_mut`], you promise not to change
|
||||
/// the CFG. This means that
|
||||
///
|
||||
/// 1) The number of basic blocks remains unchanged
|
||||
/// 2) The set of successors of each terminator remains unchanged.
|
||||
/// 3) For each `TerminatorKind::SwitchInt`, the `targets` remains the same and the terminator
|
||||
/// kind is not changed.
|
||||
///
|
||||
/// If any of these conditions cannot be upheld, you should call [`Body::invalidate_cfg_cache`].
|
||||
#[inline]
|
||||
pub fn basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate(
|
||||
&mut self,
|
||||
) -> (
|
||||
&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>,
|
||||
&mut LocalDecls<'tcx>,
|
||||
&mut Vec<VarDebugInfo<'tcx>>,
|
||||
) {
|
||||
(&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
|
||||
}
|
||||
|
||||
/// Invalidates cached information about the CFG.
|
||||
///
|
||||
/// You will only ever need this if you have also called
|
||||
/// [`Body::basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate`]. All other methods
|
||||
/// that allow you to mutate the body also call this method themselves, thereby avoiding any
|
||||
/// risk of accidentaly cache invalidation.
|
||||
pub fn invalidate_cfg_cache(&mut self) {
|
||||
self.predecessor_cache.invalidate();
|
||||
self.switch_source_cache.invalidate();
|
||||
self.is_cyclic.invalidate();
|
||||
self.postorder_cache.invalidate();
|
||||
}
|
||||
|
||||
/// Returns `true` if a cycle exists in the control-flow graph that is reachable from the
|
||||
/// `START_BLOCK`.
|
||||
pub fn is_cfg_cyclic(&self) -> bool {
|
||||
self.is_cyclic.is_cyclic(self)
|
||||
self.basic_blocks.as_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -495,14 +412,6 @@ impl<'tcx> Body<'tcx> {
|
|||
self.local_decls.drain(self.arg_count + 1..)
|
||||
}
|
||||
|
||||
/// Changes a statement to a nop. This is both faster than deleting instructions and avoids
|
||||
/// invalidating statement indices in `Location`s.
|
||||
pub fn make_statement_nop(&mut self, location: Location) {
|
||||
let block = &mut self.basic_blocks[location.block];
|
||||
debug_assert!(location.statement_index < block.statements.len());
|
||||
block.statements[location.statement_index].make_nop()
|
||||
}
|
||||
|
||||
/// Returns the source info associated with `location`.
|
||||
pub fn source_info(&self, location: Location) -> &SourceInfo {
|
||||
let block = &self[location.block];
|
||||
|
@ -538,23 +447,6 @@ impl<'tcx> Body<'tcx> {
|
|||
.unwrap_or_else(|| Either::Right(block_data.terminator()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn predecessors(&self) -> &Predecessors {
|
||||
self.predecessor_cache.compute(&self.basic_blocks)
|
||||
}
|
||||
|
||||
/// `body.switch_sources()[&(target, switch)]` returns a list of switch
|
||||
/// values that lead to a `target` block from a `switch` block.
|
||||
#[inline]
|
||||
pub fn switch_sources(&self) -> &SwitchSources {
|
||||
self.switch_source_cache.compute(&self.basic_blocks)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn dominators(&self) -> Dominators<BasicBlock> {
|
||||
dominators(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn yield_ty(&self) -> Option<Ty<'tcx>> {
|
||||
self.generator.as_ref().and_then(|generator| generator.yield_ty)
|
||||
|
@ -599,7 +491,7 @@ impl<'tcx> Index<BasicBlock> for Body<'tcx> {
|
|||
impl<'tcx> IndexMut<BasicBlock> for Body<'tcx> {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> {
|
||||
&mut self.basic_blocks_mut()[index]
|
||||
&mut self.basic_blocks.as_mut()[index]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2890,48 +2782,6 @@ fn pretty_print_const_value<'tcx>(
|
|||
})
|
||||
}
|
||||
|
||||
impl<'tcx> graph::DirectedGraph for Body<'tcx> {
|
||||
type Node = BasicBlock;
|
||||
}
|
||||
|
||||
impl<'tcx> graph::WithNumNodes for Body<'tcx> {
|
||||
#[inline]
|
||||
fn num_nodes(&self) -> usize {
|
||||
self.basic_blocks.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> graph::WithStartNode for Body<'tcx> {
|
||||
#[inline]
|
||||
fn start_node(&self) -> Self::Node {
|
||||
START_BLOCK
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> graph::WithSuccessors for Body<'tcx> {
|
||||
#[inline]
|
||||
fn successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter {
|
||||
self.basic_blocks[node].terminator().successors()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> {
|
||||
type Item = BasicBlock;
|
||||
type Iter = Successors<'b>;
|
||||
}
|
||||
|
||||
impl<'tcx, 'graph> graph::GraphPredecessors<'graph> for Body<'tcx> {
|
||||
type Item = BasicBlock;
|
||||
type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicBlock>>;
|
||||
}
|
||||
|
||||
impl<'tcx> graph::WithPredecessors for Body<'tcx> {
|
||||
#[inline]
|
||||
fn predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter {
|
||||
self.predecessors()[node].iter().copied()
|
||||
}
|
||||
}
|
||||
|
||||
/// `Location` represents the position of the start of the statement; or, if
|
||||
/// `statement_index` equals the number of statements, then the start of the
|
||||
/// terminator.
|
||||
|
@ -2968,7 +2818,7 @@ impl Location {
|
|||
return true;
|
||||
}
|
||||
|
||||
let predecessors = body.predecessors();
|
||||
let predecessors = body.basic_blocks.predecessors();
|
||||
|
||||
// If we're in another block, then we want to check that block is a predecessor of `other`.
|
||||
let mut queue: Vec<BasicBlock> = predecessors[other.block].to_vec();
|
||||
|
|
|
@ -104,22 +104,25 @@ impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> {
|
|||
///
|
||||
/// A Postorder traversal of this graph is `D B C A` or `D C B A`
|
||||
pub struct Postorder<'a, 'tcx> {
|
||||
body: &'a Body<'tcx>,
|
||||
basic_blocks: &'a IndexVec<BasicBlock, BasicBlockData<'tcx>>,
|
||||
visited: BitSet<BasicBlock>,
|
||||
visit_stack: Vec<(BasicBlock, Successors<'a>)>,
|
||||
root_is_start_block: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Postorder<'a, 'tcx> {
|
||||
pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> Postorder<'a, 'tcx> {
|
||||
pub fn new(
|
||||
basic_blocks: &'a IndexVec<BasicBlock, BasicBlockData<'tcx>>,
|
||||
root: BasicBlock,
|
||||
) -> Postorder<'a, 'tcx> {
|
||||
let mut po = Postorder {
|
||||
body,
|
||||
visited: BitSet::new_empty(body.basic_blocks().len()),
|
||||
basic_blocks,
|
||||
visited: BitSet::new_empty(basic_blocks.len()),
|
||||
visit_stack: Vec::new(),
|
||||
root_is_start_block: root == START_BLOCK,
|
||||
};
|
||||
|
||||
let data = &po.body[root];
|
||||
let data = &po.basic_blocks[root];
|
||||
|
||||
if let Some(ref term) = data.terminator {
|
||||
po.visited.insert(root);
|
||||
|
@ -190,7 +193,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
|
|||
};
|
||||
|
||||
if self.visited.insert(bb) {
|
||||
if let Some(term) = &self.body[bb].terminator {
|
||||
if let Some(term) = &self.basic_blocks[bb].terminator {
|
||||
self.visit_stack.push((bb, term.successors()));
|
||||
}
|
||||
}
|
||||
|
@ -199,7 +202,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
pub fn postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> Postorder<'a, 'tcx> {
|
||||
Postorder::new(body, START_BLOCK)
|
||||
Postorder::new(&body.basic_blocks, START_BLOCK)
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
|
||||
|
@ -211,12 +214,12 @@ impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
|
|||
self.traverse_successor();
|
||||
}
|
||||
|
||||
next.map(|(bb, _)| (bb, &self.body[bb]))
|
||||
next.map(|(bb, _)| (bb, &self.basic_blocks[bb]))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
// All the blocks, minus the number of blocks we've visited.
|
||||
let upper = self.body.basic_blocks().len() - self.visited.count();
|
||||
let upper = self.basic_blocks.len() - self.visited.count();
|
||||
|
||||
let lower = if self.root_is_start_block {
|
||||
// We will visit all remaining blocks exactly once.
|
||||
|
@ -263,10 +266,8 @@ pub struct ReversePostorder<'a, 'tcx> {
|
|||
|
||||
impl<'a, 'tcx> ReversePostorder<'a, 'tcx> {
|
||||
pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> ReversePostorder<'a, 'tcx> {
|
||||
let blocks: Vec<_> = Postorder::new(body, root).map(|(bb, _)| bb).collect();
|
||||
|
||||
let blocks: Vec<_> = Postorder::new(&body.basic_blocks, root).map(|(bb, _)| bb).collect();
|
||||
let len = blocks.len();
|
||||
|
||||
ReversePostorder { body, blocks, idx: len }
|
||||
}
|
||||
}
|
||||
|
@ -334,10 +335,8 @@ impl<'a, 'tcx> Iterator for ReversePostorderIter<'a, 'tcx> {
|
|||
impl<'a, 'tcx> ExactSizeIterator for ReversePostorderIter<'a, 'tcx> {}
|
||||
|
||||
pub fn reverse_postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> ReversePostorderIter<'a, 'tcx> {
|
||||
let blocks = body.postorder_cache.compute(body);
|
||||
|
||||
let blocks = body.basic_blocks.postorder();
|
||||
let len = blocks.len();
|
||||
|
||||
ReversePostorderIter { body, blocks, idx: len }
|
||||
}
|
||||
|
||||
|
@ -360,7 +359,7 @@ impl PostorderCache {
|
|||
|
||||
/// Returns the `&[BasicBlocks]` represents the postorder graph for this MIR.
|
||||
#[inline]
|
||||
pub(super) fn compute(&self, body: &Body<'_>) -> &[BasicBlock] {
|
||||
pub(super) fn compute(&self, body: &IndexVec<BasicBlock, BasicBlockData<'_>>) -> &[BasicBlock] {
|
||||
self.cache.get_or_init(|| Postorder::new(body, START_BLOCK).map(|(bb, _)| bb).collect())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use rustc_data_structures::graph::iterate::{
|
|||
NodeStatus, TriColorDepthFirstSearch, TriColorVisitor,
|
||||
};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_middle::mir::{BasicBlock, Body, Operand, TerminatorKind};
|
||||
use rustc_middle::mir::{BasicBlock, BasicBlocks, Body, Operand, TerminatorKind};
|
||||
use rustc_middle::ty::subst::{GenericArg, InternalSubsts};
|
||||
use rustc_middle::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt};
|
||||
use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION;
|
||||
|
@ -30,7 +30,9 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
|
|||
};
|
||||
|
||||
let mut vis = Search { tcx, body, reachable_recursive_calls: vec![], trait_substs };
|
||||
if let Some(NonRecursive) = TriColorDepthFirstSearch::new(&body).run_from_start(&mut vis) {
|
||||
if let Some(NonRecursive) =
|
||||
TriColorDepthFirstSearch::new(&body.basic_blocks).run_from_start(&mut vis)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if vis.reachable_recursive_calls.is_empty() {
|
||||
|
@ -101,7 +103,7 @@ impl<'mir, 'tcx> Search<'mir, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> {
|
||||
impl<'mir, 'tcx> TriColorVisitor<BasicBlocks<'tcx>> for Search<'mir, 'tcx> {
|
||||
type BreakVal = NonRecursive;
|
||||
|
||||
fn node_examined(
|
||||
|
|
|
@ -228,7 +228,7 @@ impl Direction for Backward {
|
|||
) where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
for pred in body.predecessors()[bb].iter().copied() {
|
||||
for pred in body.basic_blocks.predecessors()[bb].iter().copied() {
|
||||
match body[pred].terminator().kind {
|
||||
// Apply terminator-specific edge effects.
|
||||
//
|
||||
|
@ -316,7 +316,7 @@ where
|
|||
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
|
||||
assert!(!self.effects_applied);
|
||||
|
||||
let values = &self.body.switch_sources()[&(self.bb, self.pred)];
|
||||
let values = &self.body.basic_blocks.switch_sources()[&(self.bb, self.pred)];
|
||||
let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.bb });
|
||||
|
||||
let mut tmp = None;
|
||||
|
|
|
@ -101,7 +101,7 @@ where
|
|||
// transfer function for each block exactly once (assuming that we process blocks in RPO).
|
||||
//
|
||||
// In this case, there's no need to compute the block transfer functions ahead of time.
|
||||
if !body.is_cfg_cyclic() {
|
||||
if !body.basic_blocks.is_cfg_cyclic() {
|
||||
return Self::new(tcx, body, analysis, None);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! A framework that can express both [gen-kill] and generic dataflow problems.
|
||||
//!
|
||||
//! To actually use this framework, you must implement either the `Analysis` or the
|
||||
//! `GenKillAnalysis` trait. If your transfer function can be expressed with only gen/kill
|
||||
//! To use this framework, implement either the [`Analysis`] or the
|
||||
//! [`GenKillAnalysis`] trait. If your transfer function can be expressed with only gen/kill
|
||||
//! operations, prefer `GenKillAnalysis` since it will run faster while iterating to fixpoint. The
|
||||
//! `impls` module contains several examples of gen/kill dataflow analyses.
|
||||
//!
|
||||
|
@ -96,7 +96,7 @@ impl<T: Idx> BitSetExt<T> for ChunkedBitSet<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Define the domain of a dataflow problem.
|
||||
/// Defines the domain of a dataflow problem.
|
||||
///
|
||||
/// This trait specifies the lattice on which this analysis operates (the domain) as well as its
|
||||
/// initial value at the entry point of each basic block.
|
||||
|
@ -113,12 +113,12 @@ pub trait AnalysisDomain<'tcx> {
|
|||
/// suitable as part of a filename.
|
||||
const NAME: &'static str;
|
||||
|
||||
/// The initial value of the dataflow state upon entry to each basic block.
|
||||
/// Returns the initial value of the dataflow state upon entry to each basic block.
|
||||
fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain;
|
||||
|
||||
/// Mutates the initial value of the dataflow state upon entry to the `START_BLOCK`.
|
||||
///
|
||||
/// For backward analyses, initial state besides the bottom value is not yet supported. Trying
|
||||
/// For backward analyses, initial state (besides the bottom value) is not yet supported. Trying
|
||||
/// to mutate the initial state will result in a panic.
|
||||
//
|
||||
// FIXME: For backward dataflow analyses, the initial state should be applied to every basic
|
||||
|
@ -155,9 +155,9 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
|
|||
/// Updates the current dataflow state with an effect that occurs immediately *before* the
|
||||
/// given statement.
|
||||
///
|
||||
/// This method is useful if the consumer of the results of this analysis needs only to observe
|
||||
/// This method is useful if the consumer of the results of this analysis only needs to observe
|
||||
/// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule,
|
||||
/// analyses should not implement this without implementing `apply_statement_effect`.
|
||||
/// analyses should not implement this without also implementing `apply_statement_effect`.
|
||||
fn apply_before_statement_effect(
|
||||
&self,
|
||||
_state: &mut Self::Domain,
|
||||
|
@ -184,7 +184,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
|
|||
///
|
||||
/// This method is useful if the consumer of the results of this analysis needs only to observe
|
||||
/// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule,
|
||||
/// analyses should not implement this without implementing `apply_terminator_effect`.
|
||||
/// analyses should not implement this without also implementing `apply_terminator_effect`.
|
||||
fn apply_before_terminator_effect(
|
||||
&self,
|
||||
_state: &mut Self::Domain,
|
||||
|
|
|
@ -39,7 +39,7 @@ impl<'tcx> MirPass<'tcx> for AddCallGuards {
|
|||
impl AddCallGuards {
|
||||
pub fn add_call_guards(&self, body: &mut Body<'_>) {
|
||||
let mut pred_count: IndexVec<_, _> =
|
||||
body.predecessors().iter().map(|ps| ps.len()).collect();
|
||||
body.basic_blocks.predecessors().iter().map(|ps| ps.len()).collect();
|
||||
pred_count[START_BLOCK] += 1;
|
||||
|
||||
// We need a place to store the new blocks generated
|
||||
|
|
|
@ -91,7 +91,8 @@ impl<'tcx> MirPass<'tcx> for AddRetag {
|
|||
super::add_call_guards::AllCallEdges.run_pass(tcx, body);
|
||||
|
||||
let (span, arg_count) = (body.span, body.arg_count);
|
||||
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
|
||||
let basic_blocks = body.basic_blocks.as_mut();
|
||||
let local_decls = &body.local_decls;
|
||||
let needs_retag = |place: &Place<'tcx>| {
|
||||
// FIXME: Instead of giving up for unstable places, we should introduce
|
||||
// a temporary and retag on that.
|
||||
|
|
|
@ -80,7 +80,7 @@ impl CoverageGraph {
|
|||
IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
|
||||
IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
|
||||
) {
|
||||
let num_basic_blocks = mir_body.num_nodes();
|
||||
let num_basic_blocks = mir_body.basic_blocks.len();
|
||||
let mut bcbs = IndexVec::with_capacity(num_basic_blocks);
|
||||
let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks);
|
||||
|
||||
|
@ -95,7 +95,7 @@ impl CoverageGraph {
|
|||
let mut basic_blocks = Vec::new();
|
||||
for (bb, data) in mir_cfg_without_unwind {
|
||||
if let Some(last) = basic_blocks.last() {
|
||||
let predecessors = &mir_body.predecessors()[bb];
|
||||
let predecessors = &mir_body.basic_blocks.predecessors()[bb];
|
||||
if predecessors.len() > 1 || !predecessors.contains(last) {
|
||||
// The `bb` has more than one _incoming_ edge, and should start its own
|
||||
// `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet
|
||||
|
|
|
@ -321,7 +321,8 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||
}
|
||||
|
||||
fn mir_to_initial_sorted_coverage_spans(&self) -> Vec<CoverageSpan> {
|
||||
let mut initial_spans = Vec::<CoverageSpan>::with_capacity(self.mir_body.num_nodes() * 2);
|
||||
let mut initial_spans =
|
||||
Vec::<CoverageSpan>::with_capacity(self.mir_body.basic_blocks.len() * 2);
|
||||
for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() {
|
||||
initial_spans.extend(self.bcb_to_initial_coverage_spans(bcb, bcb_data));
|
||||
}
|
||||
|
|
|
@ -222,6 +222,7 @@ fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) {
|
|||
bb,
|
||||
debug::term_type(&data.terminator().kind),
|
||||
mir_body
|
||||
.basic_blocks
|
||||
.successors(bb)
|
||||
.map(|successor| { format!(" {:?} -> {:?};", bb, successor) })
|
||||
.join("\n")
|
||||
|
|
|
@ -66,7 +66,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS
|
|||
return;
|
||||
}
|
||||
|
||||
let bbs = body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate().0;
|
||||
let bbs = body.basic_blocks.as_mut_preserves_cfg();
|
||||
for Location { block, statement_index } in patch {
|
||||
bbs[block].statements[statement_index].make_nop();
|
||||
}
|
||||
|
|
|
@ -11,9 +11,7 @@ impl<'tcx> MirPass<'tcx> for Deaggregator {
|
|||
}
|
||||
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
let (basic_blocks, local_decls, _) =
|
||||
body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();
|
||||
let local_decls = &*local_decls;
|
||||
let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
|
||||
for bb in basic_blocks {
|
||||
bb.expand_statements(|stmt| {
|
||||
// FIXME(eddyb) don't match twice on `stmt.kind` (post-NLL).
|
||||
|
@ -38,7 +36,7 @@ impl<'tcx> MirPass<'tcx> for Deaggregator {
|
|||
Some(expand_aggregate(
|
||||
lhs,
|
||||
operands.into_iter().map(|op| {
|
||||
let ty = op.ty(local_decls, tcx);
|
||||
let ty = op.ty(&body.local_decls, tcx);
|
||||
(op, ty)
|
||||
}),
|
||||
*kind,
|
||||
|
|
|
@ -110,13 +110,13 @@ impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs {
|
|||
|
||||
let patch = MirPatch::new(body);
|
||||
|
||||
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
|
||||
let local_decls = &mut body.local_decls;
|
||||
|
||||
let mut visitor =
|
||||
ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch };
|
||||
|
||||
for (block, BasicBlockData { statements, terminator, .. }) in
|
||||
basic_blocks.iter_enumerated_mut()
|
||||
body.basic_blocks.as_mut().iter_enumerated_mut()
|
||||
{
|
||||
let mut index = 0;
|
||||
for statement in statements {
|
||||
|
|
|
@ -16,9 +16,8 @@ impl<'tcx> MirPass<'tcx> for InstCombine {
|
|||
}
|
||||
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
|
||||
let ctx = InstCombineContext { tcx, local_decls };
|
||||
for block in basic_blocks.iter_mut() {
|
||||
let ctx = InstCombineContext { tcx, local_decls: &body.local_decls };
|
||||
for block in body.basic_blocks.as_mut() {
|
||||
for statement in block.statements.iter_mut() {
|
||||
match statement.kind {
|
||||
StatementKind::Assign(box (_place, ref mut rvalue)) => {
|
||||
|
|
|
@ -11,8 +11,8 @@ pub struct LowerIntrinsics;
|
|||
|
||||
impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
|
||||
for block in basic_blocks {
|
||||
let local_decls = &body.local_decls;
|
||||
for block in body.basic_blocks.as_mut() {
|
||||
let terminator = block.terminator.as_mut().unwrap();
|
||||
if let TerminatorKind::Call { func, args, destination, target, .. } =
|
||||
&mut terminator.kind
|
||||
|
|
|
@ -27,12 +27,10 @@ pub fn lower_slice_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
|||
};
|
||||
|
||||
// The one successor remains unchanged, so no need to invalidate
|
||||
let (basic_blocks, local_decls, _) =
|
||||
body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();
|
||||
|
||||
let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
|
||||
for block in basic_blocks {
|
||||
// lower `<[_]>::len` calls
|
||||
lower_slice_len_call(tcx, block, &*local_decls, slice_len_fn_item_def_id);
|
||||
lower_slice_len_call(tcx, block, &body.local_decls, slice_len_fn_item_def_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
|||
let def_id = body.source.def_id();
|
||||
let param_env = tcx.param_env(def_id);
|
||||
|
||||
let (bbs, local_decls) = body.basic_blocks_and_local_decls_mut();
|
||||
let bbs = body.basic_blocks.as_mut();
|
||||
let mut should_cleanup = false;
|
||||
'outer: for bb_idx in bbs.indices() {
|
||||
if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {:?} ", def_id)) {
|
||||
|
@ -108,7 +108,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
|||
|
||||
// Introduce a temporary for the discriminant value.
|
||||
let source_info = bbs[bb_idx].terminator().source_info;
|
||||
let discr_local = local_decls.push(LocalDecl::new(switch_ty, source_info.span));
|
||||
let discr_local = body.local_decls.push(LocalDecl::new(switch_ty, source_info.span));
|
||||
|
||||
// We already checked that first and second are different blocks,
|
||||
// and bb_idx has a different terminator from both of them.
|
||||
|
|
|
@ -33,8 +33,8 @@ impl<'tcx> MirPass<'tcx> for NormalizeArrayLen {
|
|||
|
||||
pub fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
// We don't ever touch terminators, so no need to invalidate the CFG cache
|
||||
let (basic_blocks, local_decls, _) =
|
||||
body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();
|
||||
let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
|
||||
let local_decls = &mut body.local_decls;
|
||||
|
||||
// do a preliminary analysis to see if we ever have locals of type `[T;N]` or `&[T;N]`
|
||||
let mut interesting_locals = BitSet::new_empty(local_decls.len());
|
||||
|
|
|
@ -133,7 +133,7 @@ fn find_local_assigned_to_return_place(
|
|||
return local;
|
||||
}
|
||||
|
||||
match body.predecessors()[block].as_slice() {
|
||||
match body.basic_blocks.predecessors()[block].as_slice() {
|
||||
&[pred] => block = pred,
|
||||
_ => return None,
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ impl<'tcx> MirPass<'tcx> for RemoveStorageMarkers {
|
|||
}
|
||||
|
||||
trace!("Running RemoveStorageMarkers on {:?}", body.source);
|
||||
for data in body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate().0 {
|
||||
for data in body.basic_blocks.as_mut_preserves_cfg() {
|
||||
data.statements.retain(|statement| match statement.kind {
|
||||
StatementKind::StorageLive(..)
|
||||
| StatementKind::StorageDead(..)
|
||||
|
|
|
@ -20,11 +20,10 @@ impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops {
|
|||
let param_env = tcx.param_env_reveal_all_normalized(did);
|
||||
let mut should_simplify = false;
|
||||
|
||||
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
|
||||
for block in basic_blocks {
|
||||
for block in body.basic_blocks.as_mut() {
|
||||
let terminator = block.terminator_mut();
|
||||
if let TerminatorKind::Drop { place, target, .. } = terminator.kind {
|
||||
let ty = place.ty(local_decls, tcx);
|
||||
let ty = place.ty(&body.local_decls, tcx);
|
||||
if ty.ty.needs_drop(tcx, param_env) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -18,9 +18,9 @@ impl<'tcx> MirPass<'tcx> for RemoveZsts {
|
|||
return;
|
||||
}
|
||||
let param_env = tcx.param_env(body.source.def_id());
|
||||
let (basic_blocks, local_decls, _) =
|
||||
body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate();
|
||||
for block in basic_blocks.iter_mut() {
|
||||
let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
|
||||
let local_decls = &body.local_decls;
|
||||
for block in basic_blocks {
|
||||
for statement in block.statements.iter_mut() {
|
||||
if let StatementKind::Assign(box (place, _)) | StatementKind::Deinit(box place) =
|
||||
statement.kind
|
||||
|
|
|
@ -61,7 +61,7 @@ impl<'tcx> MirPass<'tcx> for SeparateConstSwitch {
|
|||
/// Returns the amount of blocks that were duplicated
|
||||
pub fn separate_const_switch(body: &mut Body<'_>) -> usize {
|
||||
let mut new_blocks: SmallVec<[(BasicBlock, BasicBlock); 6]> = SmallVec::new();
|
||||
let predecessors = body.predecessors();
|
||||
let predecessors = body.basic_blocks.predecessors();
|
||||
'block_iter: for (block_id, block) in body.basic_blocks().iter_enumerated() {
|
||||
if let TerminatorKind::SwitchInt {
|
||||
discr: Operand::Copy(switch_place) | Operand::Move(switch_place),
|
||||
|
|
|
@ -386,14 +386,17 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
|
|||
trace!("running SimplifyArmIdentity on {:?}", source);
|
||||
|
||||
let local_uses = LocalUseCounter::get_local_uses(body);
|
||||
let (basic_blocks, local_decls, debug_info) =
|
||||
body.basic_blocks_local_decls_mut_and_var_debug_info();
|
||||
for bb in basic_blocks {
|
||||
for bb in body.basic_blocks.as_mut() {
|
||||
if let Some(opt_info) =
|
||||
get_arm_identity_info(&bb.statements, local_decls.len(), debug_info)
|
||||
get_arm_identity_info(&bb.statements, body.local_decls.len(), &body.var_debug_info)
|
||||
{
|
||||
trace!("got opt_info = {:#?}", opt_info);
|
||||
if !optimization_applies(&opt_info, local_decls, &local_uses, &debug_info) {
|
||||
if !optimization_applies(
|
||||
&opt_info,
|
||||
&body.local_decls,
|
||||
&local_uses,
|
||||
&body.var_debug_info,
|
||||
) {
|
||||
debug!("optimization skipped for {:?}", source);
|
||||
continue;
|
||||
}
|
||||
|
@ -431,7 +434,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
|
|||
|
||||
// Fix the debug info to point to the right local
|
||||
for dbg_index in opt_info.dbg_info_to_adjust {
|
||||
let dbg_info = &mut debug_info[dbg_index];
|
||||
let dbg_info = &mut body.var_debug_info[dbg_index];
|
||||
assert!(
|
||||
matches!(dbg_info.value, VarDebugInfoContents::Place(_)),
|
||||
"value was not a Place"
|
||||
|
|
|
@ -886,8 +886,12 @@ impl<K: DepKind> DepGraph<K> {
|
|||
#[derive(Clone, Debug, Encodable, Decodable)]
|
||||
pub struct WorkProduct {
|
||||
pub cgu_name: String,
|
||||
/// Saved file associated with this CGU.
|
||||
pub saved_file: String,
|
||||
/// Saved files associated with this CGU. In each key/value pair, the value is the path to the
|
||||
/// saved file and the key is some identifier for the type of file being saved.
|
||||
///
|
||||
/// By convention, file extensions are currently used as identifiers, i.e. the key "o" maps to
|
||||
/// the object file's path, and "dwo" to the dwarf object file's path.
|
||||
pub saved_files: FxHashMap<String, String>,
|
||||
}
|
||||
|
||||
// Index type for `DepNodeData`'s edges.
|
||||
|
|
|
@ -1705,8 +1705,8 @@ fn clean_ty<'tcx>(this: Ty<'tcx>, cx: &mut DocContext<'tcx>, def_id: Option<DefI
|
|||
ImplTrait(bounds)
|
||||
}
|
||||
|
||||
ty::Closure(..) | ty::Generator(..) => Tuple(vec![]), // FIXME(pcwalton)
|
||||
|
||||
ty::Closure(..) => panic!("Closure"),
|
||||
ty::Generator(..) => panic!("Generator"),
|
||||
ty::Bound(..) => panic!("Bound"),
|
||||
ty::Placeholder(..) => panic!("Placeholder"),
|
||||
ty::GeneratorWitness(..) => panic!("GeneratorWitness"),
|
||||
|
@ -1760,7 +1760,6 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
|||
match tcx.def_kind(parent) {
|
||||
DefKind::Struct | DefKind::Union => false,
|
||||
DefKind::Variant => true,
|
||||
// FIXME: what about DefKind::Ctor?
|
||||
parent_kind => panic!("unexpected parent kind: {:?}", parent_kind),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,9 +237,6 @@ pub(crate) struct RenderOptions {
|
|||
pub(crate) resource_suffix: String,
|
||||
/// Whether to run the static CSS/JavaScript through a minifier when outputting them. `true` by
|
||||
/// default.
|
||||
//
|
||||
// FIXME(misdreavus): the flag name is `--disable-minification` but the meaning is inverted
|
||||
// once read.
|
||||
pub(crate) enable_minification: bool,
|
||||
/// Whether to create an index page in the root of the output directory. If this is true but
|
||||
/// `enable_index_page` is None, generate a static listing of crates instead.
|
||||
|
|
|
@ -1412,7 +1412,10 @@ fn render_impl(
|
|||
id, item_type, in_trait_class,
|
||||
);
|
||||
render_rightside(w, cx, item, containing_item, render_mode);
|
||||
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
|
||||
if trait_.is_some() {
|
||||
// Anchors are only used on trait impls.
|
||||
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
|
||||
}
|
||||
w.write_str("<h4 class=\"code-header\">");
|
||||
render_assoc_item(
|
||||
w,
|
||||
|
@ -1435,7 +1438,10 @@ fn render_impl(
|
|||
id, item_type, in_trait_class
|
||||
);
|
||||
render_rightside(w, cx, item, containing_item, render_mode);
|
||||
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
|
||||
if trait_.is_some() {
|
||||
// Anchors are only used on trait impls.
|
||||
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
|
||||
}
|
||||
w.write_str("<h4 class=\"code-header\">");
|
||||
assoc_const(
|
||||
w,
|
||||
|
@ -1457,7 +1463,10 @@ fn render_impl(
|
|||
let source_id = format!("{}.{}", item_type, name);
|
||||
let id = cx.derive_id(source_id.clone());
|
||||
write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class);
|
||||
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
|
||||
if trait_.is_some() {
|
||||
// Anchors are only used on trait impls.
|
||||
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
|
||||
}
|
||||
w.write_str("<h4 class=\"code-header\">");
|
||||
assoc_type(
|
||||
w,
|
||||
|
@ -1480,7 +1489,10 @@ fn render_impl(
|
|||
"<section id=\"{}\" class=\"{}{} has-srclink\">",
|
||||
id, item_type, in_trait_class
|
||||
);
|
||||
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
|
||||
if trait_.is_some() {
|
||||
// Anchors are only used on trait impls.
|
||||
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
|
||||
}
|
||||
w.write_str("<h4 class=\"code-header\">");
|
||||
assoc_type(
|
||||
w,
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// Check that compiling with packed Split DWARF twice succeeds. This should confirm that DWARF
|
||||
// objects are cached as work products and available to the incremental compilation for `thorin` to
|
||||
// pack into a DWARF package.
|
||||
|
||||
// ignore-tidy-linelength
|
||||
// only-x86_64-unknown-linux-gnu
|
||||
// revisions:rpass1 rpass2
|
||||
|
||||
// [rpass1]compile-flags: -g -Zquery-dep-graph -Zunstable-options -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split
|
||||
// [rpass2]compile-flags: -g -Zquery-dep-graph -Zunstable-options -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
// For `rpass2`, nothing has changed so everything should re-used.
|
||||
#![rustc_partition_reused(module = "split_debuginfo_cached", cfg = "rpass2")]
|
||||
#![rustc_partition_reused(module = "split_debuginfo_cached-another_module", cfg = "rpass2")]
|
||||
|
||||
mod another_module {
|
||||
pub fn foo() -> &'static str {
|
||||
"hello world"
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
println!("{}", another_module::foo());
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<div id="associatedconstant.YOLO" class="method has-srclink"><div class="rightside"><a class="srclink" href="../src/foo/anchors.rs.html#16">source</a></div><h4 class="code-header">const <a href="#associatedconstant.YOLO" class="constant">YOLO</a>: <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a></h4></div>
|
|
@ -0,0 +1 @@
|
|||
<section id="associatedconstant.X" class="associatedconstant has-srclink"><span class="rightside"><a class="srclink" href="../src/foo/anchors.rs.html#42">source</a></span><h4 class="code-header">pub const <a href="#associatedconstant.X" class="constant">X</a>: <a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a> = 0i32</h4></section>
|
|
@ -0,0 +1 @@
|
|||
<section id="method.new" class="method has-srclink"><span class="rightside"><a class="srclink" href="../src/foo/anchors.rs.html#48">source</a></span><h4 class="code-header">pub fn <a href="#method.new" class="fnname">new</a>() -> Self</h4></section>
|
|
@ -0,0 +1 @@
|
|||
<div id="method.bar" class="method has-srclink"><div class="rightside"><a class="srclink" href="../src/foo/anchors.rs.html#23">source</a></div><h4 class="code-header">fn <a href="#method.bar" class="fnname">bar</a>()</h4></div>
|
|
@ -0,0 +1 @@
|
|||
<div id="tymethod.foo" class="method has-srclink"><div class="rightside"><a class="srclink" href="../src/foo/anchors.rs.html#20">source</a></div><h4 class="code-header">fn <a href="#tymethod.foo" class="fnname">foo</a>()</h4></div>
|
|
@ -0,0 +1 @@
|
|||
<div id="associatedtype.T" class="method has-srclink"><div class="rightside"><a class="srclink" href="../src/foo/anchors.rs.html#13">source</a></div><h4 class="code-header">type <a href="#associatedtype.T" class="associatedtype">T</a></h4></div>
|
|
@ -0,0 +1 @@
|
|||
<section id="associatedtype.Y" class="associatedtype has-srclink"><h4 class="code-header">type <a href="#associatedtype.Y" class="associatedtype">Y</a> = <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a></h4></section>
|
|
@ -0,0 +1,49 @@
|
|||
// This test ensures that anchors are generated in the right places.
|
||||
|
||||
#![feature(inherent_associated_types)]
|
||||
#![allow(incomplete_features)]
|
||||
#![crate_name = "foo"]
|
||||
|
||||
pub struct Foo;
|
||||
|
||||
// @has 'foo/trait.Bar.html'
|
||||
pub trait Bar {
|
||||
// There should be no anchors here.
|
||||
// @snapshot no_type_anchor - '//*[@id="associatedtype.T"]'
|
||||
type T;
|
||||
// There should be no anchors here.
|
||||
// @snapshot no_const_anchor - '//*[@id="associatedconstant.YOLO"]'
|
||||
const YOLO: u32;
|
||||
|
||||
// There should be no anchors here.
|
||||
// @snapshot no_tymethod_anchor - '//*[@id="tymethod.foo"]'
|
||||
fn foo();
|
||||
// There should be no anchors here.
|
||||
// @snapshot no_trait_method_anchor - '//*[@id="method.bar"]'
|
||||
fn bar() {}
|
||||
}
|
||||
|
||||
// @has 'foo/struct.Foo.html'
|
||||
impl Bar for Foo {
|
||||
// @has - '//*[@id="associatedtype.T"]/a[@class="anchor"]' ''
|
||||
type T = u32;
|
||||
// @has - '//*[@id="associatedconstant.YOLO"]/a[@class="anchor"]' ''
|
||||
const YOLO: u32 = 0;
|
||||
|
||||
// @has - '//*[@id="method.foo"]/a[@class="anchor"]' ''
|
||||
fn foo() {}
|
||||
// Same check for provided "bar" method.
|
||||
// @has - '//*[@id="method.bar"]/a[@class="anchor"]' ''
|
||||
}
|
||||
|
||||
impl Foo {
|
||||
// @snapshot no_const_anchor2 - '//*[@id="associatedconstant.X"]'
|
||||
// There should be no anchors here.
|
||||
pub const X: i32 = 0;
|
||||
// @snapshot no_type_anchor2 - '//*[@id="associatedtype.Y"]'
|
||||
// There should be no anchors here.
|
||||
pub type Y = u32;
|
||||
// @snapshot no_method_anchor - '//*[@id="method.new"]'
|
||||
// There should be no anchors here.
|
||||
pub fn new() -> Self { Self }
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
#![feature(lang_items)]
|
||||
#![no_std]
|
||||
|
||||
struct NonNull<T: ?Sized>(*mut T);
|
||||
struct NonNull<T: ?Sized>(*const T);
|
||||
|
||||
struct Unique<T: ?Sized>(NonNull<T>);
|
||||
|
||||
|
@ -23,7 +23,7 @@ unsafe fn box_free<T: ?Sized>(ptr: Unique<T>) {
|
|||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn dealloc<T: ?Sized>(_: *mut T) {}
|
||||
fn dealloc<T: ?Sized>(_: *const T) {}
|
||||
|
||||
pub struct Foo<T>(T);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind};
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext, Level};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{FileName, Span};
|
||||
use std::collections::BTreeMap;
|
||||
|
@ -49,6 +49,7 @@ declare_clippy_lint! {
|
|||
struct Modules {
|
||||
local_path: PathBuf,
|
||||
spans: Vec<Span>,
|
||||
lint_levels: Vec<Level>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -70,13 +71,30 @@ impl EarlyLintPass for DuplicateMod {
|
|||
let modules = self.modules.entry(absolute_path).or_insert(Modules {
|
||||
local_path,
|
||||
spans: Vec::new(),
|
||||
lint_levels: Vec::new(),
|
||||
});
|
||||
modules.spans.push(item.span_with_attributes());
|
||||
modules.lint_levels.push(cx.get_lint_level(DUPLICATE_MOD));
|
||||
}
|
||||
}
|
||||
|
||||
fn check_crate_post(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
|
||||
for Modules { local_path, spans } in self.modules.values() {
|
||||
for Modules { local_path, spans, lint_levels } in self.modules.values() {
|
||||
if spans.len() < 2 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// At this point the lint would be emitted
|
||||
assert_eq!(spans.len(), lint_levels.len());
|
||||
let spans: Vec<_> = spans.into_iter().zip(lint_levels).filter_map(|(span, lvl)|{
|
||||
if let Some(id) = lvl.get_expectation_id() {
|
||||
cx.fulfill_expectation(id);
|
||||
}
|
||||
|
||||
(!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span)
|
||||
})
|
||||
.collect();
|
||||
|
||||
if spans.len() < 2 {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
|
|||
// `arg` is a reference as it is `.deref()`ed in the previous block.
|
||||
// Look into the predecessor block and find out the source of deref.
|
||||
|
||||
let ps = &mir.predecessors()[bb];
|
||||
let ps = &mir.basic_blocks.predecessors()[bb];
|
||||
if ps.len() != 1 {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#[feature(lint_reasons)]
|
||||
|
||||
mod a;
|
||||
|
||||
mod b;
|
||||
|
@ -13,4 +15,15 @@ mod c3;
|
|||
mod from_other_module;
|
||||
mod other_module;
|
||||
|
||||
mod d;
|
||||
#[path = "d.rs"]
|
||||
mod d2;
|
||||
#[path = "d.rs"]
|
||||
#[expect(clippy::duplicate_mod)]
|
||||
mod d3;
|
||||
#[path = "d.rs"]
|
||||
#[allow(clippy::duplicate_mod)]
|
||||
mod d4;
|
||||
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: file is loaded as a module multiple times: `$DIR/b.rs`
|
||||
--> $DIR/main.rs:3:1
|
||||
--> $DIR/main.rs:5:1
|
||||
|
|
||||
LL | mod b;
|
||||
| ^^^^^^ first loaded here
|
||||
|
@ -11,7 +11,7 @@ LL | | mod b2;
|
|||
= help: replace all but one `mod` item with `use` items
|
||||
|
||||
error: file is loaded as a module multiple times: `$DIR/c.rs`
|
||||
--> $DIR/main.rs:7:1
|
||||
--> $DIR/main.rs:9:1
|
||||
|
|
||||
LL | mod c;
|
||||
| ^^^^^^ first loaded here
|
||||
|
@ -25,7 +25,7 @@ LL | | mod c3;
|
|||
= help: replace all but one `mod` item with `use` items
|
||||
|
||||
error: file is loaded as a module multiple times: `$DIR/from_other_module.rs`
|
||||
--> $DIR/main.rs:13:1
|
||||
--> $DIR/main.rs:15:1
|
||||
|
|
||||
LL | mod from_other_module;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ first loaded here
|
||||
|
@ -38,5 +38,16 @@ LL | | mod m;
|
|||
|
|
||||
= help: replace all but one `mod` item with `use` items
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: file is loaded as a module multiple times: `$DIR/b.rs`
|
||||
--> $DIR/main.rs:18:1
|
||||
|
|
||||
LL | mod d;
|
||||
| ^^^^^^ first loaded here
|
||||
LL | / #[path = "d.rs"]
|
||||
LL | | mod d2;
|
||||
| |_______^ loaded again here
|
||||
|
|
||||
= help: replace all but one `mod` item with `use` items
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
Loading…
Reference in New Issue