From 5aa5e402f3237b98fe36d8def7ac1d485594bcd1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 16 Oct 2026 09:36:51 +1100 Subject: [PATCH 01/21] Expand an early return case in `coerce_unsized` We can add ADTs to the early return check by avoiding the cases where `coerce_unsized_info` can succeed with ADTs. - It only succeeds if the ADTs are structs, which means we can return early for enums and unions. - It only succeeds if the ADTs are the same struct but differ in a field type, which means we can return early for non-generic structs. --- compiler/rustc_hir_typeck/src/coercion.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 068ec3424d91d..d803ef18f3006 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -574,8 +574,15 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // and call arg (among other positions). // // These targets are known to never be RHS in `LHS: CoerceUnsized`. - // That's because these are built-in types for which a core-provided impl - // doesn't exist, and for which a user-written impl is invalid. + // That's because one of the following is true. + // - For the built-in types, a core-provided impl doesn't exist and a + // user-written impl is invalid. + // - For enums and unions, they can never be the target of a + // `CoerceUnsized` impl. + // - For structs, they can only be the target of a `CoerceUnsized` impl + // if `LHS` and `RHS` are instances of the same struct type but with + // at least one differing field type. This is impossible when we + // exclude structs with any generic args. // // This is technically incomplete when users write impossible bounds like // `where T: CoerceUnsized`, for example, but that trait is unstable @@ -604,6 +611,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { | ty::CoroutineWitness(_, _) | ty::Never | ty::Tuple(_) => return Err(TypeError::Mismatch), + ty::Adt(adt, args) => match adt.adt_kind() { + ty::AdtKind::Enum | ty::AdtKind::Union => return Err(TypeError::Mismatch), + ty::AdtKind::Struct if args.is_empty() => return Err(TypeError::Mismatch), + _ => {} + }, _ => {} } // `&str: CoerceUnsized<&str>` does not hold but is encountered frequently From 395241367501d0eb0f37a0f89ee2a5f0fd83ca8f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 16 Oct 2026 10:04:42 +1100 Subject: [PATCH 02/21] Narrow the scope of a `commit_if_ok` It currently wraps `coerce_unsized` but that function has a number of early returns that don't need the `commit_if_ok`. This commit moves the `commit_if_ok` inside of `coerce_unsized`, to wrap just the part that needs it. --- compiler/rustc_hir_typeck/src/coercion.rs | 163 +++++++++++----------- 1 file changed, 83 insertions(+), 80 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index d803ef18f3006..60df33f307999 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -267,11 +267,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } // Consider coercing the subtype to a DST - // - // NOTE: this is wrapped in a `commit_if_ok` because it creates - // a "spurious" type variable, and we don't want to have that - // type variable in memory if the coercion fails. - let unsize = self.commit_if_ok(|_| self.coerce_unsized(a, b)); + let unsize = self.coerce_unsized(a, b); match unsize { Ok(_) => { debug!("coerce: unsize successful"); @@ -639,85 +635,92 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // a DST unless we have to. This currently comes out in the wash since // we can't unify [T] with U. But to properly support DST, we need to allow // that, at which point we will need extra checks on the target here. + // + // NOTE: this is wrapped in a `commit_if_ok` because it creates + // a "spurious" type variable, and we don't want to have that + // type variable in memory if the coercion fails. + self.commit_if_ok(|_| { + // Handle reborrows before selecting `Source: CoerceUnsized`. + let reborrow = match (source.kind(), target.kind()) { + (&ty::Ref(_, ty_a, mutbl_a), &ty::Ref(_, _, mutbl_b)) => { + coerce_mutbls(mutbl_a, mutbl_b)?; - // Handle reborrows before selecting `Source: CoerceUnsized`. - let reborrow = match (source.kind(), target.kind()) { - (&ty::Ref(_, ty_a, mutbl_a), &ty::Ref(_, _, mutbl_b)) => { - coerce_mutbls(mutbl_a, mutbl_b)?; - - let coercion = RegionVariableOrigin::Coercion(self.cause.span); - let r_borrow = self.next_region_var(coercion); - - // We don't allow two-phase borrows here, at least for initial - // implementation. If it happens that this coercion is a function argument, - // the reborrow in coerce_borrowed_ptr will pick it up. - let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); - - Some(( - Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, - Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), - target: Ty::new_ref(self.tcx, r_borrow, ty_a, mutbl_b), - }, - )) - } - (&ty::Ref(_, ty_a, mt_a), &ty::RawPtr(_, mt_b)) => { - coerce_mutbls(mt_a, mt_b)?; - - Some(( - Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, - Adjustment { - kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), - target: Ty::new_ptr(self.tcx, ty_a, mt_b), - }, - )) - } - _ => None, - }; - let coerce_source = reborrow.as_ref().map_or(source, |(_, r)| r.target); - - // Setup either a subtyping or a LUB relationship between - // the `CoerceUnsized` target type and the expected type. - // We only have the latter, so we use an inference variable - // for the former and let type inference do the rest. - let coerce_target = self.next_ty_var(self.cause.span); - - let mut coercion = self.unify_and( - coerce_target, - target, - reborrow.map(|(deref, autoref)| [deref, autoref]).into_flat_iter(), - Adjust::Pointer(PointerCoercion::Unsize), - ForceLeakCheck::No, - )?; - - // Create an obligation for `Source: CoerceUnsized`. - let cause = self.cause(self.cause.span, ObligationCauseCode::Coercion { source, target }); - let pred = ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]); - let obligation = Obligation::new(self.tcx, cause, self.fcx.param_env, pred); - - if self.next_trait_solver() { - coercion.obligations.push(obligation); + let coercion = RegionVariableOrigin::Coercion(self.cause.span); + let r_borrow = self.next_region_var(coercion); + + // We don't allow two-phase borrows here, at least for initial + // implementation. If it happens that this coercion is a function argument, + // the reborrow in coerce_borrowed_ptr will pick it up. + let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); + + Some(( + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, + Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), + target: Ty::new_ref(self.tcx, r_borrow, ty_a, mutbl_b), + }, + )) + } + (&ty::Ref(_, ty_a, mt_a), &ty::RawPtr(_, mt_b)) => { + coerce_mutbls(mt_a, mt_b)?; + + Some(( + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, + Adjustment { + kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), + target: Ty::new_ptr(self.tcx, ty_a, mt_b), + }, + )) + } + _ => None, + }; + let coerce_source = reborrow.as_ref().map_or(source, |(_, r)| r.target); + + // Setup either a subtyping or a LUB relationship between + // the `CoerceUnsized` target type and the expected type. + // We only have the latter, so we use an inference variable + // for the former and let type inference do the rest. + let coerce_target = self.next_ty_var(self.cause.span); + + let mut coercion = self.unify_and( + coerce_target, + target, + reborrow.map(|(deref, autoref)| [deref, autoref]).into_flat_iter(), + Adjust::Pointer(PointerCoercion::Unsize), + ForceLeakCheck::No, + )?; - if self - .infcx - .visit_proof_tree( - Goal::new(self.tcx, self.param_env, pred), - &mut CoerceVisitor { fcx: self.fcx, span: self.cause.span, errored: false }, - ) - .is_break() - { - return Err(TypeError::Mismatch); + // Create an obligation for `Source: CoerceUnsized`. + let cause = + self.cause(self.cause.span, ObligationCauseCode::Coercion { source, target }); + let pred = + ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]); + let obligation = Obligation::new(self.tcx, cause, self.fcx.param_env, pred); + + if self.next_trait_solver() { + coercion.obligations.push(obligation); + + if self + .infcx + .visit_proof_tree( + Goal::new(self.tcx, self.param_env, pred), + &mut CoerceVisitor { fcx: self.fcx, span: self.cause.span, errored: false }, + ) + .is_break() + { + return Err(TypeError::Mismatch); + } + } else { + self.coerce_unsized_old_solver( + obligation, + &mut coercion, + coerce_unsized_did, + unsize_did, + )?; } - } else { - self.coerce_unsized_old_solver( - obligation, - &mut coercion, - coerce_unsized_did, - unsize_did, - )?; - } - Ok(coercion) + Ok(coercion) + }) } fn coerce_unsized_old_solver( From 771d9a149017da3d7ef9abaee5487c0fb02fc192 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Wed, 23 Sep 2026 04:15:47 +0100 Subject: [PATCH 03/21] Add supporting APIs for MIR storage reconstruction --- compiler/rustc_index/src/interval.rs | 47 +++++++++++++++++----- compiler/rustc_index/src/interval/tests.rs | 2 +- compiler/rustc_mir_dataflow/src/points.rs | 9 +++++ compiler/rustc_mir_transform/src/patch.rs | 15 +++++++ 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_index/src/interval.rs b/compiler/rustc_index/src/interval.rs index d0dcb947c6d1c..475c81e66ab09 100644 --- a/compiler/rustc_index/src/interval.rs +++ b/compiler/rustc_index/src/interval.rs @@ -1,6 +1,7 @@ use std::iter::Step; use std::marker::PhantomData; -use std::ops::{Bound, Range, RangeBounds}; +use std::ops::{Bound, RangeBounds}; +use std::range::RangeInclusive; use smallvec::SmallVec; @@ -59,11 +60,14 @@ impl IntervalSet { } /// Iterates through intervals stored in the set, in order. - pub fn iter_intervals(&self) -> impl Iterator> + pub fn iter_intervals(&self) -> impl Iterator> where I: Step, { - self.map.iter().map(|&(start, end)| I::new(start as usize)..I::new(end as usize + 1)) + self.map.iter().map(|&(start, end)| RangeInclusive { + start: I::new(start as usize), + last: I::new(end as usize), + }) } /// Returns true if we increased the number of elements present. @@ -204,17 +208,38 @@ impl IntervalSet { needle <= *prev_end } + /// Returns whether any point in `range` is contained in the set. + pub fn intersects_range(&self, range: impl RangeBounds + Clone) -> bool { + let start = inclusive_start(range.clone()); + let Some(end) = inclusive_end(self.domain, range) else { + // empty range + return false; + }; + if start > end { + return false; + } + + // Find the last interval whose start is <= end. + let Some(last) = self.map.partition_point(|r| r.0 <= end).checked_sub(1) else { + // All ranges in the map start after the new range's end + return false; + }; + let (_, prev_end) = &self.map[last]; + start <= *prev_end + } + pub fn superset(&self, other: &IntervalSet) -> bool where I: Step, { let mut sup_iter = self.iter_intervals(); let mut current = None; - let contains = |sup: Range, sub: Range, current: &mut Option>| { - if sup.end < sub.start { - // if `sup.end == sub.start`, the next sup doesn't contain `sub.start` + let contains = |sup: RangeInclusive, + sub: RangeInclusive, + current: &mut Option>| { + if sup.last < sub.start { None // continue to the next sup - } else if sup.end >= sub.end && sup.start <= sub.start { + } else if sup.last >= sub.last && sup.start <= sub.start { *current = Some(sup); // save the current sup Some(true) } else { @@ -224,8 +249,8 @@ impl IntervalSet { other.iter_intervals().all(|sub| { current .take() - .and_then(|sup| contains(sup, sub.clone(), &mut current)) - .or_else(|| sup_iter.find_map(|sup| contains(sup, sub.clone(), &mut current))) + .and_then(|sup| contains(sup, sub, &mut current)) + .or_else(|| sup_iter.find_map(|sup| contains(sup, sub, &mut current))) .unwrap_or(false) }) } @@ -242,11 +267,11 @@ impl IntervalSet { let mut other_current = other_iter.next()?; loop { - if self_current.end <= other_current.start { + if self_current.last < other_current.start { self_current = self_iter.next()?; continue; } - if other_current.end <= self_current.start { + if other_current.last < self_current.start { other_current = other_iter.next()?; continue; } diff --git a/compiler/rustc_index/src/interval/tests.rs b/compiler/rustc_index/src/interval/tests.rs index 375af60f66207..cf3222e6c6572 100644 --- a/compiler/rustc_index/src/interval/tests.rs +++ b/compiler/rustc_index/src/interval/tests.rs @@ -5,7 +5,7 @@ fn insert_collapses() { let mut set = IntervalSet::::new(10000); set.insert_range(9831..=9837); set.insert_range(43..=9830); - assert_eq!(set.iter_intervals().collect::>(), [43..9838]); + assert_eq!(set.iter_intervals().collect::>(), [(43..=9837).into()]); } #[test] diff --git a/compiler/rustc_mir_dataflow/src/points.rs b/compiler/rustc_mir_dataflow/src/points.rs index 8568f325f1306..a4e5a3a79f04a 100644 --- a/compiler/rustc_mir_dataflow/src/points.rs +++ b/compiler/rustc_mir_dataflow/src/points.rs @@ -61,6 +61,15 @@ impl DenseLocationMap { PointIndex::new(start_index) } + /// Returns the `PointIndex` for the terminator in the given `BasicBlock`. O(1). + #[inline] + pub fn terminator(&self, block: BasicBlock) -> PointIndex { + let next_block = BasicBlock::new(block.index() + 1); + let next_start_index = + *self.statements_before_block.get(next_block).unwrap_or(&self.num_points); + PointIndex::new(next_start_index - 1) + } + /// Return the PointIndex for the block start of this index. #[inline] pub fn to_block_start(&self, index: PointIndex) -> PointIndex { diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index da1077707baff..739fa5e37ab13 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -210,6 +210,21 @@ impl<'tcx> MirPatch<'tcx> { self.term_patch_map.insert(block, new); } + /// Modifies the terminator of a block, reading the existing patch if one exists or + /// cloning from the body otherwise. + pub(crate) fn mutate_terminator( + &mut self, + body: &Body<'tcx>, + bb: BasicBlock, + f: impl FnOnce(&mut TerminatorKind<'tcx>), + ) { + let kind = self + .term_patch_map + .entry(bb) + .or_insert_with(|| body.basic_blocks[bb].terminator().kind.clone()); + f(kind); + } + /// Mark given statement to be replaced by a `Nop`. /// /// This method only works on statements from the initial body, and cannot be used to remove From 72ffc10d301bb58c8e3401cacd5863b16b8aa44b Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sun, 18 Oct 2026 03:29:30 +0900 Subject: [PATCH 04/21] use adaptive sparse encoding for metadata tables --- compiler/rustc_metadata/src/rmeta/decoder.rs | 14 ++-- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_metadata/src/rmeta/mod.rs | 11 ++-- compiler/rustc_metadata/src/rmeta/table.rs | 67 ++++++++++++++++---- 4 files changed, 71 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index f68cfcc0d388b..5b09e1b1b5703 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -196,8 +196,14 @@ pub(super) trait LazyDecoder: BlobDecoder { self.read_lazy_offset_then(|pos| LazyArray::from_position_and_num_elems(pos, len)) } - fn read_lazy_table(&mut self, width: usize, len: usize) -> LazyTable { - self.read_lazy_offset_then(|pos| LazyTable::from_position_and_encoded_size(pos, width, len)) + fn read_lazy_table( + &mut self, + width_and_sparse: usize, + len: usize, + ) -> LazyTable { + self.read_lazy_offset_then(|pos| { + LazyTable::from_position_and_encoded_size(pos, width_and_sparse, len) + }) } #[inline] @@ -689,9 +695,9 @@ impl Decodable for LazyArray { impl Decodable for LazyTable { fn decode(decoder: &mut D) -> Self { - let width = decoder.read_usize(); + let width_and_sparse = decoder.read_usize(); let len = decoder.read_usize(); - decoder.read_lazy_table(width, len) + decoder.read_lazy_table(width_and_sparse, len) } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index dc8a117704ca4..1a7142872fb25 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -144,7 +144,7 @@ impl<'a, 'tcx, T> Encodable> for LazyArray { impl<'a, 'tcx, Ie, Id, T> Encodable> for LazyTable { fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) { - e.emit_usize(self.width); + e.emit_usize(self.width_and_sparse); e.emit_usize(self.len); e.emit_lazy_distance(self.position); } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 0410874c122a6..6b848a75663f1 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -69,7 +69,7 @@ pub(crate) fn rustc_version(cfg_version: &'static str) -> String { /// Metadata encoding version. /// N.B., increment this if you change the format of metadata such that /// the rustc version can't be found to compare with `rustc_version()`. -const METADATA_VERSION: u8 = 11; +const METADATA_VERSION: u8 = 12; /// Metadata header which includes `METADATA_VERSION`. /// @@ -156,8 +156,9 @@ impl LazyArray { struct LazyTable { position: NonZero, /// The encoded size of the elements of a table is selected at runtime to drop - /// trailing zeroes. This is the number of bytes used for each table element. - width: usize, + /// trailing zeroes. The low bit selects sparse encoding; the remaining bits + /// store the number of bytes used for each table element. + width_and_sparse: usize, /// How many elements are in the table. len: usize, _marker: PhantomData T>, @@ -166,10 +167,10 @@ struct LazyTable { impl LazyTable { fn from_position_and_encoded_size( position: NonZero, - width: usize, + width_and_sparse: usize, len: usize, ) -> LazyTable { - LazyTable { position, width, len, _marker: PhantomData } + LazyTable { position, width_and_sparse, len, _marker: PhantomData } } } diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 2844b7c26a550..dd0e08f7682c8 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -5,6 +5,9 @@ use crate::rmeta::decoder::MetaBlob; use crate::rmeta::remapped_def_id::RemappedRawDefId; use crate::rmeta::*; +const SPARSE_BLOCK_LEN: usize = 64; +const SPARSE_INDEX_BYTES: usize = 12; + pub(super) trait IsDefault: Default { fn is_default(&self) -> bool; } @@ -467,11 +470,6 @@ impl ); } if !value.is_default() { - // FIXME(eddyb) investigate more compact encodings for sparse tables. - // On the PR @michaelwoerister mentioned: - // > Space requirements could perhaps be optimized by using the HAMT `popcnt` - // > trick (i.e. divide things into buckets of 32 or 64 items and then - // > store bit-masks of which item in each bucket is actually serialized). let block = self.blocks.ensure_contains_elem(i, || [0; N]); value.write_to_bytes(block); if self.width != N { @@ -485,17 +483,45 @@ impl let pos = buf.position(); let width = self.width; + let len = self.blocks.len(); + let index_bytes = len.div_ceil(SPARSE_BLOCK_LEN) * SPARSE_INDEX_BYTES; + let non_defaults = if index_bytes < len * width { + self.blocks.iter().filter(|block| **block != [0; N]).count() + } else { + len + }; + let sparse = + u32::try_from(non_defaults).is_ok() && index_bytes + non_defaults * width < len * width; + + if sparse { + let mut base = 0u32; + for chunk in self.blocks.raw.chunks(SPARSE_BLOCK_LEN) { + let mut bitmap = 0u64; + for (i, block) in chunk.iter().enumerate() { + if *block != [0; N] { + bitmap |= 1 << i; + } + } + buf.write_array(bitmap.to_le_bytes()); + buf.write_array(base.to_le_bytes()); + base += bitmap.count_ones(); + } + } + for block in &self.blocks { - buf.write_with(|dest| { - *dest = *block; - width - }); + if !sparse || *block != [0; N] { + buf.write_with(|dest| { + *dest = *block; + width + }); + } } + debug_assert!(width <= usize::MAX >> 1); LazyTable::from_position_and_encoded_size( NonZero::new(pos).unwrap(), - width, - self.blocks.len(), + (width << 1) | usize::from(sparse), + len, ) } } @@ -520,8 +546,23 @@ where return Default::default(); } - let width = self.width; - let start = self.position.get() + (width * i.index()); + let width = self.width_and_sparse >> 1; + let mut position = self.position.get(); + let mut index = i.index(); + if self.width_and_sparse & 1 != 0 { + let block = index / SPARSE_BLOCK_LEN; + let bit = 1u64 << (index % SPARSE_BLOCK_LEN); + let start = position + block * SPARSE_INDEX_BYTES; + let bitmap = u64::from_le_bytes(metadata.blob()[start..start + 8].try_into().unwrap()); + if bitmap & bit == 0 { + return Default::default(); + } + let base = + u32::from_le_bytes(metadata.blob()[start + 8..start + 12].try_into().unwrap()); + index = base as usize + (bitmap & (bit - 1)).count_ones() as usize; + position += self.len.div_ceil(SPARSE_BLOCK_LEN) * SPARSE_INDEX_BYTES; + } + let start = position + width * index; let end = start + width; let bytes = &metadata.blob()[start..end]; From de21c98c951e91adb4abf353494ba7739f425ab8 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 23 Oct 2026 05:07:21 +0900 Subject: [PATCH 05/21] reduce sparse metadata table lookup overhead --- compiler/rustc_metadata/src/rmeta/table.rs | 43 ++++++++++++++++++---- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index dd0e08f7682c8..93d34ac40781e 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -490,9 +490,31 @@ impl } else { len }; - let sparse = - u32::try_from(non_defaults).is_ok() && index_bytes + non_defaults * width < len * width; - + // Use dense encoding when at least 75% of the entries are non-default. + // This avoids sparse lookup overhead for tables with few default entries. + let sparse = len - non_defaults > len / 4 + && u32::try_from(non_defaults).is_ok() + && index_bytes + non_defaults * width < len * width; + + // Sparse tables store one block index per 64 table entries, followed by the + // non-default values in table order: + // + // [ block index 0 ][ block index 1 ] ... [ non-default values, each `width` bytes ] + // + // Each block index occupies 12 bytes, with both integers stored little-endian: + // + // [ bitmap: u64 (8 bytes) | base: u32 (4 bytes) ] + // + // Bitmap bits mark non-default entries, starting with the least significant + // bit. `base` counts the non-default entries in all preceding blocks, so + // lookups do not need to scan their bitmaps. + // + // The `LazyTable` header combines `width` and `sparse` in one integer: + // + // [ width bits ... | sparse ] + // + // Shifting `width` left by one bit makes room for the `sparse` flag. + // The least significant bit is 1 for sparse tables and 0 for dense tables. if sparse { let mut base = 0u32; for chunk in self.blocks.raw.chunks(SPARSE_BLOCK_LEN) { @@ -549,22 +571,29 @@ where let width = self.width_and_sparse >> 1; let mut position = self.position.get(); let mut index = i.index(); + let blob = metadata.blob(); + // An unset bitmap bit represents a default value. For a set bit, the + // index among the packed values is: + // + // base + number of set bits before this bit in the current block + // + // Skip all block indices to reach the start of the packed values. if self.width_and_sparse & 1 != 0 { let block = index / SPARSE_BLOCK_LEN; let bit = 1u64 << (index % SPARSE_BLOCK_LEN); let start = position + block * SPARSE_INDEX_BYTES; - let bitmap = u64::from_le_bytes(metadata.blob()[start..start + 8].try_into().unwrap()); + let entry = &blob[start..start + SPARSE_INDEX_BYTES]; + let bitmap = u64::from_le_bytes(entry[..8].try_into().unwrap()); if bitmap & bit == 0 { return Default::default(); } - let base = - u32::from_le_bytes(metadata.blob()[start + 8..start + 12].try_into().unwrap()); + let base = u32::from_le_bytes(entry[8..].try_into().unwrap()); index = base as usize + (bitmap & (bit - 1)).count_ones() as usize; position += self.len.div_ceil(SPARSE_BLOCK_LEN) * SPARSE_INDEX_BYTES; } let start = position + width * index; let end = start + width; - let bytes = &metadata.blob()[start..end]; + let bytes = &blob[start..end]; if let Ok(fixed) = bytes.try_into() { FixedSizeEncoding::from_bytes(fixed) From 5afdb7aa01dc54b09008f360ff00bc816b13c979 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Thu, 15 Oct 2026 12:11:29 +0200 Subject: [PATCH 06/21] factor out lint name --- compiler/rustc_attr_ir/src/lint.rs | 75 ++++++++++++++---- .../rustc_attr_parsing/src/attributes/lint.rs | 45 ++++------- compiler/rustc_lint/src/diagnostics.rs | 2 +- compiler/rustc_lint/src/levels.rs | 50 ++++-------- compiler/rustc_passes/src/check_attr.rs | 77 ++++++++----------- .../clippy/clippy_lints/src/collapsible_if.rs | 10 ++- .../src/returns/needless_return.rs | 9 ++- tests/pretty/delegation/generics.pp | 2 +- tests/pretty/delegation/inherit-attributes.pp | 2 +- tests/pretty/delegation/inline-attribute.pp | 2 +- tests/pretty/hir-delegation.pp | 2 +- tests/pretty/hir-lifetimes.pp | 3 +- tests/pretty/pin-ergonomics-hir.pp | 5 +- tests/ui/eii/cfg_on_eii.stdout | 3 +- tests/ui/unpretty/exhaustive.hir.stdout | 10 ++- ...ct-exprs-tuple-call-pretty-printing.stdout | 3 +- tests/ui/unpretty/unpretty-expr-fn-arg.stdout | 3 +- 17 files changed, 155 insertions(+), 148 deletions(-) diff --git a/compiler/rustc_attr_ir/src/lint.rs b/compiler/rustc_attr_ir/src/lint.rs index 4839412ed59ad..71fdb8a12d4d0 100644 --- a/compiler/rustc_attr_ir/src/lint.rs +++ b/compiler/rustc_attr_ir/src/lint.rs @@ -1,7 +1,12 @@ +use std::fmt; + +use rustc_ast as ast; +use rustc_ast::join_path_syms; use rustc_macros::{Decodable, Encodable, PrintAttribute, StableHash}; -use rustc_span::{Ident, Span, Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use crate::{HashIgnoredAttrId, PrintAttribute}; + #[derive(Clone, Copy, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub enum LintCheckKind { Allow, @@ -43,21 +48,7 @@ impl LintCheckKind { /// ``` #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub struct LintCheck { - /// The lint's tool name, if present. - pub tool: Option, - /// The lint's name, minus the tool name if any. - /// - /// For example: - /// - `clippy::blah` -> `blah` - /// - `unstable_imports` -> `unstable_imports` - /// - `custom_tool::blah::bleh` -> `blah::bleh` - pub lint_name: Symbol, - /// The span of the lint name. - /// - /// For example `#[deny(foo, bar, reason = "reason")]` - /// produces multiple `LintCheck`s, one with a span pointing to `foo` - /// and another pointing to `bar`. - pub lint_span: Span, + pub lint_name: LintName, pub kind: LintCheckKind, pub reason: Option, /// Needed by `LintExpectationId` to track fulfilled expectations @@ -68,4 +59,56 @@ pub struct LintCheck { /// inside one attribute are all smooshed and flattened together, so /// multiple `LintCheck`s can have the same `attr_span`. pub attr_span: Span, + /// The span of the entire lint name. + /// + /// #[deny(foo::bar::baz)] + /// ^^^^^^^^^^^^^ + pub lint_span: Span, + pub tool_span: Option, +} + +#[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] +pub struct LintName { + /// The lint's tool name, if present. + pub tool: Option, + /// The lint's name, minus the tool name if any. + /// + /// For example: + /// - `clippy::blah` -> `blah` + /// - `unstable_imports` -> `unstable_imports` + /// - `custom_tool::blah::bleh` -> `blah::bleh` + pub name: Symbol, +} + +impl LintName { + pub fn new(path: &ast::Path) -> LintName { + let (tool, name) = match &*path.segments { + [] => unreachable!(), + [lint_name] => (None, lint_name.ident.name), + [tool, lint_name] => (Some(tool.ident.name), lint_name.ident.name), + // Not reachable for any builtin lints, + // but reachable using `register_tool`. + [tool, multi_segment_lint_name @ ..] => ( + Some(tool.ident.name), + Symbol::intern(&join_path_syms( + multi_segment_lint_name.iter().map(|seg| seg.ident.name), + )), + ), + }; + LintName { tool, name } + } + + pub fn is(&self, lint: Symbol) -> bool { + lint == self.name && self.tool.is_none() + } +} + +impl fmt::Display for LintName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(tool) = self.tool { + write!(f, "{tool}::")? + } + write!(f, "{}", self.name)?; + Ok(()) + } } diff --git a/compiler/rustc_attr_parsing/src/attributes/lint.rs b/compiler/rustc_attr_parsing/src/attributes/lint.rs index db57b23f6296a..ae7e40b185f43 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint.rs @@ -1,9 +1,8 @@ -use rustc_ast::join_path_syms; use rustc_attr_ir::AttributeKind; -use rustc_attr_ir::lint::{LintCheck, LintCheckKind}; +use rustc_attr_ir::lint::{LintCheck, LintCheckKind, LintName}; use rustc_attr_ir::target::{AssocCtxt, MethodKind, Target}; use rustc_lint_defs::builtin::UNUSED_ATTRIBUTES; -use rustc_span::{Symbol, sym}; +use rustc_span::sym; use thin_vec::ThinVec; use crate::attributes::{AcceptMapping, AttributeParser, AttributeStability}; @@ -52,33 +51,19 @@ impl LintParser { match item { MetaItemOrLitParser::MetaItemParser(p) => { match p.args() { - ArgParser::NoArgs => { - let (tool, lint_name) = match &*p.path().0.segments { - [] => unreachable!(), - [lint_name] => (None, lint_name.ident.name), - [tool, lint_name] => (Some(tool.ident), lint_name.ident.name), - // Not reachable for any builtin lints, - // but reachable using `register_tool`. - [tool, multi_segment_lint_name @ ..] => ( - Some(tool.ident), - Symbol::intern(&join_path_syms( - multi_segment_lint_name - .iter() - .map(|seg| seg.ident.name), - )), - ), - }; - - self.lints.push(LintCheck { - tool, - lint_name, - lint_span: p.span(), - kind, - attr_id, - reason, - attr_span, - }) - } + ArgParser::NoArgs => self.lints.push(LintCheck { + lint_name: LintName::new(&p.path().0), + kind, + attr_id, + reason, + attr_span, + lint_span: p.span(), + tool_span: if let [tool, _, ..] = &*p.path().0.segments { + Some(tool.span()) + } else { + None + }, + }), // We've found a `reason = "reason"` but we're not the last element. ArgParser::NameValue(nv) if p.path().word_is(sym::reason) => { cx.emit_err(diagnostics::MalformedAttribute { diff --git a/compiler/rustc_lint/src/diagnostics.rs b/compiler/rustc_lint/src/diagnostics.rs index 6273414e9ccda..8694495c0be5c 100644 --- a/compiler/rustc_lint/src/diagnostics.rs +++ b/compiler/rustc_lint/src/diagnostics.rs @@ -63,7 +63,7 @@ impl Subdiagnostic for OverruledAttributeSub { } #[derive(Diagnostic)] -#[diag("unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}`", code = E0710)] +#[diag("unknown tool name `{$tool_name}` found in scoped lint: `{$lint_name}`", code = E0710)] pub(crate) struct UnknownToolInScopedLint { #[primary_span] pub span: Option, diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index a442c62819c8d..ad029e48b3737 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use rustc_ast as ast; use rustc_ast::CRATE_NODE_ID; -use rustc_attr_ir::lint::{LintCheck, LintCheckKind}; +use rustc_attr_ir::lint::{LintCheck, LintCheckKind, LintName}; use rustc_attr_ir::target::Target; use rustc_attr_ir::{Attribute, DocAttribute, find_attr}; use rustc_attr_parsing::{AttributeParser, Recovery, ShouldEmit}; @@ -30,7 +30,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; use rustc_span::def_id::CRATE_MOD_ID; -use rustc_span::{AttrId, DUMMY_SP, Ident, Span, Symbol, sym}; +use rustc_span::{AttrId, DUMMY_SP, Span, Symbol, sym}; use tracing::{debug, instrument}; use crate::builtin::MISSING_DOCS; @@ -728,10 +728,11 @@ where // FIXME: consider performing these checks in attribute parsing instead for (lint_index, lint_check) in lint_checks.into_iter().enumerate() { - let LintCheck { tool, lint_name, lint_span, kind, reason, attr_id, attr_span: _ } = + let LintCheck { lint_name, kind, reason, attr_id, attr_span: _, lint_span, tool_span } = lint_check; - let full_lint_name = lint_name.as_str(); + let full_lint_name = format!("{lint_name}"); + let LintName { tool, name: lint_name } = lint_name; let level = match kind { LintCheckKind::Allow => Level::Allow, @@ -746,21 +747,15 @@ where let lint_id = (level == Level::Expect) .then(|| self.provider.mk_lint_expectation_id(attr_id.attr_id, lint_index as u16)); - let lint_result = self.store.check_lint_name( - full_lint_name, - tool.map(|i| i.name), - self.registered_lint_tools, - ); + let lint_result = + self.store.check_lint_name(lint_name.as_str(), tool, self.registered_lint_tools); let (ids, name) = match lint_result { CheckLintNameResult::Ok(ids) => (ids, lint_name), CheckLintNameResult::Tool(ids, new_lint_name) => { let name = match new_lint_name { - None => { - let complete_name = &format!("{}::{lint_name}", tool.unwrap().name); - Symbol::intern(complete_name) - } + None => Symbol::intern(&full_lint_name), Some(new_lint_name) => { self.emit_span_lint( builtin::RENAMED_AND_REMOVED_LINTS, @@ -787,8 +782,8 @@ where CheckLintNameResult::NoTool => { sess.dcx().emit_err(UnknownToolInScopedLint { - span: Some(tool.unwrap().span), - tool_name: tool.unwrap().name, + span: Some(tool_span.unwrap()), + tool_name: tool.unwrap(), lint_name: full_lint_name.to_string(), is_nightly_build: sess.is_nightly_build(), }); @@ -799,13 +794,11 @@ where if self.lint_added_lints { let suggestion = RenamedLintSuggestion::WithSpan { suggestion: lint_span, replace }; - let name = tool - .map(|Ident { name, .. }| format!("{name}::{full_lint_name}")) - .unwrap_or_else(|| full_lint_name.to_string()); + self.emit_span_lint( RENAMED_AND_REMOVED_LINTS, lint_span.into(), - RenamedLint { name: name.as_str(), replace, suggestion }, + RenamedLint { name: full_lint_name.as_str(), replace, suggestion }, ); } @@ -825,15 +818,10 @@ where CheckLintNameResult::Removed(ref reason) => { if self.lint_added_lints { - let name = if let Some(Ident { name, .. }) = tool { - &format!("{name}::{full_lint_name}") - } else { - full_lint_name - }; self.emit_span_lint( RENAMED_AND_REMOVED_LINTS, lint_span.into(), - RemovedLint { name, reason }, + RemovedLint { name: &full_lint_name, reason }, ); } continue; @@ -841,9 +829,6 @@ where CheckLintNameResult::NoLint(suggestion) => { if self.lint_added_lints { - let name = tool - .map(|Ident { name, .. }| format!("{name}::{full_lint_name}")) - .unwrap_or_else(|| full_lint_name.to_string()); let suggestion = suggestion.map(|(replace, from_rustc)| { UnknownLintSuggestion::WithSpan { suggestion: lint_span, @@ -854,7 +839,7 @@ where self.emit_span_lint( UNKNOWN_LINTS, lint_span.into(), - UnknownLint { name, suggestion }, + UnknownLint { name: full_lint_name, suggestion }, ); } continue; @@ -883,12 +868,7 @@ where }; self.provider.push_expectation( expect_id, - LintExpectation::new( - reason, - lint_span, - is_unfulfilled_lint_expectations, - tool.map(|i| i.name), - ), + LintExpectation::new(reason, lint_span, is_unfulfilled_lint_expectations, tool), ); } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ab951a2cc1ce9..74783a9b8c164 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1412,54 +1412,39 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } fn check_lint_check(&self, hir_id: HirId, lints: &[LintCheck]) { - for lint in lints { - let LintCheck { - tool: None, - lint_name, - lint_span, - kind: _, - reason: _, - attr_id: _, - attr_span: _, - } = *lint - else { - continue; - }; - - match lint_name { - sym::dead_code_pub_in_binary => { - if !self.tcx.crate_types().contains(&CrateType::Executable) { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - lint_span, - diagnostics::Unused { - attr_span: lint_span, - note: diagnostics::UnusedNote::NoEffectDeadCodePubInBinary, - }, - ); - } + for LintCheck { lint_name, lint_span, .. } in lints { + if lint_name.is(sym::dead_code_pub_in_binary) { + if !self.tcx.crate_types().contains(&CrateType::Executable) { + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + *lint_span, + diagnostics::Unused { + attr_span: *lint_span, + note: diagnostics::UnusedNote::NoEffectDeadCodePubInBinary, + }, + ); } - sym::linker_messages | sym::linker_info => { - if hir_id == CRATE_HIR_ID - && self - .tcx - .crate_types() - .iter() - .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib)) - { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - lint_span, - diagnostics::Unused { - attr_span: lint_span, - note: diagnostics::UnusedNote::LinkerMessagesBinaryCrateOnly, - }, - ); - } + } + + if lint_name.is(sym::linker_messages) | lint_name.is(sym::linker_info) { + if hir_id == CRATE_HIR_ID + && self + .tcx + .crate_types() + .iter() + .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib)) + { + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + *lint_span, + diagnostics::Unused { + attr_span: *lint_span, + note: diagnostics::UnusedNote::LinkerMessagesBinaryCrateOnly, + }, + ); } - _ => {} } } } diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index 54291d2867dec..c829b615221cd 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -10,7 +10,8 @@ use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, StmtKind}; use rustc_lexer::TokenKind; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; -use rustc_span::{BytePos, Ident, Span, Symbol}; +use rustc_span::{BytePos, Span, Symbol}; +use rustc_attr_ir::lint::LintName; declare_clippy_lint! { /// ### What it does @@ -247,13 +248,16 @@ impl CollapsibleIf { matches!( lint, LintCheck { - tool: Some(Ident { name: sym::clippy, .. }), + lint_name: LintName { + tool: Some(sym::clippy), + .. + }, kind: LintCheckKind::Expect, .. } ) }) - .any(|lint| [expected_lint_name, sym::style, sym::all].contains(&lint.lint_name)) => + .any(|lint| [expected_lint_name, sym::style, sym::all].contains(&lint.lint_name.name)) => { let attr_span = lints.first().unwrap().attr_span.to(lints.last().unwrap().attr_span); // There is an `expect` attribute -- check that there is no _other_ significant text diff --git a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs index 3e1b4be2cf031..7e9d659d2ce96 100644 --- a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs @@ -13,7 +13,8 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Expr, ExprKind, HirId, MatchSource, StmtKind}; use rustc_lint::{LateContext, LintContext as _}; use rustc_middle::ty::{self, Ty}; -use rustc_span::{BytePos, Ident, Pos as _, Span}; +use rustc_span::{BytePos, Pos as _, Span}; +use rustc_attr_ir::lint::LintName; use std::borrow::Cow; use std::fmt::Display; @@ -188,8 +189,10 @@ fn check_final_expr<'tcx>( matches!( lint, LintCheck { - tool: Some(Ident { name: sym::clippy, .. }), - lint_name: sym::needless_return | sym::style | sym::all | sym::warnings, + lint_name: LintName { + tool: Some(sym::clippy), + name: sym::needless_return | sym::style | sym::all | sym::warnings + }, kind: LintCheckKind::Expect, .. } diff --git a/tests/pretty/delegation/generics.pp b/tests/pretty/delegation/generics.pp index 5bfecd355396d..0baec4c4bde21 100644 --- a/tests/pretty/delegation/generics.pp +++ b/tests/pretty/delegation/generics.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/delegation/inherit-attributes.pp b/tests/pretty/delegation/inherit-attributes.pp index 575e309c392bb..b0f89d12cd169 100644 --- a/tests/pretty/delegation/inherit-attributes.pp +++ b/tests/pretty/delegation/inherit-attributes.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/delegation/inline-attribute.pp b/tests/pretty/delegation/inline-attribute.pp index dfe1097035674..753518972e357 100644 --- a/tests/pretty/delegation/inline-attribute.pp +++ b/tests/pretty/delegation/inline-attribute.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/hir-delegation.pp b/tests/pretty/hir-delegation.pp index 15841d103e0f6..d2c7dc1d8bad4 100644 --- a/tests/pretty/hir-delegation.pp +++ b/tests/pretty/hir-delegation.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/hir-lifetimes.pp b/tests/pretty/hir-lifetimes.pp index 92cc37b584b32..93151a3cd4679 100644 --- a/tests/pretty/hir-lifetimes.pp +++ b/tests/pretty/hir-lifetimes.pp @@ -1,4 +1,5 @@ -#![attr = LintCheck([LintCheck {lint_name: "unused", kind: Allow}])] +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "unused"}, +kind: Allow}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; diff --git a/tests/pretty/pin-ergonomics-hir.pp b/tests/pretty/pin-ergonomics-hir.pp index 5dd405c89258d..3b38ab005c608 100644 --- a/tests/pretty/pin-ergonomics-hir.pp +++ b/tests/pretty/pin-ergonomics-hir.pp @@ -1,6 +1,7 @@ #![attr = Feature([pin_ergonomics#0])] -#![attr = LintCheck([LintCheck {lint_name: "dead_code", kind: Allow}, -LintCheck {lint_name: "incomplete_features", kind: Allow}])] +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, +kind: Allow}, LintCheck {lint_name: LintName {name: "incomplete_features"}, +kind: Allow}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; diff --git a/tests/ui/eii/cfg_on_eii.stdout b/tests/ui/eii/cfg_on_eii.stdout index e49f3cdbb8c45..84b35b003a3d7 100644 --- a/tests/ui/eii/cfg_on_eii.stdout +++ b/tests/ui/eii/cfg_on_eii.stdout @@ -1,5 +1,6 @@ #![attr = Feature([extern_item_impls#0])] -#![attr = LintCheck([LintCheck {lint_name: "deprecated", kind: Deny}])] +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "deprecated"}, +kind: Deny}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 7fb3f85b7f5ce..794f73a766f31 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -3,8 +3,9 @@ coroutines#0, decl_macro#0, deref_patterns#0, explicit_tail_calls#0, gen_blocks#0, more_qualified_paths#0, never_patterns#0, pattern_types#0, pattern_type_macro#0, prelude_import#0, specialization#0, trace_macros#0, trait_alias#0, try_blocks#0, try_blocks_heterogeneous#0, yeet_expr#0])] -#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", -kind: Allow}, LintCheck {lint_name: "unused_attributes", kind: Allow}])] +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, +kind: Allow}, LintCheck {lint_name: LintName {name: "unused_attributes"}, +kind: Allow}])] extern crate std; #[attr = PreludeImport] use std::prelude::rust_2024::*; @@ -39,8 +40,9 @@ use self::prelude::*; * inner multi-line doc comment */ #[doc = "inner doc attribute"] -#[attr = LintCheck([LintCheck {lint_name: "dead_code", kind: Allow}, -LintCheck {lint_name: "unused_variables", kind: Allow}])] +#[attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, +kind: Allow}, LintCheck {lint_name: LintName {name: "unused_variables"}, +kind: Allow}])] #[attr = NoStd] mod attributes { diff --git a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout index 11d31361acdd6..aacd7c313f294 100644 --- a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout +++ b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout @@ -1,6 +1,7 @@ #![attr = Feature([adt_const_params#0, gca_adts#0, gca_min_const_items#0, gca_macroless_args#0])] -#![attr = LintCheck([LintCheck {lint_name: "dead_code", kind: Allow}])] +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, +kind: Allow}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; diff --git a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout index 9c70b1fec7469..5eb05cf14fc2e 100644 --- a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout +++ b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout @@ -1,4 +1,5 @@ -#![attr = LintCheck([LintCheck {lint_name: "dead_code", kind: Allow}])] +#![attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, +kind: Allow}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; From 123d20dd01c57e08028ab298adff0d3b117d4e2d Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Thu, 15 Oct 2026 12:59:12 +0200 Subject: [PATCH 07/21] edit check_lint_name to accept LintName --- compiler/rustc_attr_ir/src/lint.rs | 35 +++++++++++++------ .../rustc_attr_parsing/src/attributes/lint.rs | 4 ++- compiler/rustc_lint/src/context.rs | 7 ++-- compiler/rustc_lint/src/levels.rs | 22 +++++++----- 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_attr_ir/src/lint.rs b/compiler/rustc_attr_ir/src/lint.rs index 71fdb8a12d4d0..a9d1ad351e36b 100644 --- a/compiler/rustc_attr_ir/src/lint.rs +++ b/compiler/rustc_attr_ir/src/lint.rs @@ -1,6 +1,5 @@ use std::fmt; -use rustc_ast as ast; use rustc_ast::join_path_syms; use rustc_macros::{Decodable, Encodable, PrintAttribute, StableHash}; use rustc_span::{Span, Symbol, sym}; @@ -81,23 +80,37 @@ pub struct LintName { } impl LintName { - pub fn new(path: &ast::Path) -> LintName { - let (tool, name) = match &*path.segments { + pub fn new(segments: impl Iterator) -> LintName { + let segments = segments.collect::>(); + let (tool, name) = match &*segments { [] => unreachable!(), - [lint_name] => (None, lint_name.ident.name), - [tool, lint_name] => (Some(tool.ident.name), lint_name.ident.name), + [lint_name] => (None, *lint_name), + [tool, lint_name] => (Some(*tool), *lint_name), // Not reachable for any builtin lints, // but reachable using `register_tool`. - [tool, multi_segment_lint_name @ ..] => ( - Some(tool.ident.name), - Symbol::intern(&join_path_syms( - multi_segment_lint_name.iter().map(|seg| seg.ident.name), - )), - ), + [tool, multi_segment_lint_name @ ..] => { + (Some(*tool), Symbol::intern(&join_path_syms(multi_segment_lint_name))) + } }; LintName { tool, name } } + /// Panics if `lint` is empty. + pub fn from_str(lint: &str) -> LintName { + if lint.is_empty() { + if cfg!(debug_assertions) { + rustc_span::bug!("`lint` is empty") + } else { + return LintName { tool: None, name: sym::empty }; + } + } + if let Some((tool, name)) = lint.split_once("::") { + LintName { tool: Some(Symbol::intern(tool)), name: Symbol::intern(name) } + } else { + LintName { tool: None, name: Symbol::intern(lint) } + } + } + pub fn is(&self, lint: Symbol) -> bool { lint == self.name && self.tool.is_none() } diff --git a/compiler/rustc_attr_parsing/src/attributes/lint.rs b/compiler/rustc_attr_parsing/src/attributes/lint.rs index ae7e40b185f43..1bc35ad1beca0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint.rs @@ -52,7 +52,9 @@ impl LintParser { MetaItemOrLitParser::MetaItemParser(p) => { match p.args() { ArgParser::NoArgs => self.lints.push(LintCheck { - lint_name: LintName::new(&p.path().0), + lint_name: LintName::new( + p.path().0.segments.iter().map(|s| s.ident.name), + ), kind, attr_id, reason, diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 9678ed65d2891..d85941df99cca 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -8,6 +8,7 @@ use std::slice; use rustc_abi as abi; use rustc_ast::BindingMode; use rustc_ast::util::parser::ExprPrecedence; +use rustc_attr_ir::lint::LintName; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; @@ -342,10 +343,12 @@ impl LintStore { /// printing duplicate warnings. pub fn check_lint_name( &self, - lint_name: &str, - tool_name: Option, + lint_name: &LintName, registered_lint_tools: &RegisteredTools, ) -> CheckLintNameResult<'_> { + let LintName { tool: tool_name, name: primary_name } = *lint_name; + let lint_name = primary_name.as_str(); + if let Some(tool_name) = tool_name { // FIXME: rustc and rustdoc are considered tools for lints, but not for attributes. if tool_name != sym::rustc diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index ad029e48b3737..5c57cd336dc5c 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -488,7 +488,9 @@ where .dcx() .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() }); } - match self.store.check_lint_name(lint_name_only, tool_name, self.registered_lint_tools) + match self + .store + .check_lint_name(&LintName::from_str(lint_name), self.registered_lint_tools) { CheckLintNameResult::Renamed(ref replace) => { let name = lint_name.as_str(); @@ -732,7 +734,7 @@ where lint_check; let full_lint_name = format!("{lint_name}"); - let LintName { tool, name: lint_name } = lint_name; + let LintName { tool, name: primary_name } = lint_name; let level = match kind { LintCheckKind::Allow => Level::Allow, @@ -747,8 +749,9 @@ where let lint_id = (level == Level::Expect) .then(|| self.provider.mk_lint_expectation_id(attr_id.attr_id, lint_index as u16)); - let lint_result = - self.store.check_lint_name(lint_name.as_str(), tool, self.registered_lint_tools); + let lint_result = self.store.check_lint_name(&lint_name, self.registered_lint_tools); + + let lint_name = { primary_name }; let (ids, name) = match lint_result { CheckLintNameResult::Ok(ids) => (ids, lint_name), @@ -805,12 +808,13 @@ where // If this lint was renamed, apply the new lint instead of ignoring the // attribute. Ignore any errors or warnings that happen because the new // name is inaccurate. - // NOTE: `new_name` already includes the tool name, so we don't - // have to add it again. - let CheckLintNameResult::Ok(ids) = - self.store.check_lint_name(replace, None, self.registered_lint_tools) + let replace_check = self + .store + .check_lint_name(&LintName::from_str(replace), self.registered_lint_tools); + let (CheckLintNameResult::Ok(ids) | CheckLintNameResult::Tool(ids, _)) = + replace_check else { - panic!("renamed lint does not exist: {replace}"); + panic!("renamed lint does not exist: {replace} :{replace_check:?}"); }; (ids, Symbol::intern(&replace)) From 6a3df0995a73acbbbacd2328b837b5d155f20566 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Thu, 15 Oct 2026 16:42:10 +0200 Subject: [PATCH 08/21] use Lintname in lint machinery --- compiler/rustc_attr_ir/src/lint.rs | 64 +---------------- compiler/rustc_attr_ir/src/pretty_printing.rs | 5 +- .../rustc_attr_parsing/src/attributes/lint.rs | 3 +- compiler/rustc_driver_impl/src/lib.rs | 14 ++-- compiler/rustc_lint/src/context.rs | 63 ++++++++-------- compiler/rustc_lint/src/levels.rs | 3 +- compiler/rustc_session/src/session.rs | 4 +- compiler/rustc_structures/src/lib.rs | 2 + compiler/rustc_structures/src/lint.rs | 71 +++++++++++++++++++ .../src/cargo/lint_groups_priority.rs | 9 +-- .../clippy/clippy_lints/src/collapsible_if.rs | 2 +- .../src/returns/needless_return.rs | 2 +- tests/pretty/delegation/generics.pp | 2 +- tests/pretty/delegation/inherit-attributes.pp | 2 +- tests/pretty/delegation/inline-attribute.pp | 2 +- tests/pretty/hir-delegation.pp | 2 +- tests/pretty/hir-lifetimes.pp | 2 +- tests/pretty/pin-ergonomics-hir.pp | 5 +- tests/ui/eii/cfg_on_eii.stdout | 2 +- tests/ui/unpretty/exhaustive.hir.stdout | 10 +-- ...ct-exprs-tuple-call-pretty-printing.stdout | 2 +- tests/ui/unpretty/unpretty-expr-fn-arg.stdout | 2 +- 22 files changed, 145 insertions(+), 128 deletions(-) create mode 100644 compiler/rustc_structures/src/lint.rs diff --git a/compiler/rustc_attr_ir/src/lint.rs b/compiler/rustc_attr_ir/src/lint.rs index a9d1ad351e36b..2dafadf4cb2a9 100644 --- a/compiler/rustc_attr_ir/src/lint.rs +++ b/compiler/rustc_attr_ir/src/lint.rs @@ -1,8 +1,6 @@ -use std::fmt; - -use rustc_ast::join_path_syms; use rustc_macros::{Decodable, Encodable, PrintAttribute, StableHash}; use rustc_span::{Span, Symbol, sym}; +use rustc_structures::LintName; use crate::{HashIgnoredAttrId, PrintAttribute}; @@ -65,63 +63,3 @@ pub struct LintCheck { pub lint_span: Span, pub tool_span: Option, } - -#[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] -pub struct LintName { - /// The lint's tool name, if present. - pub tool: Option, - /// The lint's name, minus the tool name if any. - /// - /// For example: - /// - `clippy::blah` -> `blah` - /// - `unstable_imports` -> `unstable_imports` - /// - `custom_tool::blah::bleh` -> `blah::bleh` - pub name: Symbol, -} - -impl LintName { - pub fn new(segments: impl Iterator) -> LintName { - let segments = segments.collect::>(); - let (tool, name) = match &*segments { - [] => unreachable!(), - [lint_name] => (None, *lint_name), - [tool, lint_name] => (Some(*tool), *lint_name), - // Not reachable for any builtin lints, - // but reachable using `register_tool`. - [tool, multi_segment_lint_name @ ..] => { - (Some(*tool), Symbol::intern(&join_path_syms(multi_segment_lint_name))) - } - }; - LintName { tool, name } - } - - /// Panics if `lint` is empty. - pub fn from_str(lint: &str) -> LintName { - if lint.is_empty() { - if cfg!(debug_assertions) { - rustc_span::bug!("`lint` is empty") - } else { - return LintName { tool: None, name: sym::empty }; - } - } - if let Some((tool, name)) = lint.split_once("::") { - LintName { tool: Some(Symbol::intern(tool)), name: Symbol::intern(name) } - } else { - LintName { tool: None, name: Symbol::intern(lint) } - } - } - - pub fn is(&self, lint: Symbol) -> bool { - lint == self.name && self.tool.is_none() - } -} - -impl fmt::Display for LintName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(tool) = self.tool { - write!(f, "{tool}::")? - } - write!(f, "{}", self.name)?; - Ok(()) - } -} diff --git a/compiler/rustc_attr_ir/src/pretty_printing.rs b/compiler/rustc_attr_ir/src/pretty_printing.rs index 313602e48993f..4ffd0f07029a3 100644 --- a/compiler/rustc_attr_ir/src/pretty_printing.rs +++ b/compiler/rustc_attr_ir/src/pretty_printing.rs @@ -14,7 +14,9 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_span::def_id::DefId; use rustc_span::hygiene::Transparency; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; -use rustc_structures::{CollapseMacroDebuginfo, CrateType, Limit, NativeLibKind, SanitizerSet}; +use rustc_structures::{ + CollapseMacroDebuginfo, CrateType, Limit, LintName, NativeLibKind, SanitizerSet, +}; use thin_vec::ThinVec; use crate::HashIgnoredAttrId; @@ -223,4 +225,5 @@ print_debug!( CrateType, NativeLibKind, CollapseMacroDebuginfo, + LintName, ); diff --git a/compiler/rustc_attr_parsing/src/attributes/lint.rs b/compiler/rustc_attr_parsing/src/attributes/lint.rs index 1bc35ad1beca0..e0607c49124d9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint.rs @@ -1,8 +1,9 @@ use rustc_attr_ir::AttributeKind; -use rustc_attr_ir::lint::{LintCheck, LintCheckKind, LintName}; +use rustc_attr_ir::lint::{LintCheck, LintCheckKind}; use rustc_attr_ir::target::{AssocCtxt, MethodKind, Target}; use rustc_lint_defs::builtin::UNUSED_ATTRIBUTES; use rustc_span::sym; +use rustc_structures::LintName; use thin_vec::ThinVec; use crate::attributes::{AcceptMapping, AttributeParser, AttributeStability}; diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index b878affae386a..33b019445686a 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -59,7 +59,7 @@ use rustc_session::output::invalid_output_for_target; use rustc_session::{EarlyDiagCtxt, Session, config}; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::{DUMMY_SP, FileName}; -use rustc_structures::CrateType; +use rustc_structures::{CrateType, LintName}; use rustc_target::json::ToJson; use rustc_target::spec::{Target, TargetTuple}; use tracing::trace; @@ -990,11 +990,9 @@ Available lint options: lints } - fn sort_lint_groups( - lints: Vec<(&'static str, Vec, bool)>, - ) -> Vec<(&'static str, Vec)> { + fn sort_lint_groups(lints: Vec<(LintName, Vec, bool)>) -> Vec<(LintName, Vec)> { let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect(); - lints.sort_by_key(|l| l.0); + lints.sort_by(|left, right| Ord::cmp(&left.0, &right.0)); lints } @@ -1041,7 +1039,7 @@ Available lint options: loaded_groups .iter() .chain(&builtin_groups) - .map(|&(s, _)| s.chars().count()) + .map(|(s, _)| s.name.as_str().chars().count()) .max() .unwrap_or(0), ); @@ -1054,7 +1052,7 @@ Available lint options: safe_println!("Lint groups provided by rustc:\n"); - let print_lint_groups = |lints: Vec<(&'static str, Vec)>, all_warnings| { + let print_lint_groups = |lints: Vec<(LintName, Vec)>, all_warnings| { safe_println!(" {} sub-lints", padded("name")); safe_println!(" {} ---------", padded("----")); @@ -1063,7 +1061,7 @@ Available lint options: } for (name, to) in lints { - let name = name.to_lowercase().replace('_', "-"); + let name = name.to_string().to_lowercase().replace('_', "-"); let desc = to .into_iter() .map(|x| x.to_string().replace('_', "-")) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d85941df99cca..a0828074f2e6b 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -8,7 +8,6 @@ use std::slice; use rustc_abi as abi; use rustc_ast::BindingMode; use rustc_ast::util::parser::ExprPrecedence; -use rustc_attr_ir::lint::LintName; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; @@ -33,6 +32,7 @@ use rustc_middle::ty::{ use rustc_session::{DynLintStore, Session}; use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::{Ident, Span, Symbol, bug, sym}; +use rustc_structures::LintName; use tracing::debug; use self::TargetLint::*; @@ -80,10 +80,10 @@ pub struct LintStore { pub(crate) late_lint_mod_passes: Vec, /// Lints indexed by name. - by_name: UnordMap, + by_name: UnordMap, /// Map of registered lint groups to what lints they expand to. - lint_groups: FxIndexMap<&'static str, LintGroup>, + lint_groups: FxIndexMap, } impl DynLintStore for LintStore { @@ -166,7 +166,7 @@ impl LintStore { &self.lints } - pub fn get_lint_groups(&self) -> impl Iterator, bool)> { + pub fn get_lint_groups(&self) -> impl Iterator, bool)> { self.lint_groups .iter() .filter(|(_, LintGroup { depr, .. })| { @@ -174,12 +174,12 @@ impl LintStore { depr.is_none() }) .map(|(k, LintGroup { lint_ids, is_externally_loaded, .. })| { - (*k, lint_ids.clone(), *is_externally_loaded) + (k.clone(), lint_ids.clone(), *is_externally_loaded) }) } /// Returns all lint group names, including deprecated/aliased groups - pub fn get_all_group_names(&self) -> impl Iterator { + pub fn get_all_group_names(&self) -> impl Iterator { self.lint_groups.keys().copied() } @@ -209,14 +209,14 @@ impl LintStore { self.lints.push(lint); let id = LintId::of(lint); - if self.by_name.insert(lint.name_lower(), Id(id)).is_some() { + if self.by_name.insert(LintName::from_str(&lint.name_lower()), Id(id)).is_some() { bug!("duplicate specification of lint {}", lint.name_lower()) } if let Some(FutureIncompatibleInfo { reason, .. }) = lint.future_incompatible { if let Some(edition) = reason.edition() { self.lint_groups - .entry(edition.lint_name()) + .entry(LintName::from_str(edition.lint_name())) .or_insert(LintGroup { lint_ids: vec![], is_externally_loaded: lint.is_externally_loaded, @@ -229,7 +229,7 @@ impl LintStore { // future version of rustc will cause existing code to stop compiling. // Lints tied to an edition don't count because they are opt-in. self.lint_groups - .entry("future_incompatible") + .entry(LintName::from_str("future_incompatible")) .or_insert(LintGroup { lint_ids: vec![], is_externally_loaded: lint.is_externally_loaded, @@ -243,14 +243,16 @@ impl LintStore { } fn insert_group(&mut self, name: &'static str, group: LintGroup) { - let previous = self.lint_groups.insert(name, group); + let previous = self.lint_groups.insert(LintName::from_str(name), group); if previous.is_some() { bug!("group {name:?} already exists"); } } pub fn register_group_alias(&mut self, group_name: &'static str, alias: &'static str) { - let Some(LintGroup { lint_ids, .. }) = self.lint_groups.get(group_name) else { + let Some(LintGroup { lint_ids, .. }) = + self.lint_groups.get(&LintName::from_str(group_name)) + else { bug!("group alias {alias:?} points to unregistered group {group_name:?}") }; @@ -289,7 +291,7 @@ impl LintStore { /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints. #[track_caller] pub fn register_ignored(&mut self, name: &str) { - if self.by_name.insert(name.to_string(), Ignored).is_some() { + if self.by_name.insert(LintName::from_str(name), Ignored).is_some() { bug!("duplicate specification of lint {}", name); } } @@ -297,17 +299,18 @@ impl LintStore { /// This lint has been renamed; warn about using the new name and apply the lint. #[track_caller] pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { - let Some(&Id(target)) = self.by_name.get(new_name) else { + let Some(&Id(target)) = self.by_name.get(&LintName::from_str(new_name)) else { bug!("invalid lint renaming of {} to {}", old_name, new_name); }; - self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target)); + self.by_name.insert(LintName::from_str(old_name), Renamed(new_name.to_string(), target)); } pub fn register_removed(&mut self, name: &str, reason: &str) { - self.by_name.insert(name.into(), Removed(reason.into())); + self.by_name.insert(LintName::from_str(name), Removed(reason.into())); } pub fn find_lints(&self, lint_name: &str) -> Option<&[LintId]> { + let lint_name = &LintName::from_str(lint_name); match self.by_name.get(lint_name) { Some(Id(lint_id)) => Some(slice::from_ref(lint_id)), Some(Renamed(_, lint_id)) => Some(slice::from_ref(lint_id)), @@ -328,7 +331,7 @@ impl LintStore { self.lint_groups.keys().collect::>() ); let lint_name_str = lint_name.as_str(); - self.lint_groups.contains_key(lint_name_str) || { + self.lint_groups.contains_key(&LintName::from_str(lint_name_str)) || { let warnings_name_str = crate::WARNINGS.name_lower(); lint_name_str == warnings_name_str } @@ -346,8 +349,7 @@ impl LintStore { lint_name: &LintName, registered_lint_tools: &RegisteredTools, ) -> CheckLintNameResult<'_> { - let LintName { tool: tool_name, name: primary_name } = *lint_name; - let lint_name = primary_name.as_str(); + let tool_name = lint_name.tool; if let Some(tool_name) = tool_name { // FIXME: rustc and rustdoc are considered tools for lints, but not for attributes. @@ -359,23 +361,19 @@ impl LintStore { } } - let complete_name = if let Some(tool_name) = tool_name { - &format!("{tool_name}::{lint_name}") - } else { - lint_name - }; + let complete_name: &str = &format!("{lint_name}"); + // If the lint was scoped with `tool::` check if the tool lint exists if let Some(tool_name) = tool_name { - match self.by_name.get(complete_name) { - None => match self.lint_groups.get(complete_name) { + match self.by_name.get(lint_name) { + None => match self.lint_groups.get(lint_name) { // If the lint isn't registered, there are two possibilities: None => { // 1. The tool is currently running, so this lint really doesn't exist. // FIXME: should this handle tools that never register a lint, like rustfmt? debug!("lints={:?}", self.by_name); - let tool_prefix = format!("{tool_name}::"); - return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) { + return if self.by_name.keys().any(|lint| lint.tool == Some(tool_name)) { self.no_lint_suggestion(complete_name, tool_name.as_str()) } else { // 2. The tool isn't currently running, so no lints will be registered. @@ -397,10 +395,10 @@ impl LintStore { _ => {} } } - match self.by_name.get(complete_name) { + match self.by_name.get(lint_name) { Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(new_name.to_string()), Some(Removed(reason)) => CheckLintNameResult::Removed(reason.to_string()), - None => match self.lint_groups.get(complete_name) { + None => match self.lint_groups.get(lint_name) { // If neither the lint, nor the lint group exists check if there is a `clippy::` // variant of this lint None => self.check_tool_name_for_backwards_compat(complete_name, "clippy"), @@ -438,7 +436,7 @@ impl LintStore { .filter_map(|(k, LintGroup { depr, .. })| depr.is_none().then_some(k)) .collect(); groups.sort(); - let groups = groups.iter().map(|k| Symbol::intern(k)); + let groups = groups.iter().map(|k| Symbol::intern(&k.to_string())); let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower())); let names: Vec = groups.chain(lints).collect(); let mut lookups = vec![Symbol::intern(&name_lower)]; @@ -460,8 +458,9 @@ impl LintStore { tool_name: &str, ) -> CheckLintNameResult<'_> { let complete_name = format!("{tool_name}::{lint_name}"); - match self.by_name.get(&complete_name) { - None => match self.lint_groups.get(&*complete_name) { + let nlint_name = LintName::from_str(&complete_name); + match self.by_name.get(&nlint_name) { + None => match self.lint_groups.get(&nlint_name) { // Now we are sure, that this lint exists nowhere None => self.no_lint_suggestion(lint_name, tool_name), Some(LintGroup { lint_ids, .. }) => { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 5c57cd336dc5c..ae5ef4d9e7784 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use rustc_ast as ast; use rustc_ast::CRATE_NODE_ID; -use rustc_attr_ir::lint::{LintCheck, LintCheckKind, LintName}; +use rustc_attr_ir::lint::{LintCheck, LintCheckKind}; use rustc_attr_ir::target::Target; use rustc_attr_ir::{Attribute, DocAttribute, find_attr}; use rustc_attr_parsing::{AttributeParser, Recovery, ShouldEmit}; @@ -31,6 +31,7 @@ use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; use rustc_span::def_id::CRATE_MOD_ID; use rustc_span::{AttrId, DUMMY_SP, Span, Symbol, sym}; +use rustc_structures::LintName; use tracing::{debug, instrument}; use crate::builtin::MISSING_DOCS; diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index ad866eb5b28a7..b69c26639e7e8 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -25,7 +25,7 @@ pub use rustc_span::def_id::StableCrateId; use rustc_span::edition::Edition; use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::{RealFileName, Span, Symbol}; -use rustc_structures::{CrateType, Limit}; +use rustc_structures::{CrateType, Limit, LintName}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{ Arch, CfgAbi, CodeModel, DebuginfoKind, MergeFunctions, Os, PanicStrategy, RelocModel, @@ -596,7 +596,7 @@ impl CodegenUnits { } pub struct LintGroup { - pub name: &'static str, + pub name: LintName, pub lints: Vec, pub is_externally_loaded: bool, } diff --git a/compiler/rustc_structures/src/lib.rs b/compiler/rustc_structures/src/lib.rs index cb5dccdfcd180..caa3cdae45acc 100644 --- a/compiler/rustc_structures/src/lib.rs +++ b/compiler/rustc_structures/src/lib.rs @@ -5,12 +5,14 @@ mod collapse_macro_debug_info; mod crate_type; mod limit; +mod lint; mod native_lib_kind; mod sanitizer_set; pub use collapse_macro_debug_info::CollapseMacroDebuginfo; pub use crate_type::CrateType; pub use limit::Limit; +pub use lint::LintName; pub use native_lib_kind::NativeLibKind; pub use sanitizer_set::SanitizerSet; diff --git a/compiler/rustc_structures/src/lint.rs b/compiler/rustc_structures/src/lint.rs new file mode 100644 index 0000000000000..d6891462f15a5 --- /dev/null +++ b/compiler/rustc_structures/src/lint.rs @@ -0,0 +1,71 @@ +use std::fmt; + +#[cfg(feature = "nightly")] +use rustc_macros::{Decodable, Encodable, StableHash}; +use rustc_span::{Symbol, sym}; + +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "nightly", derive(StableHash, Encodable, Decodable))] +pub struct LintName { + /// The lint's tool name, if present. + pub tool: Option, + /// The lint's name, minus the tool name if any. + /// + /// For example: + /// - `clippy::blah` -> `blah` + /// - `unstable_imports` -> `unstable_imports` + /// - `custom_tool::blah::bleh` -> `blah::bleh` + pub name: Symbol, +} + +impl LintName { + pub fn new(segments: impl Iterator) -> LintName { + let segments = segments.collect::>(); + let (tool, name) = match &*segments { + [] => unreachable!(), + [lint_name] => (None, *lint_name), + [tool, lint_name] => (Some(*tool), *lint_name), + // Not reachable for any builtin lints, + // but reachable using `register_tool`. + [tool, lint_name, rest @ ..] => { + let mut lint_name = String::from(lint_name.as_str()); + for seg in rest { + lint_name.push_str("::"); + lint_name.push_str(seg.as_str()); + } + (Some(*tool), Symbol::intern(&lint_name)) + } + }; + LintName { tool, name } + } + + /// Panics if `lint` is empty. + pub fn from_str(lint: &str) -> LintName { + if lint.is_empty() { + if cfg!(debug_assertions) { + rustc_span::bug!("`lint` is empty") + } else { + return LintName { tool: None, name: sym::empty }; + } + } + if let Some((tool, name)) = lint.split_once("::") { + LintName { tool: Some(Symbol::intern(tool)), name: Symbol::intern(name) } + } else { + LintName { tool: None, name: Symbol::intern(lint) } + } + } + + pub fn is(&self, lint: Symbol) -> bool { + lint == self.name && self.tool.is_none() + } +} + +impl fmt::Display for LintName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(tool) = self.tool { + write!(f, "{tool}::")? + } + write!(f, "{}", self.name)?; + Ok(()) + } +} diff --git a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs index 3b35480297f66..dd87fe61b16ea 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs @@ -4,6 +4,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_lint::{LateContext, unerased_lint_store}; use rustc_span::{BytePos, Pos as _, SourceFile, Span, SyntaxContext}; +use rustc_structures::LintName; use std::ops::Range; use std::path::Path; use toml::Spanned; @@ -52,14 +53,14 @@ impl<'a> LintConfig<'a> { } } -fn check_table(cx: &LateContext<'_>, table: &DeTable<'_>, known_groups: &FxHashSet<&str>, file: &SourceFile) { +fn check_table(cx: &LateContext<'_>, table: &DeTable<'_>, known_groups: &FxHashSet, file: &SourceFile) { let mut lints = Vec::new(); let mut groups = Vec::new(); for (name, config) in table { if name.get_ref() != "warnings" && let Some(config) = LintConfig::parse(config) { - if known_groups.contains(&**name.get_ref()) { + if known_groups.contains(&LintName::from_str(&**name.get_ref())) { groups.push((name, config)); } else { lints.push((name, config)); @@ -136,11 +137,11 @@ pub fn check(cx: &LateContext<'_>) { let mut rustc_groups = FxHashSet::default(); let mut clippy_groups = FxHashSet::default(); for group in unerased_lint_store(cx.tcx.sess).get_all_group_names() { - match group.split_once("::") { + match group.tool { None => { rustc_groups.insert(group); }, - Some(("clippy", group)) => { + Some(rustc_span::sym::clippy) => { clippy_groups.insert(group); }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index c829b615221cd..82ed4b1993f3b 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -11,7 +11,7 @@ use rustc_hir::{Block, Expr, ExprKind, StmtKind}; use rustc_lexer::TokenKind; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; use rustc_span::{BytePos, Span, Symbol}; -use rustc_attr_ir::lint::LintName; +use rustc_structures::LintName; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs index 7e9d659d2ce96..915211d74fc1b 100644 --- a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs @@ -14,7 +14,7 @@ use rustc_hir::{Body, Expr, ExprKind, HirId, MatchSource, StmtKind}; use rustc_lint::{LateContext, LintContext as _}; use rustc_middle::ty::{self, Ty}; use rustc_span::{BytePos, Pos as _, Span}; -use rustc_attr_ir::lint::LintName; +use rustc_structures::LintName; use std::borrow::Cow; use std::fmt::Display; diff --git a/tests/pretty/delegation/generics.pp b/tests/pretty/delegation/generics.pp index 0baec4c4bde21..f5b42b351633d 100644 --- a/tests/pretty/delegation/generics.pp +++ b/tests/pretty/delegation/generics.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "incomplete_features" }, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/delegation/inherit-attributes.pp b/tests/pretty/delegation/inherit-attributes.pp index b0f89d12cd169..53f977ebbb7ac 100644 --- a/tests/pretty/delegation/inherit-attributes.pp +++ b/tests/pretty/delegation/inherit-attributes.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "incomplete_features" }, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/delegation/inline-attribute.pp b/tests/pretty/delegation/inline-attribute.pp index 753518972e357..8787a9e167405 100644 --- a/tests/pretty/delegation/inline-attribute.pp +++ b/tests/pretty/delegation/inline-attribute.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "incomplete_features" }, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/hir-delegation.pp b/tests/pretty/hir-delegation.pp index d2c7dc1d8bad4..97364da0e02d8 100644 --- a/tests/pretty/hir-delegation.pp +++ b/tests/pretty/hir-delegation.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "incomplete_features" }, kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; diff --git a/tests/pretty/hir-lifetimes.pp b/tests/pretty/hir-lifetimes.pp index 93151a3cd4679..daee41b37889d 100644 --- a/tests/pretty/hir-lifetimes.pp +++ b/tests/pretty/hir-lifetimes.pp @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "unused"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "unused" }, kind: Allow}])] extern crate std; #[attr = PreludeImport] diff --git a/tests/pretty/pin-ergonomics-hir.pp b/tests/pretty/pin-ergonomics-hir.pp index 3b38ab005c608..624cd37ce5fc4 100644 --- a/tests/pretty/pin-ergonomics-hir.pp +++ b/tests/pretty/pin-ergonomics-hir.pp @@ -1,6 +1,7 @@ #![attr = Feature([pin_ergonomics#0])] -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, -kind: Allow}, LintCheck {lint_name: LintName {name: "incomplete_features"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "dead_code" }, +kind: Allow}, +LintCheck {lint_name: LintName { tool: None, name: "incomplete_features" }, kind: Allow}])] extern crate std; #[attr = PreludeImport] diff --git a/tests/ui/eii/cfg_on_eii.stdout b/tests/ui/eii/cfg_on_eii.stdout index 84b35b003a3d7..226ad1849fa43 100644 --- a/tests/ui/eii/cfg_on_eii.stdout +++ b/tests/ui/eii/cfg_on_eii.stdout @@ -1,5 +1,5 @@ #![attr = Feature([extern_item_impls#0])] -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "deprecated"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "deprecated" }, kind: Deny}])] extern crate std; #[attr = PreludeImport] diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 794f73a766f31..b1c956c2fe9f1 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -3,8 +3,9 @@ coroutines#0, decl_macro#0, deref_patterns#0, explicit_tail_calls#0, gen_blocks#0, more_qualified_paths#0, never_patterns#0, pattern_types#0, pattern_type_macro#0, prelude_import#0, specialization#0, trace_macros#0, trait_alias#0, try_blocks#0, try_blocks_heterogeneous#0, yeet_expr#0])] -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "incomplete_features"}, -kind: Allow}, LintCheck {lint_name: LintName {name: "unused_attributes"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "incomplete_features" }, +kind: Allow}, +LintCheck {lint_name: LintName { tool: None, name: "unused_attributes" }, kind: Allow}])] extern crate std; #[attr = PreludeImport] @@ -40,8 +41,9 @@ use self::prelude::*; * inner multi-line doc comment */ #[doc = "inner doc attribute"] -#[attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, -kind: Allow}, LintCheck {lint_name: LintName {name: "unused_variables"}, +#[attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "dead_code" }, +kind: Allow}, +LintCheck {lint_name: LintName { tool: None, name: "unused_variables" }, kind: Allow}])] #[attr = NoStd] mod attributes { diff --git a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout index aacd7c313f294..d94a76fb882e1 100644 --- a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout +++ b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout @@ -1,6 +1,6 @@ #![attr = Feature([adt_const_params#0, gca_adts#0, gca_min_const_items#0, gca_macroless_args#0])] -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "dead_code" }, kind: Allow}])] extern crate std; #[attr = PreludeImport] diff --git a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout index 5eb05cf14fc2e..d80b073e52b73 100644 --- a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout +++ b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout @@ -1,4 +1,4 @@ -#![attr = LintCheck([LintCheck {lint_name: LintName {name: "dead_code"}, +#![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "dead_code" }, kind: Allow}])] extern crate std; #[attr = PreludeImport] From f6f868345dc1aae2c85d34f80538a53ffdf0bc25 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Fri, 16 Oct 2026 23:15:16 +0200 Subject: [PATCH 09/21] store Lintname in error types --- compiler/rustc_lint/src/context.rs | 88 ++++++----------- compiler/rustc_lint/src/diagnostics.rs | 93 +++++++++--------- compiler/rustc_lint/src/levels.rs | 96 ++++++++++--------- compiler/rustc_structures/src/lint.rs | 22 +++++ .../tests/ui/legacy_numeric_constants.fixed | 6 +- .../tests/ui/legacy_numeric_constants.rs | 6 +- 6 files changed, 154 insertions(+), 157 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index a0828074f2e6b..8a72c064f4823 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -101,7 +101,7 @@ enum TargetLint { Id(LintId), /// Temporary renaming, used for easing migration pain; see #16545 - Renamed(String, LintId), + Renamed(LintName, LintId), /// Lint with this name existed previously, but has been removed/deprecated. /// The string argument is the reason for removal. @@ -130,18 +130,18 @@ struct LintGroup { pub enum CheckLintNameResult<'a> { Ok(&'a [LintId]), /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. - NoLint(Option<(Symbol, bool)>), + NoLint(Option<(LintName, bool)>), /// The lint refers to a tool that has not been registered. NoTool, /// The lint has been renamed to a new name. - Renamed(String), + Renamed(LintName), /// The lint has been removed due to the given reason. Removed(String), /// The lint is from a tool. The `LintId` will be returned as if it were a /// rustc lint. The `Option` indicates if the lint has been /// renamed. - Tool(&'a [LintId], Option), + Tool(&'a [LintId], Option), /// The lint is from a tool. Either the lint does not exist in the tool or /// the code was not compiled with the tool and therefore the lint was @@ -299,24 +299,24 @@ impl LintStore { /// This lint has been renamed; warn about using the new name and apply the lint. #[track_caller] pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { - let Some(&Id(target)) = self.by_name.get(&LintName::from_str(new_name)) else { - bug!("invalid lint renaming of {} to {}", old_name, new_name); + let new_name = LintName::from_str(new_name); + let Some(&Id(target)) = self.by_name.get(&new_name) else { + bug!("invalid lint renaming of {old_name} to {new_name}"); }; - self.by_name.insert(LintName::from_str(old_name), Renamed(new_name.to_string(), target)); + self.by_name.insert(LintName::from_str(old_name), Renamed(new_name, target)); } pub fn register_removed(&mut self, name: &str, reason: &str) { self.by_name.insert(LintName::from_str(name), Removed(reason.into())); } - pub fn find_lints(&self, lint_name: &str) -> Option<&[LintId]> { - let lint_name = &LintName::from_str(lint_name); - match self.by_name.get(lint_name) { + pub fn find_lints(&self, lint_name: LintName) -> Option<&[LintId]> { + match self.by_name.get(&lint_name) { Some(Id(lint_id)) => Some(slice::from_ref(lint_id)), Some(Renamed(_, lint_id)) => Some(slice::from_ref(lint_id)), Some(Removed(_)) => None, Some(Ignored) => Some(&[]), - None => match self.lint_groups.get(lint_name) { + None => match self.lint_groups.get(&lint_name) { Some(LintGroup { lint_ids, .. }) => Some(lint_ids), None => None, }, @@ -346,7 +346,7 @@ impl LintStore { /// printing duplicate warnings. pub fn check_lint_name( &self, - lint_name: &LintName, + lint_name: LintName, registered_lint_tools: &RegisteredTools, ) -> CheckLintNameResult<'_> { let tool_name = lint_name.tool; @@ -361,12 +361,10 @@ impl LintStore { } } - let complete_name: &str = &format!("{lint_name}"); - // If the lint was scoped with `tool::` check if the tool lint exists if let Some(tool_name) = tool_name { - match self.by_name.get(lint_name) { - None => match self.lint_groups.get(lint_name) { + match self.by_name.get(&lint_name) { + None => match self.lint_groups.get(&lint_name) { // If the lint isn't registered, there are two possibilities: None => { // 1. The tool is currently running, so this lint really doesn't exist. @@ -374,7 +372,7 @@ impl LintStore { debug!("lints={:?}", self.by_name); return if self.by_name.keys().any(|lint| lint.tool == Some(tool_name)) { - self.no_lint_suggestion(complete_name, tool_name.as_str()) + self.no_lint_suggestion(lint_name) } else { // 2. The tool isn't currently running, so no lints will be registered. // To avoid giving a false positive, ignore all unknown lints. @@ -383,7 +381,7 @@ impl LintStore { } Some(LintGroup { lint_ids, depr, .. }) => { return if let &Some(LintAlias { name, silent: false }) = depr { - CheckLintNameResult::Tool(lint_ids, Some(name.to_string())) + CheckLintNameResult::Tool(lint_ids, Some(LintName::from_str(name))) } else { CheckLintNameResult::Tool(lint_ids, None) }; @@ -395,17 +393,15 @@ impl LintStore { _ => {} } } - match self.by_name.get(lint_name) { - Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(new_name.to_string()), + match self.by_name.get(&lint_name) { + Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(*new_name), Some(Removed(reason)) => CheckLintNameResult::Removed(reason.to_string()), - None => match self.lint_groups.get(lint_name) { - // If neither the lint, nor the lint group exists check if there is a `clippy::` - // variant of this lint - None => self.check_tool_name_for_backwards_compat(complete_name, "clippy"), + None => match self.lint_groups.get(&lint_name) { + None => self.no_lint_suggestion(lint_name), Some(LintGroup { lint_ids, depr, .. }) => { // Check if the lint group name is deprecated if let &Some(LintAlias { name, silent: false }) = depr { - CheckLintNameResult::Tool(lint_ids, Some(name.to_string())) + CheckLintNameResult::Tool(lint_ids, Some(LintName::from_str(name))) } else { CheckLintNameResult::Ok(lint_ids) } @@ -416,12 +412,12 @@ impl LintStore { } } - fn no_lint_suggestion(&self, lint_name: &str, tool_name: &str) -> CheckLintNameResult<'_> { + fn no_lint_suggestion(&self, lint_name: LintName) -> CheckLintNameResult<'_> { let name_lower = lint_name.to_lowercase(); - if lint_name.chars().any(char::is_uppercase) && self.find_lints(&name_lower).is_some() { + if self.find_lints(name_lower).is_some() { // First check if the lint name is (partly) in upper case instead of lower case... - return CheckLintNameResult::NoLint(Some((Symbol::intern(&name_lower), false))); + return CheckLintNameResult::NoLint(Some((name_lower, false))); } // ...if not, search for lints with a similar name @@ -439,41 +435,17 @@ impl LintStore { let groups = groups.iter().map(|k| Symbol::intern(&k.to_string())); let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower())); let names: Vec = groups.chain(lints).collect(); - let mut lookups = vec![Symbol::intern(&name_lower)]; - if let Some(stripped) = name_lower.split("::").last() { + let mut lookups = vec![Symbol::intern(&name_lower.to_string())]; + if let Some(stripped) = name_lower.name.as_str().split("::").last() { lookups.push(Symbol::intern(stripped)); } let res = find_best_match_for_names(&names, &lookups, None); - let is_rustc = res.map_or_else( - || false, - |s| name_lower.contains("::") && !s.as_str().starts_with(tool_name), - ); - let suggestion = res.map(|s| (s, is_rustc)); + let res = res.map(|s| LintName::from_str(s.as_str())); + let is_builtin = + res.as_ref().map(|s| lint_name.tool.is_some() && s.tool.is_none()).unwrap_or(false); + let suggestion = res.map(|s| (s, is_builtin)); CheckLintNameResult::NoLint(suggestion) } - - fn check_tool_name_for_backwards_compat( - &self, - lint_name: &str, - tool_name: &str, - ) -> CheckLintNameResult<'_> { - let complete_name = format!("{tool_name}::{lint_name}"); - let nlint_name = LintName::from_str(&complete_name); - match self.by_name.get(&nlint_name) { - None => match self.lint_groups.get(&nlint_name) { - // Now we are sure, that this lint exists nowhere - None => self.no_lint_suggestion(lint_name, tool_name), - Some(LintGroup { lint_ids, .. }) => { - CheckLintNameResult::Tool(lint_ids, Some(complete_name)) - } - }, - Some(Id(id)) => CheckLintNameResult::Tool(slice::from_ref(id), Some(complete_name)), - Some(other) => { - debug!("got renamed lint {:?}", other); - CheckLintNameResult::NoLint(None) - } - } - } } /// Context for lint checking outside of type inference. diff --git a/compiler/rustc_lint/src/diagnostics.rs b/compiler/rustc_lint/src/diagnostics.rs index 8694495c0be5c..f4e111ddc6760 100644 --- a/compiler/rustc_lint/src/diagnostics.rs +++ b/compiler/rustc_lint/src/diagnostics.rs @@ -17,6 +17,7 @@ use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::{Ident, Span, Symbol, sym}; +use rustc_structures::LintName; use crate::LateContext; use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; @@ -68,7 +69,7 @@ pub(crate) struct UnknownToolInScopedLint { #[primary_span] pub span: Option, pub tool_name: Symbol, - pub lint_name: String, + pub lint_name: LintName, #[help("add `#![register_tool({$tool_name})]` to the crate root")] pub is_nightly_build: bool, } @@ -106,9 +107,9 @@ pub(crate) struct BuiltinEllipsisInclusiveRangePatterns { #[derive(Subdiagnostic)] #[note("requested on the command line with `{$level} {$lint_name}`")] -pub(crate) struct RequestedLevel<'a> { +pub(crate) struct RequestedLevel { pub level: rustc_lint_defs::Level, - pub lint_name: &'a str, + pub lint_name: LintName, } #[derive(Diagnostic)] @@ -119,10 +120,10 @@ pub(crate) struct UnsupportedGroup { #[derive(Diagnostic)] #[diag("unknown lint tool: `{$tool_name}`", code = E0602)] -pub(crate) struct CheckNameUnknownTool<'a> { +pub(crate) struct CheckNameUnknownTool { pub tool_name: Symbol, #[subdiagnostic] - pub sub: RequestedLevel<'a>, + pub sub: RequestedLevel, } // array_into_iter.rs @@ -1395,76 +1396,76 @@ pub(crate) struct OverruledAttributeLint<'a> { } #[derive(Diagnostic)] -#[diag("lint name `{$name}` is deprecated and may not have an effect in the future")] -pub(crate) struct DeprecatedLintName<'a> { - pub name: String, - #[suggestion("change it to", code = "{replace}", applicability = "machine-applicable")] +#[diag("lint name `{$lint_name}` is deprecated and may not have an effect in the future")] +pub(crate) struct DeprecatedLintName { + pub lint_name: LintName, + #[suggestion("change it to", code = "{replacement}", applicability = "machine-applicable")] pub suggestion: Span, - pub replace: &'a str, + pub replacement: LintName, } #[derive(Diagnostic)] -#[diag("lint name `{$name}` is deprecated and may not have an effect in the future")] -#[help("change it to {$replace}")] -pub(crate) struct DeprecatedLintNameFromCommandLine<'a> { - pub name: String, - pub replace: &'a str, +#[diag("lint name `{$lint_name}` is deprecated and may not have an effect in the future")] +#[help("change it to {$replacement}")] +pub(crate) struct DeprecatedLintNameFromCommandLine { + pub lint_name: LintName, + pub replacement: LintName, #[subdiagnostic] - pub requested_level: RequestedLevel<'a>, + pub requested_level: RequestedLevel, } #[derive(Diagnostic)] -#[diag("lint `{$name}` has been renamed to `{$replace}`")] -pub(crate) struct RenamedLint<'a> { - pub name: &'a str, - pub replace: &'a str, +#[diag("lint `{$name}` has been renamed to `{$replacement}`")] +pub(crate) struct RenamedLint { + pub name: LintName, + pub replacement: LintName, #[subdiagnostic] - pub suggestion: RenamedLintSuggestion<'a>, + pub suggestion: RenamedLintSuggestion, } #[derive(Subdiagnostic)] -pub(crate) enum RenamedLintSuggestion<'a> { - #[suggestion("use the new name", code = "{replace}", applicability = "machine-applicable")] +pub(crate) enum RenamedLintSuggestion { + #[suggestion("use the new name", code = "{replacement}", applicability = "machine-applicable")] WithSpan { #[primary_span] suggestion: Span, - replace: &'a str, + replacement: LintName, }, - #[help("use the new name `{$replace}`")] - WithoutSpan { replace: &'a str }, + #[help("use the new name `{$replacement}`")] + WithoutSpan { replacement: LintName }, } #[derive(Diagnostic)] -#[diag("lint `{$name}` has been renamed to `{$replace}`")] -pub(crate) struct RenamedLintFromCommandLine<'a> { - pub name: &'a str, - pub replace: &'a str, +#[diag("lint `{$lint_name}` has been renamed to `{$replacement}`")] +pub(crate) struct RenamedLintFromCommandLine { + pub lint_name: LintName, + pub replacement: LintName, #[subdiagnostic] - pub suggestion: RenamedLintSuggestion<'a>, + pub suggestion: RenamedLintSuggestion, #[subdiagnostic] - pub requested_level: RequestedLevel<'a>, + pub requested_level: RequestedLevel, } #[derive(Diagnostic)] #[diag("lint `{$name}` has been removed: {$reason}")] pub(crate) struct RemovedLint<'a> { - pub name: &'a str, + pub name: LintName, pub reason: &'a str, } #[derive(Diagnostic)] -#[diag("lint `{$name}` has been removed: {$reason}")] +#[diag("lint `{$lint_name}` has been removed: {$reason}")] pub(crate) struct RemovedLintFromCommandLine<'a> { - pub name: &'a str, + pub lint_name: LintName, pub reason: &'a str, #[subdiagnostic] - pub requested_level: RequestedLevel<'a>, + pub requested_level: RequestedLevel, } #[derive(Diagnostic)] #[diag("unknown lint: `{$name}`")] pub(crate) struct UnknownLint { - pub name: String, + pub name: LintName, #[subdiagnostic] pub suggestion: Option, } @@ -1476,32 +1477,32 @@ pub(crate) enum UnknownLintSuggestion { [true] a lint with a similar name exists in `rustc` lints *[false] did you mean }", - code = "{replace}", + code = "{replacement}", applicability = "maybe-incorrect" )] WithSpan { #[primary_span] suggestion: Span, - replace: Symbol, + replacement: LintName, from_rustc: bool, }, #[help( "{$from_rustc -> - [true] a lint with a similar name exists in `rustc` lints: `{$replace}` - *[false] did you mean: `{$replace}` + [true] a lint with a similar name exists in `rustc` lints: `{$replacement}` + *[false] did you mean: `{$replacement}` }" )] - WithoutSpan { replace: Symbol, from_rustc: bool }, + WithoutSpan { replacement: LintName, from_rustc: bool }, } #[derive(Diagnostic)] -#[diag("unknown lint: `{$name}`", code = E0602)] -pub(crate) struct UnknownLintFromCommandLine<'a> { - pub name: String, +#[diag("unknown lint: `{$lint_name}`", code = E0602)] +pub(crate) struct UnknownLintFromCommandLine { + pub lint_name: LintName, #[subdiagnostic] pub suggestion: Option, #[subdiagnostic] - pub requested_level: RequestedLevel<'a>, + pub requested_level: RequestedLevel, } #[derive(Diagnostic)] diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index ae5ef4d9e7784..4e82620a9af21 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -481,45 +481,48 @@ where } fn add_command_line(&mut self) { - for &(ref lint_name, level) in &self.sess.opts.lint_opts { + for &(ref lint_name_, level) in &self.sess.opts.lint_opts { // Checks the validity of lint names derived from the command line. - let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name); + let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name_); if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn) { self.sess .dcx() .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() }); } - match self - .store - .check_lint_name(&LintName::from_str(lint_name), self.registered_lint_tools) - { - CheckLintNameResult::Renamed(ref replace) => { - let name = lint_name.as_str(); - let suggestion = RenamedLintSuggestion::WithoutSpan { replace }; + let lint_name = LintName::from_str(lint_name_); + match self.store.check_lint_name(lint_name, self.registered_lint_tools) { + CheckLintNameResult::Renamed(replacement) => { + let suggestion = RenamedLintSuggestion::WithoutSpan { replacement }; let requested_level = RequestedLevel { level, lint_name }; - let lint = - RenamedLintFromCommandLine { name, replace, suggestion, requested_level }; + let lint = RenamedLintFromCommandLine { + lint_name, + replacement, + suggestion, + requested_level, + }; self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint); } CheckLintNameResult::Removed(ref reason) => { - let name = lint_name.as_str(); let requested_level = RequestedLevel { level, lint_name }; - let lint = RemovedLintFromCommandLine { name, reason, requested_level }; + let lint = RemovedLintFromCommandLine { lint_name, reason, requested_level }; self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint); } CheckLintNameResult::NoLint(suggestion) => { - let name = lint_name.clone(); - let suggestion = suggestion.map(|(replace, from_rustc)| { - UnknownLintSuggestion::WithoutSpan { replace, from_rustc } + let suggestion = suggestion.map(|(replacement, from_rustc)| { + UnknownLintSuggestion::WithoutSpan { replacement, from_rustc } }); let requested_level = RequestedLevel { level, lint_name }; - let lint = UnknownLintFromCommandLine { name, suggestion, requested_level }; + let lint = + UnknownLintFromCommandLine { lint_name, suggestion, requested_level }; self.emit_lint(UNKNOWN_LINTS, lint); } - CheckLintNameResult::Tool(_, Some(ref replace)) => { - let name = lint_name.clone(); + CheckLintNameResult::Tool(_, Some(replacement)) => { let requested_level = RequestedLevel { level, lint_name }; - let lint = DeprecatedLintNameFromCommandLine { name, replace, requested_level }; + let lint = DeprecatedLintNameFromCommandLine { + lint_name, + replacement, + requested_level, + }; self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint); } CheckLintNameResult::NoTool => { @@ -531,7 +534,7 @@ where _ => {} }; - let lint_flag_val = Symbol::intern(lint_name); + let lint_flag_val = Symbol::intern(lint_name_); let Some(ids) = self.store.find_lints(lint_name) else { // errors already handled above @@ -733,9 +736,7 @@ where for (lint_index, lint_check) in lint_checks.into_iter().enumerate() { let LintCheck { lint_name, kind, reason, attr_id, attr_span: _, lint_span, tool_span } = lint_check; - - let full_lint_name = format!("{lint_name}"); - let LintName { tool, name: primary_name } = lint_name; + let LintName { tool, name: _ } = lint_name; let level = match kind { LintCheckKind::Allow => Level::Allow, @@ -750,27 +751,25 @@ where let lint_id = (level == Level::Expect) .then(|| self.provider.mk_lint_expectation_id(attr_id.attr_id, lint_index as u16)); - let lint_result = self.store.check_lint_name(&lint_name, self.registered_lint_tools); - - let lint_name = { primary_name }; + let lint_result = self.store.check_lint_name(lint_name, self.registered_lint_tools); - let (ids, name) = match lint_result { + let (ids, name): (_, LintName) = match lint_result { CheckLintNameResult::Ok(ids) => (ids, lint_name), CheckLintNameResult::Tool(ids, new_lint_name) => { let name = match new_lint_name { - None => Symbol::intern(&full_lint_name), - Some(new_lint_name) => { + None => lint_name, + Some(replacement) => { self.emit_span_lint( builtin::RENAMED_AND_REMOVED_LINTS, lint_span.into(), DeprecatedLintName { - name: lint_name.to_string(), + lint_name, suggestion: lint_span, - replace: &new_lint_name, + replacement, }, ); - Symbol::intern(&new_lint_name) + replacement } }; (ids, name) @@ -788,37 +787,36 @@ where sess.dcx().emit_err(UnknownToolInScopedLint { span: Some(tool_span.unwrap()), tool_name: tool.unwrap(), - lint_name: full_lint_name.to_string(), + lint_name, is_nightly_build: sess.is_nightly_build(), }); continue; } - CheckLintNameResult::Renamed(ref replace) => { + CheckLintNameResult::Renamed(replacement) => { if self.lint_added_lints { let suggestion = - RenamedLintSuggestion::WithSpan { suggestion: lint_span, replace }; + RenamedLintSuggestion::WithSpan { suggestion: lint_span, replacement }; self.emit_span_lint( RENAMED_AND_REMOVED_LINTS, lint_span.into(), - RenamedLint { name: full_lint_name.as_str(), replace, suggestion }, + RenamedLint { name: lint_name, replacement, suggestion }, ); } // If this lint was renamed, apply the new lint instead of ignoring the // attribute. Ignore any errors or warnings that happen because the new // name is inaccurate. - let replace_check = self - .store - .check_lint_name(&LintName::from_str(replace), self.registered_lint_tools); + let replace_check = + self.store.check_lint_name(replacement, self.registered_lint_tools); let (CheckLintNameResult::Ok(ids) | CheckLintNameResult::Tool(ids, _)) = replace_check else { - panic!("renamed lint does not exist: {replace} :{replace_check:?}"); + panic!("renamed lint does not exist: {replacement} :{replace_check:?}"); }; - (ids, Symbol::intern(&replace)) + (ids, replacement) } CheckLintNameResult::Removed(ref reason) => { @@ -826,7 +824,7 @@ where self.emit_span_lint( RENAMED_AND_REMOVED_LINTS, lint_span.into(), - RemovedLint { name: &full_lint_name, reason }, + RemovedLint { name: lint_name, reason }, ); } continue; @@ -834,24 +832,28 @@ where CheckLintNameResult::NoLint(suggestion) => { if self.lint_added_lints { - let suggestion = suggestion.map(|(replace, from_rustc)| { + let suggestion = suggestion.map(|(replacement, from_rustc)| { UnknownLintSuggestion::WithSpan { suggestion: lint_span, - replace, + replacement, from_rustc, } }); self.emit_span_lint( UNKNOWN_LINTS, lint_span.into(), - UnknownLint { name: full_lint_name, suggestion }, + UnknownLint { name: lint_name, suggestion }, ); } continue; } }; - let src = LintLevelSource::Node { name, span: lint_span, reason }; + let src = LintLevelSource::Node { + name: Symbol::intern(&name.to_string()), + span: lint_span, + reason, + }; for &id in ids { if self.check_gated_lint(id, lint_span, false) { self.insert_spec(id, LevelSpec::new(level, lint_id, src)); diff --git a/compiler/rustc_structures/src/lint.rs b/compiler/rustc_structures/src/lint.rs index d6891462f15a5..9ae557ee2ff00 100644 --- a/compiler/rustc_structures/src/lint.rs +++ b/compiler/rustc_structures/src/lint.rs @@ -55,6 +55,18 @@ impl LintName { } } + pub fn to_lowercase(mut self) -> LintName { + fn lowercase(s: &mut Symbol) { + let lower = s.as_str().to_lowercase(); + *s = Symbol::intern(&lower); + } + if let Some(tool) = &mut self.tool { + lowercase(tool); + } + lowercase(&mut self.name); + self + } + pub fn is(&self, lint: Symbol) -> bool { lint == self.name && self.tool.is_none() } @@ -69,3 +81,13 @@ impl fmt::Display for LintName { Ok(()) } } + +#[cfg(feature = "nightly")] +impl rustc_error_messages::IntoDiagArg for LintName { + fn into_diag_arg( + self, + _: &mut Option, + ) -> rustc_error_messages::DiagArgValue { + self.to_string().into_diag_arg(&mut None) + } +} diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed b/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed index 98f26d62ab84c..762fc8422dca2 100644 --- a/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed +++ b/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed @@ -1,12 +1,12 @@ //@aux-build:proc_macros.rs #![warn(clippy::legacy_numeric_constants)] -#![expect(deprecated, renamed_and_removed_lints, clippy::no_effect, clippy::useless_attribute)] +#![expect(deprecated, clippy::no_effect, clippy::useless_attribute)] #[macro_use] extern crate proc_macros; pub mod a { - #[expect(legacy_numeric_constants)] + #[expect(clippy::legacy_numeric_constants)] pub use std::u128; } @@ -22,7 +22,7 @@ macro_rules! b { }; } -#[expect(legacy_numeric_constants)] +#[expect(clippy::legacy_numeric_constants)] use std::{f64, u8::MIN, u32, u32::MAX}; fn main() { diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants.rs b/src/tools/clippy/tests/ui/legacy_numeric_constants.rs index 98f860130947f..5188f8a9de604 100644 --- a/src/tools/clippy/tests/ui/legacy_numeric_constants.rs +++ b/src/tools/clippy/tests/ui/legacy_numeric_constants.rs @@ -1,12 +1,12 @@ //@aux-build:proc_macros.rs #![warn(clippy::legacy_numeric_constants)] -#![expect(deprecated, renamed_and_removed_lints, clippy::no_effect, clippy::useless_attribute)] +#![expect(deprecated, clippy::no_effect, clippy::useless_attribute)] #[macro_use] extern crate proc_macros; pub mod a { - #[expect(legacy_numeric_constants)] + #[expect(clippy::legacy_numeric_constants)] pub use std::u128; } @@ -22,7 +22,7 @@ macro_rules! b { }; } -#[expect(legacy_numeric_constants)] +#[expect(clippy::legacy_numeric_constants)] use std::{f64, u8::MIN, u32, u32::MAX}; fn main() { From 09a506074c2e201150ddeffb1cd5ac3f86aba0d4 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sat, 17 Oct 2026 22:13:47 +0200 Subject: [PATCH 10/21] Use LintName in LintLevelSource --- compiler/rustc_lint/src/context.rs | 8 ++---- compiler/rustc_lint/src/diagnostics.rs | 22 ++++++++------- compiler/rustc_lint/src/levels.rs | 22 ++++++--------- compiler/rustc_middle/src/lint.rs | 34 +++++++++++------------- compiler/rustc_span/src/symbol.rs | 1 + src/tools/clippy/clippy_utils/src/sym.rs | 1 - 6 files changed, 39 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 8a72c064f4823..4ec3c5f7e97c4 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -324,17 +324,13 @@ impl LintStore { } /// True if this symbol represents a lint group name. - pub fn is_lint_group(&self, lint_name: Symbol) -> bool { + pub fn is_lint_group(&self, lint_name: LintName) -> bool { debug!( "is_lint_group(lint_name={:?}, lint_groups={:?})", lint_name, self.lint_groups.keys().collect::>() ); - let lint_name_str = lint_name.as_str(); - self.lint_groups.contains_key(&LintName::from_str(lint_name_str)) || { - let warnings_name_str = crate::WARNINGS.name_lower(); - lint_name_str == warnings_name_str - } + self.lint_groups.contains_key(&lint_name) || lint_name.is(sym::warnings) } /// Checks the name of a lint for its existence, and whether it was diff --git a/compiler/rustc_lint/src/diagnostics.rs b/compiler/rustc_lint/src/diagnostics.rs index f4e111ddc6760..459e1ffc11232 100644 --- a/compiler/rustc_lint/src/diagnostics.rs +++ b/compiler/rustc_lint/src/diagnostics.rs @@ -24,14 +24,14 @@ use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; use crate::lifetime_syntax::LifetimeSyntaxCategories; #[derive(Diagnostic)] -#[diag("{$lint_level}({$lint_source}) incompatible with previous forbid", code = E0453)] +#[diag("{$lint_level}({$lint_name}) incompatible with previous forbid", code = E0453)] pub(crate) struct OverruledAttribute<'a> { #[primary_span] pub span: Span, #[label("overruled by previous forbid")] pub overruled: Span, pub lint_level: &'a str, - pub lint_source: Symbol, + pub lint_name: LintName, #[subdiagnostic] pub sub: OverruledAttributeSub, } @@ -39,7 +39,7 @@ pub(crate) struct OverruledAttribute<'a> { pub(crate) enum OverruledAttributeSub { DefaultSource { id: String }, NodeSource { span: Span, reason: Option }, - CommandLineSource { id: Symbol }, + CommandLineSource { forbidden_lint_name: LintName }, } impl Subdiagnostic for OverruledAttributeSub { @@ -55,9 +55,11 @@ impl Subdiagnostic for OverruledAttributeSub { diag.note(rationale.to_string()); } } - OverruledAttributeSub::CommandLineSource { id } => { - diag.note(msg!("`forbid` lint level was set on command line (`-F {$id}`)")); - diag.arg("id", id); + OverruledAttributeSub::CommandLineSource { forbidden_lint_name } => { + diag.note(msg!( + "`forbid` lint level was set on command line (`-F {$forbidden_lint_name}`)" + )); + diag.arg("forbidden_lint_name", forbidden_lint_name); } } } @@ -1385,12 +1387,12 @@ impl Subdiagnostic for NonBindingLetSub { // levels.rs #[derive(Diagnostic)] -#[diag("{$lint_level}({$lint_source}) incompatible with previous forbid")] +#[diag("{$lint_level}({$lint_name}) incompatible with previous forbid")] pub(crate) struct OverruledAttributeLint<'a> { #[label("overruled by previous forbid")] pub overruled: Span, pub lint_level: &'a str, - pub lint_source: Symbol, + pub lint_name: LintName, #[subdiagnostic] pub sub: OverruledAttributeSub, } @@ -1506,10 +1508,10 @@ pub(crate) struct UnknownLintFromCommandLine { } #[derive(Diagnostic)] -#[diag("{$level}({$name}) is ignored unless specified at crate level")] +#[diag("{$level}({$lint_name}) is ignored unless specified at crate level")] pub(crate) struct IgnoredUnlessCrateSpecified<'a> { pub level: &'a str, - pub name: Symbol, + pub lint_name: LintName, } // dangling.rs diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 4e82620a9af21..56a137df9ea75 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -534,8 +534,6 @@ where _ => {} }; - let lint_flag_val = Symbol::intern(lint_name_); - let Some(ids) = self.store.find_lints(lint_name) else { // errors already handled above continue; @@ -549,7 +547,7 @@ where } if self.check_gated_lint(id, DUMMY_SP, true) { - let src = LintLevelSource::CommandLine(lint_flag_val, level); + let src = LintLevelSource::CommandLine(lint_name, level); self.provider.insert(id, LevelSpec::new(level, None, src)); } } @@ -587,7 +585,7 @@ where let fcw_warning = match old_src { LintLevelSource::Default => false, LintLevelSource::Node { name, .. } => self.store.is_lint_group(name), - LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol), + LintLevelSource::CommandLine(name, _) => self.store.is_lint_group(name), }; debug!( "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}", @@ -603,8 +601,8 @@ where LintLevelSource::Node { span, reason, .. } => { OverruledAttributeSub::NodeSource { span, reason } } - LintLevelSource::CommandLine(name, _) => { - OverruledAttributeSub::CommandLineSource { id: name } + LintLevelSource::CommandLine(lint_name, _) => { + OverruledAttributeSub::CommandLineSource { forbidden_lint_name: lint_name } } }; if !fcw_warning { @@ -612,7 +610,7 @@ where span: src.span(), overruled: src.span(), lint_level: level.as_str(), - lint_source: src.name(), + lint_name: src.name(), sub, }); } else { @@ -622,7 +620,7 @@ where OverruledAttributeLint { overruled: src.span(), lint_level: level.as_str(), - lint_source: src.name(), + lint_name: src.name(), sub, }, ); @@ -849,11 +847,7 @@ where } }; - let src = LintLevelSource::Node { - name: Symbol::intern(&name.to_string()), - span: lint_span, - reason, - }; + let src = LintLevelSource::Node { name, span: lint_span, reason }; for &id in ids { if self.check_gated_lint(id, lint_span, false) { self.insert_spec(id, LevelSpec::new(level, lint_id, src)); @@ -897,7 +891,7 @@ where lint_attr_span.into(), IgnoredUnlessCrateSpecified { level: level_spec.level().as_str(), - name: lint_attr_name, + lint_name: lint_attr_name, }, ); // don't set a separate error for every lint in the group diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index bef0fdf95d24e..94acf8e303a03 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -10,7 +10,8 @@ use rustc_lint_defs::{ }; use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_session::Session; -use rustc_span::{DUMMY_SP, ExpnKind, Span, Symbol, kw}; +use rustc_span::{DUMMY_SP, ExpnKind, Span, Symbol, kw, sym}; +use rustc_structures::LintName; use tracing::instrument; use crate::ty::TyCtxt; @@ -23,7 +24,7 @@ pub enum LintLevelSource { /// Lint level was set by an attribute. Node { - name: Symbol, + name: LintName, span: Span, /// RFC 2383 reason reason: Option, @@ -32,20 +33,20 @@ pub enum LintLevelSource { /// Lint level was set by a command-line flag. /// The provided `Level` is the level specified on the command line. /// (The actual level may be lower due to `--cap-lints`.) - CommandLine(Symbol, Level), + CommandLine(LintName, Level), } impl LintLevelSource { - pub fn name(&self) -> Symbol { - match *self { - LintLevelSource::Default => kw::Default, + pub fn name(self) -> LintName { + match self { + LintLevelSource::Default => LintName { tool: None, name: kw::Default }, LintLevelSource::Node { name, .. } => name, LintLevelSource::CommandLine(name, _) => name, } } - pub fn span(&self) -> Span { - match *self { + pub fn span(self) -> Span { + match self { LintLevelSource::Default => DUMMY_SP, LintLevelSource::Node { span, .. } => span, LintLevelSource::CommandLine(_, _) => DUMMY_SP, @@ -167,10 +168,7 @@ pub fn reveal_actual_level_spec( // See tests/ui/lint/rfc-2383-lint-reason/expect_warnings.rs. Level::Expect => true, Level::ForceWarn => { - sess.dcx().span_delayed_bug( - configured_level_spec.src.span(), - "cannot --force-warn the `warnings` lint group", - ); + sess.dcx().delayed_bug("cannot --force-warn the `warnings` lint group"); false } }; @@ -303,7 +301,7 @@ fn explain_lint_level_source( }) .map(|lint_group| lint_group.name) }; - let name = lint.name_lower(); + let name = LintName::from_str(&lint.name_lower()); if let Level::Allow = level { // Do not point at `#[allow(compat_lint)]` as the reason for a compatibility lint // triggering. (#121009) @@ -323,18 +321,18 @@ fn explain_lint_level_source( } LintLevelSource::CommandLine(lint_flag_val, orig_level) => { let flag = orig_level.to_cmd_flag(); - let hyphen_case_lint_name = name.replace('_', "-"); - if lint_flag_val.as_str() == name { + let hyphen_case_lint_name = name.to_string().replace('_', "-"); + if lint_flag_val == name { err.note_once(format!( "requested on the command line with `{flag} {hyphen_case_lint_name}`" )); } else { - let hyphen_case_flag_val = lint_flag_val.as_str().replace('_', "-"); + let hyphen_case_flag_val = lint_flag_val.to_string().replace('_', "-"); err.note_once(format!( "`{flag} {hyphen_case_lint_name}` implied by `{flag} {hyphen_case_flag_val}`" )); if matches!(orig_level, Level::Warn | Level::Deny) { - let help = if name == "dead_code" { + let help = if name.is(sym::dead_code) { format!( "to override `{flag} {hyphen_case_flag_val}` add `#[expect({name})]` or `#[allow({name})]`" ) @@ -352,7 +350,7 @@ fn explain_lint_level_source( err.note(rationale.to_string()); } err.span_note_once(span, "the lint level is defined here"); - if lint_attr_name.as_str() != name { + if lint_attr_name != name { let level_str = level.as_str(); err.note_once(format!( "`#[{level_str}({name})]` implied by `#[{level_str}({lint_attr_name})]`" diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 95197e6c98a9f..90e82be83c56e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2397,6 +2397,7 @@ symbols! { vtable_align, vtable_size, warn, + warnings, wasip2, wasm, wasm32, diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 598841753c8af..fbdd3e2b7bee7 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -638,7 +638,6 @@ generate! { visit_str, visit_string, wake, - warnings, wildcard_imports, with_capacity, wrapping_neg, From a8a0f42f76bd603b9a53929b12bfe9499651d341 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 18 Oct 2026 22:31:30 +0200 Subject: [PATCH 11/21] Use LintName more --- compiler/rustc_lint/src/context.rs | 33 ++++++++++++++++-------------- compiler/rustc_lint/src/levels.rs | 21 +++++-------------- compiler/rustc_lint/src/tests.rs | 15 ++++++++------ 3 files changed, 32 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 4ec3c5f7e97c4..654fee2f172dc 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -115,7 +115,7 @@ enum TargetLint { } struct LintAlias { - name: &'static str, + name: LintName, /// Whether deprecation warnings should be suppressed for this alias. silent: bool, } @@ -242,17 +242,17 @@ impl LintStore { } } - fn insert_group(&mut self, name: &'static str, group: LintGroup) { - let previous = self.lint_groups.insert(LintName::from_str(name), group); + fn insert_group(&mut self, name: LintName, group: LintGroup) { + let previous = self.lint_groups.insert(name, group); if previous.is_some() { - bug!("group {name:?} already exists"); + bug!("group {name} already exists"); } } - pub fn register_group_alias(&mut self, group_name: &'static str, alias: &'static str) { - let Some(LintGroup { lint_ids, .. }) = - self.lint_groups.get(&LintName::from_str(group_name)) - else { + pub fn register_group_alias(&mut self, group_name: &str, alias: &str) { + let group_name = LintName::from_str(group_name); + let alias = LintName::from_str(alias); + let Some(LintGroup { lint_ids, .. }) = self.lint_groups.get(&group_name) else { bug!("group alias {alias:?} points to unregistered group {group_name:?}") }; @@ -269,11 +269,13 @@ impl LintStore { pub fn register_group( &mut self, is_externally_loaded: bool, - name: &'static str, - deprecated_name: Option<&'static str>, + name: &str, + deprecated_name: Option<&str>, to: Vec, ) { + let name = LintName::from_str(name); if let Some(deprecated) = deprecated_name { + let deprecated = LintName::from_str(deprecated); self.insert_group( deprecated, LintGroup { @@ -299,11 +301,12 @@ impl LintStore { /// This lint has been renamed; warn about using the new name and apply the lint. #[track_caller] pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { + let old_name = LintName::from_str(old_name); let new_name = LintName::from_str(new_name); let Some(&Id(target)) = self.by_name.get(&new_name) else { bug!("invalid lint renaming of {old_name} to {new_name}"); }; - self.by_name.insert(LintName::from_str(old_name), Renamed(new_name, target)); + self.by_name.insert(old_name, Renamed(new_name, target)); } pub fn register_removed(&mut self, name: &str, reason: &str) { @@ -376,8 +379,8 @@ impl LintStore { }; } Some(LintGroup { lint_ids, depr, .. }) => { - return if let &Some(LintAlias { name, silent: false }) = depr { - CheckLintNameResult::Tool(lint_ids, Some(LintName::from_str(name))) + return if let Some(LintAlias { name, silent: false }) = depr { + CheckLintNameResult::Tool(lint_ids, Some(*name)) } else { CheckLintNameResult::Tool(lint_ids, None) }; @@ -396,8 +399,8 @@ impl LintStore { None => self.no_lint_suggestion(lint_name), Some(LintGroup { lint_ids, depr, .. }) => { // Check if the lint group name is deprecated - if let &Some(LintAlias { name, silent: false }) = depr { - CheckLintNameResult::Tool(lint_ids, Some(LintName::from_str(name))) + if let Some(LintAlias { name, silent: false }) = depr { + CheckLintNameResult::Tool(lint_ids, Some(*name)) } else { CheckLintNameResult::Ok(lint_ids) } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 56a137df9ea75..13487b6dd4c71 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -481,15 +481,15 @@ where } fn add_command_line(&mut self) { - for &(ref lint_name_, level) in &self.sess.opts.lint_opts { + for &(ref lint_name, level) in &self.sess.opts.lint_opts { + let lint_name = LintName::from_str(lint_name); // Checks the validity of lint names derived from the command line. - let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name_); - if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn) { + if lint_name.is(sym::warnings) && matches!(level, Level::ForceWarn) { self.sess .dcx() .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() }); } - let lint_name = LintName::from_str(lint_name_); + match self.store.check_lint_name(lint_name, self.registered_lint_tools) { CheckLintNameResult::Renamed(replacement) => { let suggestion = RenamedLintSuggestion::WithoutSpan { replacement }; @@ -527,7 +527,7 @@ where } CheckLintNameResult::NoTool => { self.sess.dcx().emit_err(CheckNameUnknownTool { - tool_name: tool_name.unwrap(), + tool_name: lint_name.tool.unwrap(), sub: RequestedLevel { level, lint_name }, }); } @@ -999,14 +999,3 @@ where pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { shallow_lint_levels_on, skippable_lints, ..*providers }; } - -pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option, &str) { - match lint_name.split_once("::") { - Some((tool_name, lint_name)) => { - let tool_name = Symbol::intern(tool_name); - - (Some(tool_name), lint_name) - } - None => (None, lint_name), - } -} diff --git a/compiler/rustc_lint/src/tests.rs b/compiler/rustc_lint/src/tests.rs index b3c91583914a1..b4f789564600d 100644 --- a/compiler/rustc_lint/src/tests.rs +++ b/compiler/rustc_lint/src/tests.rs @@ -1,21 +1,24 @@ #![allow(rustc::symbol_intern_string_literal)] -use rustc_span::{Symbol, create_default_session_globals_then}; +use rustc_span::{Symbol, create_default_session_globals_then, sym}; +use rustc_structures::LintName; use crate::builtin::is_hexagon_register_span; -use crate::levels::parse_lint_and_tool_name; #[test] fn parse_lint_no_tool() { create_default_session_globals_then(|| { - assert_eq!(parse_lint_and_tool_name("foo"), (None, "foo")) + assert_eq!(LintName::from_str("foo"), LintName { tool: None, name: Symbol::intern("foo") }) }); } #[test] fn parse_lint_with_tool() { create_default_session_globals_then(|| { - assert_eq!(parse_lint_and_tool_name("clippy::foo"), (Some(Symbol::intern("clippy")), "foo")) + assert_eq!( + LintName::from_str("clippy::foo"), + LintName { tool: Some(sym::clippy), name: Symbol::intern("foo") } + ) }); } @@ -23,8 +26,8 @@ fn parse_lint_with_tool() { fn parse_lint_multiple_path() { create_default_session_globals_then(|| { assert_eq!( - parse_lint_and_tool_name("clippy::foo::bar"), - (Some(Symbol::intern("clippy")), "foo::bar") + LintName::from_str("clippy::foo::bar"), + LintName { tool: Some(sym::clippy), name: Symbol::intern("foo::bar") } ) }); } From 232a750cc8fb16bc508a4f7a95434045073304c0 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 18 Oct 2026 23:20:44 +0200 Subject: [PATCH 12/21] return tool_name in NoTool error --- compiler/rustc_lint/src/context.rs | 4 ++-- compiler/rustc_lint/src/levels.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 654fee2f172dc..01d121c1e7f73 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -132,7 +132,7 @@ pub enum CheckLintNameResult<'a> { /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. NoLint(Option<(LintName, bool)>), /// The lint refers to a tool that has not been registered. - NoTool, + NoTool(Symbol), /// The lint has been renamed to a new name. Renamed(LintName), /// The lint has been removed due to the given reason. @@ -356,7 +356,7 @@ impl LintStore { && tool_name != sym::rustdoc && !registered_lint_tools.contains(&Ident::with_dummy_span(tool_name)) { - return CheckLintNameResult::NoTool; + return CheckLintNameResult::NoTool(tool_name); } } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 13487b6dd4c71..ededeb4196078 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -525,9 +525,9 @@ where }; self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint); } - CheckLintNameResult::NoTool => { + CheckLintNameResult::NoTool(tool_name) => { self.sess.dcx().emit_err(CheckNameUnknownTool { - tool_name: lint_name.tool.unwrap(), + tool_name, sub: RequestedLevel { level, lint_name }, }); } @@ -781,10 +781,10 @@ where continue; } - CheckLintNameResult::NoTool => { + CheckLintNameResult::NoTool(tool_name) => { sess.dcx().emit_err(UnknownToolInScopedLint { span: Some(tool_span.unwrap()), - tool_name: tool.unwrap(), + tool_name, lint_name, is_nightly_build: sess.is_nightly_build(), }); From 361557ad8780de87226c7c226aa42a0df283e05a Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 26 Oct 2026 23:39:59 +0100 Subject: [PATCH 13/21] clarify lint emptyness --- compiler/rustc_structures/src/lint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_structures/src/lint.rs b/compiler/rustc_structures/src/lint.rs index 9ae557ee2ff00..b76390c47ac4f 100644 --- a/compiler/rustc_structures/src/lint.rs +++ b/compiler/rustc_structures/src/lint.rs @@ -39,7 +39,7 @@ impl LintName { LintName { tool, name } } - /// Panics if `lint` is empty. + /// `lint` must not be empty. pub fn from_str(lint: &str) -> LintName { if lint.is_empty() { if cfg!(debug_assertions) { From 5156c51ebd2cce431703b2a1c6c783062d4d7279 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 9 Jul 2026 10:28:02 +0200 Subject: [PATCH 14/21] Move lang item collection to the resolver --- Cargo.lock | 2 +- compiler/rustc_interface/src/passes.rs | 1 + compiler/rustc_middle/src/middle/resolve.rs | 3 +- compiler/rustc_middle/src/queries.rs | 1 - compiler/rustc_passes/Cargo.toml | 1 - compiler/rustc_passes/src/diagnostics.rs | 131 ------- compiler/rustc_passes/src/lang_items.rs | 332 ------------------ compiler/rustc_passes/src/lib.rs | 3 - compiler/rustc_resolve/Cargo.toml | 1 + compiler/rustc_resolve/src/diagnostics/mod.rs | 133 ++++++- compiler/rustc_resolve/src/late.rs | 44 +++ compiler/rustc_resolve/src/late/lang_items.rs | 256 ++++++++++++++ .../src/late}/weak_lang_items.rs | 0 compiler/rustc_resolve/src/lib.rs | 19 +- tests/ui/lang-items/issue-83471.stderr | 18 +- tests/ui/panic-handler/weak-lang-item.rs | 2 + tests/ui/panic-handler/weak-lang-item.stderr | 11 +- 17 files changed, 475 insertions(+), 483 deletions(-) delete mode 100644 compiler/rustc_passes/src/lang_items.rs create mode 100644 compiler/rustc_resolve/src/late/lang_items.rs rename compiler/{rustc_passes/src => rustc_resolve/src/late}/weak_lang_items.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 5ff455c57f056..2dace16bea1f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4662,7 +4662,6 @@ dependencies = [ "rustc_ast_lowering", "rustc_attr_ir", "rustc_attr_parsing", - "rustc_crate_store", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -4787,6 +4786,7 @@ dependencies = [ "rustc_ast_pretty", "rustc_attr_ir", "rustc_attr_parsing", + "rustc_crate_store", "rustc_data_structures", "rustc_errors", "rustc_expand", diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 73bebb22bc508..978eddc5e143d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -900,6 +900,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { providers.queries.analysis = analysis; providers.queries.resolver_for_lowering_raw = resolver_for_lowering_raw; providers.queries.stripped_cfg_items = |tcx, _| &tcx.resolutions(()).stripped_cfg_items[..]; + providers.queries.get_lang_items = |tcx, ()| &tcx.resolutions(()).lang_items; providers.queries.resolutions = |tcx, ()| tcx.resolver_for_lowering_raw(()).2; providers.queries.early_lint_checks = early_lint_checks; providers.queries.env_var_os = env_var_os; diff --git a/compiler/rustc_middle/src/middle/resolve.rs b/compiler/rustc_middle/src/middle/resolve.rs index 1c92f0ee2a8b6..54357dd4fff12 100644 --- a/compiler/rustc_middle/src/middle/resolve.rs +++ b/compiler/rustc_middle/src/middle/resolve.rs @@ -3,7 +3,7 @@ use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, NodeId}; -use rustc_attr_ir::StrippedCfgItem; +use rustc_attr_ir::{LanguageItems, StrippedCfgItem}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::steal::Steal; use rustc_data_structures::unord::{UnordMap, UnordSet}; @@ -197,6 +197,7 @@ pub struct ResolverGlobalCtxt { pub delegation_infos: FxIndexMap, pub delegation_inherent_fn_map: FxIndexMap>, + pub lang_items: LanguageItems, } #[derive(Debug)] diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 56889f84f38d6..90a5e03d3a21f 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -2295,7 +2295,6 @@ rustc_queries! { /// Returns the lang items defined in all crates by loading them from metadata of dependencies /// and collecting the ones from the current crate. query get_lang_items(_: ()) -> &'tcx LanguageItems { - arena_cache eval_always desc { "calculating the lang items map" } } diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index 5e2fa8647671d..827e2b1f1bbc9 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -10,7 +10,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } -rustc_crate_store = { path = "../rustc_crate_store" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index e090fd0669cd8..5dc29956ff255 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -9,7 +9,6 @@ use rustc_middle::ty::Ty; use rustc_span::{DUMMY_SP, Ident, Span, Symbol}; use crate::check_attr::ProcMacroKind; -use crate::lang_items::Duplicate; #[derive(Diagnostic)] #[diag("`{$no_mangle_attr}` attribute may not be used in combination with `{$export_name_attr}`")] @@ -180,30 +179,6 @@ pub(crate) struct InvalidMayDangle { pub attr_span: Span, } -#[derive(Diagnostic)] -#[diag("`#[panic_handler]` function required, but not found")] -pub(crate) struct MissingPanicHandler; - -#[derive(Diagnostic)] -#[diag("unwinding panics are not supported without std")] -#[help("using nightly cargo, use -Zbuild-std with panic=\"abort\" to avoid unwinding")] -#[note( - "since the core library is usually precompiled with panic=\"unwind\", rebuilding your crate with panic=\"abort\" may not be enough to fix the problem" -)] -pub(crate) struct PanicUnwindWithoutStd; - -#[derive(Diagnostic)] -#[diag("lang item required, but not found: `{$name}`")] -#[note( - "this can occur when a binary crate with `#![no_std]` is compiled for a target where `{$name}` is defined in the standard library" -)] -#[help( - "you may be able to compile for a target that doesn't need `{$name}`, specify a target with `--target` or in `.cargo/config`" -)] -pub(crate) struct MissingLangItem { - pub name: Symbol, -} - #[derive(Diagnostic)] #[diag("duplicate diagnostic item in crate `{$crate_name}`: `{$name}`")] pub(crate) struct DuplicateDiagnosticItemInCrate { @@ -370,112 +345,6 @@ impl<'a> Diagnostic<'a> for NoMainErr { diag } } -pub(crate) struct DuplicateLangItem { - pub local_span: Option, - pub lang_item_name: Symbol, - pub crate_name: Symbol, - pub dependency_of: Option, - pub is_local: bool, - pub path: String, - pub first_defined_span: Option, - pub orig_crate_name: Option, - pub orig_dependency_of: Option, - pub orig_is_local: bool, - pub orig_path: String, - pub(crate) duplicate: Duplicate, -} - -impl Diagnostic<'_> for DuplicateLangItem { - #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { - let mut diag = Diag::new( - dcx, - level, - match self.duplicate { - Duplicate::Plain => msg!("found duplicate lang item `{$lang_item_name}`"), - Duplicate::Crate => { - msg!("duplicate lang item in crate `{$crate_name}`: `{$lang_item_name}`") - } - Duplicate::CrateDepends => msg!( - "duplicate lang item in crate `{$crate_name}` (which `{$dependency_of}` depends on): `{$lang_item_name}`" - ), - }, - ); - diag.code(E0152); - diag.arg("lang_item_name", self.lang_item_name); - diag.arg("crate_name", self.crate_name); - if let Some(dependency_of) = self.dependency_of { - diag.arg("dependency_of", dependency_of); - } - diag.arg("path", self.path); - if let Some(orig_crate_name) = self.orig_crate_name { - diag.arg("orig_crate_name", orig_crate_name); - } - if let Some(orig_dependency_of) = self.orig_dependency_of { - diag.arg("orig_dependency_of", orig_dependency_of); - } - diag.arg("orig_path", self.orig_path); - if let Some(span) = self.local_span { - diag.span(span); - } - if let Some(span) = self.first_defined_span { - diag.span_note(span, msg!("the lang item is first defined here")); - } else { - if self.orig_dependency_of.is_none() { - diag.note(msg!("the lang item is first defined in crate `{$orig_crate_name}`")); - } else { - diag.note(msg!("the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on)")); - } - - if self.orig_is_local { - diag.note(msg!("first definition in the local crate (`{$orig_crate_name}`)")); - } else { - diag.note(msg!( - "first definition in `{$orig_crate_name}` loaded from {$orig_path}" - )); - } - - if self.is_local { - diag.note(msg!("second definition in the local crate (`{$crate_name}`)")); - } else { - diag.note(msg!("second definition in `{$crate_name}` loaded from {$path}")); - } - } - diag - } -} - -#[derive(Diagnostic)] -#[diag("`{$name}` lang item must be applied to a {$kind} with {$at_least -> - [true] at least {$num} - *[false] {$num} - } generic {$num -> - [one] argument - *[other] arguments - }", code = E0718)] -pub(crate) struct IncorrectTarget<'a> { - #[primary_span] - pub span: Span, - #[label( - "this {$kind} has {$actual_num} generic {$actual_num -> - [one] argument - *[other] arguments - }" - )] - pub generics_span: Span, - pub name: &'a str, // cannot be symbol because it renders e.g. `r#fn` instead of `fn` - pub kind: &'static str, - pub num: usize, - pub actual_num: usize, - pub at_least: bool, -} - -#[derive(Diagnostic)] -#[diag("lang items are not allowed in stable dylibs")] -pub(crate) struct IncorrectCrateType { - #[primary_span] - pub span: Span, -} #[derive(Diagnostic)] #[diag( diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs deleted file mode 100644 index ab7b0f375e609..0000000000000 --- a/compiler/rustc_passes/src/lang_items.rs +++ /dev/null @@ -1,332 +0,0 @@ -//! Detecting lang items. -//! -//! Language items are items that represent concepts intrinsic to the language -//! itself. Examples are: -//! -//! * Traits that specify "kinds"; e.g., `Sync`, `Send`. -//! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. -//! * Functions called by the compiler itself. - -use rustc_ast as ast; -use rustc_ast::visit; -use rustc_attr_ir::lang_items::{GenericRequirement, LangItem, LanguageItems}; -use rustc_attr_ir::target::Target; -use rustc_crate_store::ExternCrate; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::middle::resolve::ResolverAstLowering; -use rustc_middle::query::Providers; -use rustc_middle::ty::TyCtxt; -use rustc_span::{Span, Symbol, sym}; - -use crate::diagnostics::{DuplicateLangItem, IncorrectCrateType, IncorrectTarget}; -use crate::weak_lang_items; - -pub(crate) enum Duplicate { - Plain, - Crate, - CrateDepends, -} - -struct LanguageItemCollector<'ast, 'tcx> { - items: LanguageItems, - tcx: TyCtxt<'tcx>, - resolver: &'ast ResolverAstLowering<'tcx>, - parent_item: Option<&'ast ast::Item>, -} - -impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { - fn new( - tcx: TyCtxt<'tcx>, - resolver: &'ast ResolverAstLowering<'tcx>, - ) -> LanguageItemCollector<'ast, 'tcx> { - LanguageItemCollector { tcx, resolver, items: LanguageItems::new(), parent_item: None } - } - - fn check_for_lang( - &mut self, - actual_target: Target, - def_id: LocalDefId, - attrs: &'ast [ast::Attribute], - item_span: Span, - generics: Option<&'ast ast::Generics>, - ) { - if let Some((name, attr_span)) = extract_ast(attrs) { - match LangItem::from_name(name) { - // Known lang item - Some(lang_item) => { - if actual_target != lang_item.target() { - // `#[panic_handler]` is turned into `#[lang = "panic_impl"]`, but in contrast - // to the actual lang item attr, is applied to `Fn` instead of `ForeignFn`. - if !(lang_item.is_weak() - && actual_target == Target::Fn - && lang_item.target() == Target::ForeignFn - && matches!(lang_item, LangItem::PanicImpl)) - { - self.tcx - .dcx() - .delayed_bug(format!("lang item target is checked in attribute parser: {:?} has {} but expected {}", def_id, actual_target, lang_item.target())); - return; - } - } - // Weak lang items are handled separately - if lang_item.is_weak() && actual_target == Target::ForeignFn { - self.items.missing.push(lang_item); - } else { - // Weak only lang items are always handled here - self.collect_item_extended( - lang_item, - def_id, - item_span, - attr_span, - generics, - actual_target, - ); - } - } - // Unknown lang item. - _ => { - self.tcx.dcx().delayed_bug("unknown lang item"); - } - } - } - } - - fn collect_item(&mut self, lang_item: LangItem, item_def_id: DefId, item_span: Option) { - // Check for duplicates. - if let Some(original_def_id) = self.items.get(lang_item) - && original_def_id != item_def_id - { - let lang_item_name = lang_item.name(); - let crate_name = self.tcx.crate_name(item_def_id.krate); - let mut dependency_of = None; - let is_local = item_def_id.is_local(); - let path = if is_local { - String::new() - } else { - self.tcx - .crate_extern_paths(item_def_id.krate) - .iter() - .map(|p| p.display().to_string()) - .collect::>() - .join(", ") - }; - - let mut orig_crate_name = None; - let mut orig_dependency_of = None; - let orig_is_local = original_def_id.is_local(); - let orig_path = if orig_is_local { - String::new() - } else { - self.tcx - .crate_extern_paths(original_def_id.krate) - .iter() - .map(|p| p.display().to_string()) - .collect::>() - .join(", ") - }; - - if !original_def_id.is_local() { - orig_crate_name = Some(self.tcx.crate_name(original_def_id.krate)); - if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) = - self.tcx.extern_crate(original_def_id.krate) - { - orig_dependency_of = Some(self.tcx.crate_name(*inner_dependency_of)); - } - } - - let duplicate = if item_span.is_some() { - Duplicate::Plain - } else { - match self.tcx.extern_crate(item_def_id.krate) { - Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => { - dependency_of = Some(self.tcx.crate_name(*inner_dependency_of)); - Duplicate::CrateDepends - } - _ => Duplicate::Crate, - } - }; - - // When there's a duplicate lang item, something went very wrong and there's no value - // in recovering or doing anything. Give the user the one message to let them debug the - // mess they created and then wish them farewell. - self.tcx.dcx().emit_fatal(DuplicateLangItem { - local_span: item_span, - lang_item_name, - crate_name, - dependency_of, - is_local, - path, - first_defined_span: original_def_id.as_local().map(|did| self.tcx.source_span(did)), - orig_crate_name, - orig_dependency_of, - orig_is_local, - orig_path, - duplicate, - }); - } else { - // Matched. - self.items.set(lang_item, item_def_id); - } - } - - // Like collect_item() above, but also checks whether the lang item is declared - // with the right number of generic arguments. - fn collect_item_extended( - &mut self, - lang_item: LangItem, - item_def_id: LocalDefId, - item_span: Span, - attr_span: Span, - generics: Option<&'ast ast::Generics>, - target: Target, - ) { - let name = lang_item.name(); - - if let Some(generics) = generics { - // Now check whether the lang_item has the expected number of generic - // arguments. Generally speaking, binary and indexing operations have - // one (for the RHS/index), unary operations have none, the closure - // traits have one for the argument list, coroutines have one for the - // resume argument, and ordering/equality relations have one for the RHS - // Some other types like Box and various unsizing-related traits - // have minimum requirements. - - // FIXME: This still doesn't count, e.g., elided lifetimes and APITs. - let mut actual_num = generics.params.len(); - if target.is_associated_item() { - actual_num += self - .parent_item - .unwrap() - .opt_generics() - .map_or(0, |generics| generics.params.len()); - } - - let mut at_least = false; - let required = match lang_item.required_generics() { - GenericRequirement::Exact(num) if num != actual_num => Some(num), - GenericRequirement::Minimum(num) if actual_num < num => { - at_least = true; - Some(num) - } - // If the number matches, or there is no requirement, handle it normally - _ => None, - }; - - if let Some(num) = required { - // We are issuing E0718 "incorrect target" here, because while the - // item kind of the target is correct, the target is still wrong - // because of the wrong number of generic arguments. - self.tcx.dcx().emit_err(IncorrectTarget { - span: attr_span, - generics_span: generics.span, - name: name.as_str(), - kind: target.name(), - num, - actual_num, - at_least, - }); - - // return early to not collect the lang item - return; - } - } - - if self.tcx.crate_types().contains(&rustc_structures::CrateType::Sdylib) { - self.tcx.dcx().emit_err(IncorrectCrateType { span: attr_span }); - } - - self.collect_item(lang_item, item_def_id.to_def_id(), Some(item_span)); - } -} - -/// Traverses and collects all the lang items in all crates. -fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems { - let (resolver, krate) = tcx.resolver_for_lowering(); - let resolver = &*resolver.borrow(); - let krate = &*krate.borrow(); - - // Initialize the collector. - let mut collector = LanguageItemCollector::new(tcx, resolver); - - // Collect lang items in other crates. - for &cnum in tcx.used_crates(()).iter() { - for &(def_id, lang_item) in tcx.defined_lang_items(cnum).iter() { - collector.collect_item(lang_item, def_id, None); - } - } - - // Collect lang items local to this crate. - visit::Visitor::visit_crate(&mut collector, krate); - - // Find all required but not-yet-defined lang items. - weak_lang_items::check_crate(tcx, &mut collector.items); - - // Return all the lang items that were found. - collector.items -} - -impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { - fn visit_item(&mut self, i: &'ast ast::Item) { - let target = Target::from_ast_item(i); - - self.check_for_lang( - target, - self.resolver.owners[&i.id].def_id, - &i.attrs, - i.span, - i.opt_generics(), - ); - - let parent_item = self.parent_item.replace(i); - visit::walk_item(self, i); - self.parent_item = parent_item; - } - - fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) { - self.check_for_lang( - Target::from_foreign_item_kind(&i.kind), - self.resolver.owners[&i.id].def_id, - &i.attrs, - i.span, - None, - ); - } - - fn visit_variant(&mut self, variant: &'ast ast::Variant) { - self.check_for_lang( - Target::Variant, - self.resolver.owners[&self.parent_item.unwrap().id].node_id_to_def_id[&variant.id], - &variant.attrs, - variant.span, - None, - ); - } - - fn visit_assoc_item(&mut self, i: &'ast ast::AssocItem, ctxt: visit::AssocCtxt) { - let target = Target::from_assoc_item_kind(&i.kind, ctxt); - let generics = i.opt_generics(); - - self.check_for_lang(target, self.resolver.owners[&i.id].def_id, &i.attrs, i.span, generics); - - visit::walk_assoc_item(self, i, ctxt); - } -} - -/// Extracts the first `lang = "$name"` out of a list of attributes. -/// The `#[panic_handler]` attribute is also extracted out when found. -/// -/// This function is used for `ast::Attribute`, for `rustc_attr_ir::Attribute` -/// use `find_attr!(attrs, Lang(lang_item))`. -pub(crate) fn extract_ast(attrs: &[rustc_ast::ast::Attribute]) -> Option<(Symbol, Span)> { - attrs.iter().find_map(|attr| { - Some(match attr { - _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span), - _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span), - _ => return None, - }) - }) -} - -pub(crate) fn provide(providers: &mut Providers) { - providers.get_lang_items = get_lang_items; -} diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 77bd179e8dd80..d37d1bea932fe 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -25,13 +25,11 @@ pub mod entry; mod fake_doc_items; pub mod hir_id_validator; pub mod input_stats; -mod lang_items; pub mod layout_test; mod lib_features; mod reachable; pub mod stability; mod upvars; -mod weak_lang_items; pub fn provide(providers: &mut Providers) { canonical_symbols::provide(providers); @@ -40,7 +38,6 @@ pub fn provide(providers: &mut Providers) { debugger_visualizer::provide(providers); diagnostic_items::provide(providers); entry::provide(providers); - lang_items::provide(providers); lib_features::provide(providers); reachable::provide(providers); stability::provide(providers); diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index 36db6adc7f920..d28936f8624fd 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -15,6 +15,7 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } +rustc_crate_store = { path = "../rustc_crate_store" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } diff --git a/compiler/rustc_resolve/src/diagnostics/mod.rs b/compiler/rustc_resolve/src/diagnostics/mod.rs index 01ec5bcb6cf29..4dc4572ad5176 100644 --- a/compiler/rustc_resolve/src/diagnostics/mod.rs +++ b/compiler/rustc_resolve/src/diagnostics/mod.rs @@ -8,7 +8,7 @@ use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Spanned, Symbol}; use crate::Res; -use crate::late::PatternSource; +use crate::late::{Duplicate, PatternSource}; pub(crate) mod impls; @@ -1785,3 +1785,134 @@ pub(crate) struct ToolAttrAmbiguity { pub attr_path: String, pub tool: Symbol, } + +pub(crate) struct DuplicateLangItem { + pub local_span: Option, + pub lang_item_name: Symbol, + pub crate_name: Symbol, + pub dependency_of: Option, + pub is_local: bool, + pub path: String, + pub first_defined_span: Option, + pub orig_crate_name: Option, + pub orig_dependency_of: Option, + pub orig_is_local: bool, + pub orig_path: String, + pub(crate) duplicate: Duplicate, +} + +impl Diagnostic<'_> for DuplicateLangItem { + #[track_caller] + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_> { + let mut diag = Diag::new( + dcx, + level, + match self.duplicate { + Duplicate::Plain => msg!("found duplicate lang item `{$lang_item_name}`"), + Duplicate::Crate => { + msg!("duplicate lang item in crate `{$crate_name}`: `{$lang_item_name}`") + } + Duplicate::CrateDepends => msg!( + "duplicate lang item in crate `{$crate_name}` (which `{$dependency_of}` depends on): `{$lang_item_name}`" + ), + }, + ); + diag.code(E0152); + diag.arg("lang_item_name", self.lang_item_name); + diag.arg("crate_name", self.crate_name); + if let Some(dependency_of) = self.dependency_of { + diag.arg("dependency_of", dependency_of); + } + diag.arg("path", self.path); + if let Some(orig_crate_name) = self.orig_crate_name { + diag.arg("orig_crate_name", orig_crate_name); + } + if let Some(orig_dependency_of) = self.orig_dependency_of { + diag.arg("orig_dependency_of", orig_dependency_of); + } + diag.arg("orig_path", self.orig_path); + if let Some(span) = self.local_span { + diag.span(span); + } + if let Some(span) = self.first_defined_span { + diag.span_note(span, msg!("the lang item is first defined here")); + } else { + if self.orig_dependency_of.is_none() { + diag.note(msg!("the lang item is first defined in crate `{$orig_crate_name}`")); + } else { + diag.note(msg!("the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on)")); + } + + if self.orig_is_local { + diag.note(msg!("first definition in the local crate (`{$orig_crate_name}`)")); + } else { + diag.note(msg!( + "first definition in `{$orig_crate_name}` loaded from {$orig_path}" + )); + } + + if self.is_local { + diag.note(msg!("second definition in the local crate (`{$crate_name}`)")); + } else { + diag.note(msg!("second definition in `{$crate_name}` loaded from {$path}")); + } + } + diag + } +} + +#[derive(Diagnostic)] +#[diag("`#[panic_handler]` function required, but not found")] +pub(crate) struct MissingPanicHandler; + +#[derive(Diagnostic)] +#[diag("unwinding panics are not supported without std")] +#[help("using nightly cargo, use -Zbuild-std with panic=\"abort\" to avoid unwinding")] +#[note( + "since the core library is usually precompiled with panic=\"unwind\", rebuilding your crate with panic=\"abort\" may not be enough to fix the problem" +)] +pub(crate) struct PanicUnwindWithoutStd; + +#[derive(Diagnostic)] +#[diag("lang items are not allowed in stable dylibs")] +pub(crate) struct IncorrectCrateType { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag("`{$name}` lang item must be applied to a {$kind} with {$at_least -> + [true] at least {$num} + *[false] {$num} + } generic {$num -> + [one] argument + *[other] arguments + }", code = E0718)] +pub(crate) struct IncorrectTarget<'a> { + #[primary_span] + pub span: Span, + #[label( + "this {$kind} has {$actual_num} generic {$actual_num -> + [one] argument + *[other] arguments + }" + )] + pub generics_span: Span, + pub name: &'a str, // cannot be symbol because it renders e.g. `r#fn` instead of `fn` + pub kind: &'static str, + pub num: usize, + pub actual_num: usize, + pub at_least: bool, +} + +#[derive(Diagnostic)] +#[diag("lang item required, but not found: `{$name}`")] +#[note( + "this can occur when a binary crate with `#![no_std]` is compiled for a target where `{$name}` is defined in the standard library" +)] +#[help( + "you may be able to compile for a target that doesn't need `{$name}`, specify a target with `--target` or in `.cargo/config`" +)] +pub(crate) struct MissingLangItem { + pub name: Symbol, +} diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index bffcf60a920ff..13a8d8b8f25e9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -17,6 +17,7 @@ use rustc_ast::visit::{ AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list, }; use rustc_ast::*; +use rustc_attr_ir::target::Target; use rustc_data_structures::either::Either; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::unord::{UnordMap, UnordSet}; @@ -50,8 +51,11 @@ use crate::{ }; mod diagnostics; +mod lang_items; +pub(super) mod weak_lang_items; use diagnostics::{ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime}; +pub(super) use lang_items::{Duplicate, collect_item}; #[derive(Copy, Clone, Debug)] struct BindingInfo { @@ -1130,6 +1134,13 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc with_owner(self, foreign_item.id, |this| { this.resolve_doc_links(&foreign_item.attrs, MaybeExported::Ok(foreign_item.id)); let def_kind = this.r.tcx.def_kind(this.r.current_owner.def_id); + this.check_for_lang( + Target::from_foreign_item_kind(&foreign_item.kind), + this.r.current_owner.def_id, + &foreign_item.attrs, + foreign_item.span, + None, + ); match foreign_item.kind { ForeignItemKind::TyAlias(TyAlias { ref generics, .. }) => { this.with_generic_param_rib( @@ -1549,6 +1560,13 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc self.visit_vis(&v.vis); self.visit_ident(&v.ident); self.visit_variant_data(&v.data); + self.check_for_lang( + Target::Variant, + self.r.current_owner.node_id_to_def_id[&v.id], + &v.attrs, + v.span, + None, + ); if let Some(discr) = &v.disr_expr { self.resolve_anon_const(discr, AnonConstKind::EnumDiscriminant); } @@ -2989,6 +3007,15 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { debug!("(resolving item) resolving {:?} ({:?})", item.kind.ident(), item.kind); let def_kind = self.r.tcx.def_kind(self.r.current_owner.def_id); + + self.check_for_lang( + Target::from_ast_item(item), + self.r.current_owner.def_id, + &item.attrs, + item.span, + item.opt_generics(), + ); + match &item.kind { ItemKind::TyAlias(TyAlias { generics, .. }) => { self.with_generic_param_rib( @@ -3588,6 +3615,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }; self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); + self.check_for_lang( + Target::from_assoc_item_kind(&item.kind, AssocCtxt::Trait), + self.r.current_owner.def_id, + &item.attrs, + item.span, + item.opt_generics(), + ); match &item.kind { AssocItemKind::Const(ast::ConstItem { generics, ty, body, define_opaque, .. }) => { self.with_generic_param_rib( @@ -3819,6 +3853,16 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.resolve_doc_links(&item.attrs, MaybeExported::ImplItem(trait_id.ok_or(&item.vis))); let prev = self.diag_metadata.current_impl_item.take(); self.diag_metadata.current_impl_item = Some(&item); + self.check_for_lang( + Target::from_assoc_item_kind( + &item.kind, + AssocCtxt::Impl { of_trait: is_in_trait_impl }, + ), + self.r.current_owner.def_id, + &item.attrs, + item.span, + item.opt_generics(), + ); match &item.kind { AssocItemKind::Const(ast::ConstItem { ident, diff --git a/compiler/rustc_resolve/src/late/lang_items.rs b/compiler/rustc_resolve/src/late/lang_items.rs new file mode 100644 index 0000000000000..b53928df24dc8 --- /dev/null +++ b/compiler/rustc_resolve/src/late/lang_items.rs @@ -0,0 +1,256 @@ +//! Detecting lang items. +//! +//! Language items are items that represent concepts intrinsic to the language +//! itself. Examples are: +//! +//! * Traits that specify "kinds"; e.g., `Sync`, `Send`. +//! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. +//! * Functions called by the compiler itself. + +use rustc_ast as ast; +use rustc_attr_ir::target::Target; +use rustc_attr_ir::{GenericRequirement, LangItem, LanguageItems}; +use rustc_crate_store::ExternCrate; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::ty::TyCtxt; +use rustc_span::{Span, sym}; +use rustc_structures::CrateType; + +use crate::diagnostics::{DuplicateLangItem, IncorrectCrateType, IncorrectTarget}; +use crate::late::{LateResolutionVisitor, RibKind}; + +pub(crate) enum Duplicate { + Plain, + Crate, + CrateDepends, +} + +impl<'tcx> LateResolutionVisitor<'_, '_, '_, 'tcx> { + pub(super) fn check_for_lang( + &mut self, + actual_target: Target, + def_id: LocalDefId, + attrs: &[ast::Attribute], + item_span: Span, + generics: Option<&ast::Generics>, + ) { + let attrs = attrs.iter().find_map(|attr| { + Some(match attr { + _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span), + _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span), + _ => return None, + }) + }); + if let Some((name, attr_span)) = attrs { + match LangItem::from_name(name) { + // Known lang item + Some(lang_item) => { + if actual_target != lang_item.target() { + // `#[panic_handler]` is turned into `#[lang = "panic_impl"]`, but in contrast + // to the actual lang item attr, is applied to `Fn` instead of `ForeignFn`. + if !(lang_item.is_weak() + && actual_target == Target::Fn + && lang_item.target() == Target::ForeignFn + && matches!(lang_item, LangItem::PanicImpl)) + { + self.r.tcx + .dcx() + .delayed_bug(format!("lang item target is checked in attribute parser: {:?} has {} but expected {}", def_id, actual_target, lang_item.target())); + return; + } + } + // Weak lang items are handled separately + if lang_item.is_weak() && actual_target == Target::ForeignFn { + self.r.lang_items.missing.push(lang_item); + } else { + // Weak only lang items are always handled here + self.collect_item_extended( + lang_item, + def_id, + item_span, + attr_span, + generics, + actual_target, + ); + } + } + // Unknown lang item. + _ => { + self.r.tcx.dcx().delayed_bug("unknown lang item"); + } + } + } + } + + // Like collect_item() above, but also checks whether the lang item is declared + // with the right number of generic arguments. + fn collect_item_extended( + &mut self, + lang_item: LangItem, + item_def_id: LocalDefId, + item_span: Span, + attr_span: Span, + generics: Option<&ast::Generics>, + target: Target, + ) { + let name = lang_item.name(); + + if let Some(generics) = generics { + // Now check whether the lang_item has the expected number of generic + // arguments. Generally speaking, binary and indexing operations have + // one (for the RHS/index), unary operations have none, the closure + // traits have one for the argument list, coroutines have one for the + // resume argument, and ordering/equality relations have one for the RHS + // Some other types like Box and various unsizing-related traits + // have minimum requirements. + + // FIXME: This still doesn't count, e.g., elided lifetimes and APITs. + let mut actual_num = generics.params.len(); + if target.is_associated_item() { + for rib in self.ribs.type_ns.iter().rev() { + match rib.kind { + RibKind::Item(_, _) => { + actual_num += rib.bindings.len(); + break; + } + _ => {} + } + } + } + + let mut at_least = false; + let required = match lang_item.required_generics() { + GenericRequirement::Exact(num) if num != actual_num => Some(num), + GenericRequirement::Minimum(num) if actual_num < num => { + at_least = true; + Some(num) + } + // If the number matches, or there is no requirement, handle it normally + _ => None, + }; + + if let Some(num) = required { + // We are issuing E0718 "incorrect target" here, because while the + // item kind of the target is correct, the target is still wrong + // because of the wrong number of generic arguments. + self.r.tcx.dcx().emit_err(IncorrectTarget { + span: attr_span, + generics_span: generics.span, + name: name.as_str(), + kind: target.name(), + num, + actual_num, + at_least, + }); + + // return early to not collect the lang item + return; + } + } + + if self.r.tcx.crate_types().contains(&CrateType::Sdylib) { + self.r.tcx.dcx().emit_err(IncorrectCrateType { span: attr_span }); + } + + collect_item( + self.r.tcx, + &mut self.r.lang_items, + lang_item, + item_def_id.to_def_id(), + Some(item_span), + ); + } +} + +pub(crate) fn collect_item( + tcx: TyCtxt<'_>, + lang_items: &mut LanguageItems, + lang_item: LangItem, + item_def_id: DefId, + item_span: Option, +) { + // Check for duplicates. + if let Some(original_def_id) = lang_items.get(lang_item) + && original_def_id != item_def_id + { + let (item_def_id, original_def_id, item_span) = if let Some(original_def_id) = + original_def_id.as_local() + && !item_def_id.is_local() + { + // The crate-local definitions are loaded before the upstream crate definitions, + // because we only know which crates are being loaded after the resolver is fully done. + // In that case we swap the order of the items to report it meaningfully to the user. + (original_def_id.to_def_id(), item_def_id, Some(tcx.source_span(original_def_id))) + } else { + (item_def_id, original_def_id, item_span) + }; + let lang_item_name = lang_item.name(); + let crate_name = tcx.crate_name(item_def_id.krate); + let mut dependency_of = None; + let is_local = item_def_id.is_local(); + let path = if is_local { + String::new() + } else { + tcx.crate_extern_paths(item_def_id.krate) + .iter() + .map(|p| p.display().to_string()) + .collect::>() + .join(", ") + }; + + let mut orig_crate_name = None; + let mut orig_dependency_of = None; + let orig_is_local = original_def_id.is_local(); + let orig_path = if orig_is_local { + String::new() + } else { + tcx.crate_extern_paths(original_def_id.krate) + .iter() + .map(|p| p.display().to_string()) + .collect::>() + .join(", ") + }; + + if !original_def_id.is_local() { + orig_crate_name = Some(tcx.crate_name(original_def_id.krate)); + if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) = + tcx.extern_crate(original_def_id.krate) + { + orig_dependency_of = Some(tcx.crate_name(*inner_dependency_of)); + } + } + + let duplicate = if item_span.is_some() { + Duplicate::Plain + } else { + match tcx.extern_crate(item_def_id.krate) { + Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => { + dependency_of = Some(tcx.crate_name(*inner_dependency_of)); + Duplicate::CrateDepends + } + _ => Duplicate::Crate, + } + }; + + // When there's a duplicate lang item, something went very wrong and there's no value + // in recovering or doing anything. Give the user the one message to let them debug the + // mess they created and then wish them farewell. + tcx.dcx().emit_fatal(DuplicateLangItem { + local_span: item_span, + lang_item_name, + crate_name, + dependency_of, + is_local, + path, + first_defined_span: original_def_id.as_local().map(|did| tcx.source_span(did)), + orig_crate_name, + orig_dependency_of, + orig_is_local, + orig_path, + duplicate, + }); + } else { + // Matched. + lang_items.set(lang_item, item_def_id); + } +} diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_resolve/src/late/weak_lang_items.rs similarity index 100% rename from compiler/rustc_passes/src/weak_lang_items.rs rename to compiler/rustc_resolve/src/late/weak_lang_items.rs diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 58d6b8005c787..0fc9c31f43e32 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -42,7 +42,7 @@ use rustc_ast::{ self as ast, AngleBracketedArg, CRATE_NODE_ID, Crate, DUMMY_NODE_ID, Expr, ExprKind, GenericArg, GenericArgs, Generics, NodeId, Path, attr, }; -use rustc_attr_ir::{StrippedCfgItem, find_attr}; +use rustc_attr_ir::{LanguageItems, StrippedCfgItem, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, default}; use rustc_data_structures::intern::Interned; use rustc_data_structures::steal::Steal; @@ -1601,6 +1601,8 @@ pub struct Resolver<'ra, 'tcx> { /// Stores `#[diagnostic::on_unknown]` attributes placed on module declarations. on_unknown_data: FxHashMap = default::fx_hash_map(), features: &'tcx Features, + + lang_items: LanguageItems, } /// This provides memory for the rest of the crate. The `'ra` lifetime that is @@ -1920,6 +1922,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { current_crate_outer_attr_insert_span, disambiguators: Default::default(), features: tcx.features(), + lang_items: LanguageItems::new(), .. }; @@ -1990,6 +1993,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let confused_type_with_std_module = self.confused_type_with_std_module; let paths_matching_assoc_types = self.paths_matching_assoc_types; let effective_visibilities = self.effective_visibilities; + let lang_items = self.lang_items; let stripped_cfg_items = self .stripped_cfg_items @@ -2026,6 +2030,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { stripped_cfg_items, delegation_infos: self.delegation_infos, delegation_inherent_fn_map: self.delegation_inherent_fn_map, + lang_items, }; let ast_lowering = ResolverAstLowering { partial_res_map: self.partial_res_map, @@ -2119,6 +2124,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.tcx .sess .time("resolve_postprocess", || self.cstore_mut().postprocess(self.tcx, krate)); + + self.tcx.sess.time("finalize_lang_items", || self.finalize_lang_items()); }); // Don't mutate the cstore or stable crate id map from here on. @@ -2700,6 +2707,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } self.main_def = Some(MainDefinition { res, is_import, span }); } + + fn finalize_lang_items(&mut self) { + // Collect lang items in other crates. + for &cnum in self.tcx.used_crates(()) { + for &(def_id, lang_item) in self.tcx.defined_lang_items(cnum).iter() { + late::collect_item(self.tcx, &mut self.lang_items, lang_item, def_id, None); + } + } + late::weak_lang_items::check_crate(self.tcx, &mut self.lang_items); + } } fn with_owner<'ra, 'tcx, R: AsMut>, T>( diff --git a/tests/ui/lang-items/issue-83471.stderr b/tests/ui/lang-items/issue-83471.stderr index ffc48accdd917..49b3bfe84dc61 100644 --- a/tests/ui/lang-items/issue-83471.stderr +++ b/tests/ui/lang-items/issue-83471.stderr @@ -1,3 +1,12 @@ +error[E0718]: `fn` lang item must be applied to a trait with 1 generic argument + --> $DIR/issue-83471.rs:20:1 + | +LL | #[lang = "fn"] + | ^^^^^^^^^^^^^^ +... +LL | trait Fn { + | - this trait has 0 generic arguments + error[E0573]: cannot find type `export_name` in this scope --> $DIR/issue-83471.rs:24:13 | @@ -16,15 +25,6 @@ LL | fn call(export_name); = note: for more information, see = note: `#[warn(anonymous_parameters)]` (part of `#[warn(rust_2018_compatibility)]`) on by default -error[E0718]: `fn` lang item must be applied to a trait with 1 generic argument - --> $DIR/issue-83471.rs:20:1 - | -LL | #[lang = "fn"] - | ^^^^^^^^^^^^^^ -... -LL | trait Fn { - | - this trait has 0 generic arguments - error[E0658]: lang items are subject to change --> $DIR/issue-83471.rs:8:3 | diff --git a/tests/ui/panic-handler/weak-lang-item.rs b/tests/ui/panic-handler/weak-lang-item.rs index 6c3a5b2b271c2..d43f80cc55d6f 100644 --- a/tests/ui/panic-handler/weak-lang-item.rs +++ b/tests/ui/panic-handler/weak-lang-item.rs @@ -1,6 +1,8 @@ //@ edition:2015 //@ aux-build:weak-lang-items.rs //@ needs-unwind since it affects the error output +//~? ERROR: `#[panic_handler]` function required +//~? ERROR: unwinding panics are not supported without std #![no_std] diff --git a/tests/ui/panic-handler/weak-lang-item.stderr b/tests/ui/panic-handler/weak-lang-item.stderr index d7b453c96e488..5dcb37df68921 100644 --- a/tests/ui/panic-handler/weak-lang-item.stderr +++ b/tests/ui/panic-handler/weak-lang-item.stderr @@ -1,5 +1,5 @@ error[E0259]: the name `core` is defined multiple times - --> $DIR/weak-lang-item.rs:7:1 + --> $DIR/weak-lang-item.rs:9:1 | LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ `core` reimported here @@ -10,6 +10,13 @@ help: you can use `as` to change the binding name of the import LL | extern crate core as other_core; | +++++++++++++ -error: aborting due to 1 previous error +error: `#[panic_handler]` function required, but not found + +error: unwinding panics are not supported without std + | + = help: using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding + = note: since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0259`. From 0827b4d71504bc9844a2c8c6dd2af4816857e6c4 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Wed, 23 Sep 2026 04:15:47 +0100 Subject: [PATCH 15/21] Reconstruct storage lifetimes after move elimination --- .../src/move_elimination.rs | 361 +++++++++++++++++- ...indirect_source_alias.MoveElimination.diff | 2 + ..._fixup.aggregate_swap.MoveElimination.diff | 18 +- ...lias_fixup.array_swap.MoveElimination.diff | 8 +- ...lias_fixup.enum_alias.MoveElimination.diff | 16 +- ...ed_aggregate_aliasing.MoveElimination.diff | 6 +- ...lias_fixup.simd_alias.MoveElimination.diff | 6 +- ....simple_partial_alias.MoveElimination.diff | 14 +- .../basic.nrvo_borrowed.MoveElimination.diff | 10 +- .../dse.dse_guard.MoveElimination.diff | 27 +- tests/mir-opt/move-elimination/dse.rs | 2 + ...stant_index_projected.MoveElimination.diff | 10 +- ...x_local_not_projected.MoveElimination.diff | 4 + ...overlapping_lifetimes.MoveElimination.diff | 24 +- ..._fields_not_projected.MoveElimination.diff | 11 +- ...l_tuple_not_projected.MoveElimination.diff | 15 +- ...d_field_not_projected.MoveElimination.diff | 6 +- ...ubslice_not_projected.MoveElimination.diff | 8 +- ...d_storage_dead_at_end.MoveElimination.diff | 61 +++ ...ed_to_last_direct_use.MoveElimination.diff | 87 +++++ ...plit_for_storage_live.MoveElimination.diff | 35 ++ tests/mir-opt/move-elimination/storage.rs | 236 ++++++++++++ ....shorten_non_borrowed.MoveElimination.diff | 79 ++++ ...ge_dead_before_return.MoveElimination.diff | 17 + ...e_elided_on_join_fork.MoveElimination.diff | 36 ++ ..._live_moved_to_branch.MoveElimination.diff | 63 +++ ...age_dead_in_successor.MoveElimination.diff | 57 +++ ...age_disabled.disabled.MoveElimination.diff | 18 + .../move-elimination/storage_disabled.rs | 30 ++ ...wind.call_destination.MoveElimination.diff | 6 + 30 files changed, 1195 insertions(+), 78 deletions(-) create mode 100644 tests/mir-opt/move-elimination/storage.address_observed_storage_dead_at_end.MoveElimination.diff create mode 100644 tests/mir-opt/move-elimination/storage.borrowed_not_shortened_to_last_direct_use.MoveElimination.diff create mode 100644 tests/mir-opt/move-elimination/storage.critical_edge_split_for_storage_live.MoveElimination.diff create mode 100644 tests/mir-opt/move-elimination/storage.rs create mode 100644 tests/mir-opt/move-elimination/storage.shorten_non_borrowed.MoveElimination.diff create mode 100644 tests/mir-opt/move-elimination/storage.storage_dead_before_return.MoveElimination.diff create mode 100644 tests/mir-opt/move-elimination/storage.storage_live_elided_on_join_fork.MoveElimination.diff create mode 100644 tests/mir-opt/move-elimination/storage.storage_live_moved_to_branch.MoveElimination.diff create mode 100644 tests/mir-opt/move-elimination/storage.terminator_end_storage_dead_in_successor.MoveElimination.diff create mode 100644 tests/mir-opt/move-elimination/storage_disabled.disabled.MoveElimination.diff create mode 100644 tests/mir-opt/move-elimination/storage_disabled.rs diff --git a/compiler/rustc_mir_transform/src/move_elimination.rs b/compiler/rustc_mir_transform/src/move_elimination.rs index e02f1103fbf77..95a7f8cf237b5 100644 --- a/compiler/rustc_mir_transform/src/move_elimination.rs +++ b/compiler/rustc_mir_transform/src/move_elimination.rs @@ -70,10 +70,13 @@ //! as `Index` projections. These locals may only be replaced by another bare //! local. //! -//! # Storage lifetimes +//! # Storage reconstruction //! -//! The original storage markers no longer describe the merged locals, so they -//! are removed. Locals without storage markers have storage for the full function. +//! The original `StorageLive` and `StorageDead` statements no longer describe +//! the merged liveness produced by unification, so they are removed and rebuilt +//! from the liveness matrix when lifetime markers are emitted. This is done for +//! all locals, even ones that have not been merged, which has the additional +//! benefit of tightening the storage lifetime passed to LLVM. //! //! # Aliasing fixup //! @@ -101,16 +104,23 @@ //! `Discriminant`, `Ref`, and `RawPtr`, are left untouched because their //! codegen does not rely on the no-aliasing assumption. +use std::range::RangeInclusive; + use rustc_abi::{ExternAbi, FieldIdx, VariantIdx}; use rustc_const_eval::util::most_packed_projection; +use rustc_data_structures::fx::FxHashMap; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::SparseIntervalMatrix; use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, VisitPlacesWith, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::{Ty, TyCtxt}; -use rustc_mir_dataflow::impls::{SplitPointIndex, dump_liveness_matrix, liveness_matrix}; +use rustc_mir_dataflow::impls::{ + DefUse, SplitPointEffect, SplitPointIndex, VisitAllocatedLocalsWith, dump_liveness_matrix, + liveness_matrix, +}; use rustc_mir_dataflow::points::DenseLocationMap; +use rustc_mir_dataflow::{Analysis, Backward, GenKill, ResultsVisitor, visit_results}; use tracing::{debug, trace}; use crate::PassPolicy; @@ -154,6 +164,10 @@ impl<'tcx> crate::MirPass<'tcx> for MoveElimination { dump_liveness_matrix(tcx, body, "MoveElimination.post-liveness", &points, &liveness_matrix); + if tcx.sess.emit_lifetime_markers() { + reconstruct_storage(tcx, body, &points, &liveness_matrix); + } + apply_alias_fixup(tcx, body); } } @@ -591,6 +605,345 @@ impl<'tcx> MutVisitor<'tcx> for PlaceUpdater<'_, 'tcx> { } } +//////////////////////////////////////////////////////////////////////////////// +// Storage reconstruction + +/// Backward dataflow analysis which answers the question: from this point, is +/// there a path whose first access to a local is an initialization? +/// +/// This is used when a local is dead in a predecessor but maybe-live in its +/// successor. A `StorageLive` is inserted on that edge only if some continuation +/// initializes the local before reading it. If every continuation instead +/// reads the local first or never accesses it, `StorageLive` would be +/// unnecessary: it only allocates uninitialized storage and cannot make the +/// read valid. +/// +/// For example: +/// +/// ```text +/// bb1 bb2 +/// StorageLive(_1); _1 = ... // _1 is dead +/// _flag = true _flag = false +/// \ / +/// \ / +/// bb3 +/// switchInt(_flag) +/// / \ +/// bb4: use(_1) bb5: no use +/// ``` +/// +/// Liveness is path-insensitive, so `_1` is maybe-live in `bb3`: it is live in +/// `bb1` and `bb4`, but dead in `bb2` and `bb5`. Nevertheless, no `StorageLive` +/// is needed on `bb2 -> bb3`. The continuation to `bb5` never accesses `_1`, +/// while the continuation to `bb4` reads `_1` without initializing it first and +/// is therefore already UB. (The `_flag` assignments make that latter +/// continuation dynamically impossible, but this analysis does not need to +/// prove the correlation.) +/// +/// Live ranges which start in the middle of a block do not need this analysis: +/// such ranges always start at an initialization, so a `StorageLive` is +/// unconditionally required there. +/// +/// This analysis deliberately ignores the reconstructed `StorageDead` +/// boundaries. This can cause an initialization from a later allocation range +/// to propagate into an earlier range and result in an unnecessary +/// `StorageLive`, but cannot cause a required `StorageLive` to be omitted. +struct InitializedBeforeUse; + +impl<'tcx> Analysis<'tcx> for InitializedBeforeUse { + type Domain = DenseBitSet; + type Direction = Backward; + + const NAME: &'static str = "initialized-before-use"; + + fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { + DenseBitSet::new_empty(body.local_decls.len()) + } + + fn initialize_start_block(&self, _body: &Body<'tcx>, _state: &mut Self::Domain) {} + + fn apply_primary_statement_effect( + &self, + state: &mut Self::Domain, + statement: &Statement<'tcx>, + location: Location, + ) { + // In backward order, process writes before reads. + VisitAllocatedLocalsWith(|local, _| state.gen_(local)).visit_statement(statement, location); + VisitPlacesWith(|place: Place<'tcx>, context| { + if matches!(DefUse::for_place(place, context), DefUse::Use) { + state.kill(place.local); + } + }) + .visit_statement(statement, location); + } + + fn apply_primary_terminator_effect( + &self, + state: &mut Self::Domain, + terminator: &Terminator<'tcx>, + location: Location, + ) { + // In backward order, process writes before reads. + VisitAllocatedLocalsWith(|local, _| state.gen_(local)) + .visit_terminator(terminator, location); + VisitPlacesWith(|place: Place<'tcx>, context| { + if matches!(DefUse::for_place(place, context), DefUse::Use) { + state.kill(place.local); + } + }) + .visit_terminator(terminator, location); + } +} + +impl InitializedBeforeUse { + /// Computes the analysis state at the start of each block. + fn compute<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + ) -> IndexVec> { + let results = + Self.iterate_to_fixpoint(tcx, body, Some("MoveElimination.initialized-before-use")); + let mut block_start = IndexVec::from_elem_n( + DenseBitSet::new_empty(body.local_decls.len()), + body.basic_blocks.len(), + ); + + struct BlockStartVisitor<'a> { + block_start: &'a mut IndexVec>, + } + + impl<'tcx> ResultsVisitor<'tcx, InitializedBeforeUse> for BlockStartVisitor<'_> { + fn visit_block_exit(&mut self, state: &DenseBitSet, block: BasicBlock) { + self.block_start[block].clone_from(state); + } + } + + visit_results( + body, + rustc_middle::mir::traversal::reachable(body).map(|(block, _)| block), + &results, + &mut BlockStartVisitor { block_start: &mut block_start }, + ); + block_start + } +} + +/// Helper function to split a critical edge if necessary. +fn get_or_split_edge<'tcx>( + patcher: &mut MirPatch<'tcx>, + body: &Body<'tcx>, + split_edges: &mut FxHashMap<(BasicBlock, BasicBlock), BasicBlock>, + pred: BasicBlock, + succ: BasicBlock, +) -> BasicBlock { + if let Some(&split_bb) = split_edges.get(&(pred, succ)) { + return split_bb; + } + let source_info = body.basic_blocks[pred].terminator().source_info; + let split_bb = patcher.new_block(BasicBlockData::new( + Some(Terminator { source_info, kind: TerminatorKind::goto(succ) }), + body.basic_blocks[succ].is_cleanup, + )); + patcher.mutate_terminator(body, pred, |kind| { + kind.successors_mut(|t| { + if *t == succ { + *t = split_bb; + } + }); + }); + split_edges.insert((pred, succ), split_bb); + split_bb +} + +/// Live range boundary within a block which indicates where a storage statement +/// should be inserted. +enum RangeBoundary { + /// The range boundary is at the given location, and storage statements + /// should be inserted before/after it. + Loc(Location), + + /// The range is live across a block edge, and storage statements should be + /// inserted in predecessor/successor blocks. + Edge(BasicBlock), +} + +/// Splits a live interval at block boundaries, returning the location at which +/// storage statements need to be inserted at the start and end of the range. +/// +/// This is needed because `SparseIntervalMatrix` coalesces numerically adjacent +/// ranges, which can span multiple basic blocks. +fn split_range_by_block( + points: &DenseLocationMap, + range: RangeInclusive, +) -> impl Iterator { + let start = points.to_location(range.start.point()); + let end = points.to_location(range.last.point()); + debug_assert!(start.block <= end.block); + + (start.block.index()..=end.block.index()).map(move |block| { + let block = BasicBlock::from_usize(block); + + // If the live range starts at the first statement's `Early` point, + // the value came from a predecessor block. A write from the first + // statement would happen at the `Late` point instead. + let start = if block != start.block + || (start.statement_index == 0 && range.start.effect() == SplitPointEffect::Early) + { + RangeBoundary::Edge(block) + } else { + RangeBoundary::Loc(start) + }; + + // We need to insert `StorageDead` after the last statement that + // uses a local. If this is a terminator then we need to instead + // insert it at the start of every successor block where the local + // is dead on entry. + let end = if block == end.block && range.last.point() != points.terminator(block) { + RangeBoundary::Loc(end) + } else { + RangeBoundary::Edge(block) + }; + (start, end) + }) +} + +/// Re-constructs storage statements for all locals. +fn reconstruct_storage<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + points: &DenseLocationMap, + liveness_matrix: &SparseIntervalMatrix, +) { + let initialized_before_use = InitializedBeforeUse::compute(tcx, body); + let mut patcher = MirPatch::new(body); + let mut split_edges: FxHashMap<(BasicBlock, BasicBlock), BasicBlock> = Default::default(); + let mut storage_lives = Vec::new(); + + for local in body.local_decls.indices() { + // Arguments and return values don't use storage statements. + match body.local_kind(local) { + LocalKind::Arg | LocalKind::ReturnPointer => continue, + LocalKind::Temp => {} + } + + // Ignore dead locals. + let Some(row) = liveness_matrix.row(local) else { continue }; + if row.is_empty() { + continue; + } + + let mut emit_storage_live_in_preds = + |body: &mut Body<'tcx>, + patcher: &mut MirPatch<'tcx>, + storage_lives: &mut Vec<(Location, Local)>, + local: Local, + block: BasicBlock| { + if !initialized_before_use[block].contains(local) { + // No continuation initializes the local before reading it, + // so allocating storage cannot make any such read valid. + return; + } + + for &pred in &body.basic_blocks.predecessors()[block].clone() { + // If the local is live at any point in the predecessor's + // terminator then no StorageLive is needed. + let term = points.terminator(pred); + let term_early = SplitPointIndex::new(term, SplitPointEffect::Early); + let term_late = SplitPointIndex::new(term, SplitPointEffect::Late); + if !row.intersects_range(term_early..=term_late) { + // The local must be live on at least one predecessor, + // so if this is the only one then there is nothing to + // do. + debug_assert!(body.basic_blocks.predecessors()[block].len() > 1); + + // If the predecessor block has multiple successors then + // we need to split the critical edge before inserting + // StorageLive, otherwise the local would end up live on + // paths where it is supposed to be dead. + let loc = if body.basic_blocks[pred].terminator().successors().count() > 1 { + get_or_split_edge(patcher, body, &mut split_edges, pred, block) + .start_location() + } else { + body.terminator_loc(pred) + }; + storage_lives.push((loc, local)); + } + } + }; + let emit_storage_dead_in_succs = + |body: &mut Body<'tcx>, + patcher: &mut MirPatch<'tcx>, + local: Local, + block: BasicBlock| { + for succ in body.basic_blocks[block].terminator().successors() { + if !row.contains(SplitPointIndex::new( + points.entry_point(succ), + SplitPointEffect::Early, + )) { + // We don't care about critical edges here: if the local + // is already dead in the successor then it doesn't + // matter if we emit a redundant StorageDead. + + patcher.add_statement( + succ.start_location(), + StatementKind::StorageDead(local), + ); + } + } + }; + + // Iterate through the live range of the local and insert `StorageLive` + // and `StorageDead` at the points where it transitions from dead to + // live and vice versa. + for (start, end) in + row.iter_intervals().flat_map(|range| split_range_by_block(points, range)) + { + match start { + // Live-in from predecessor blocks: emit StorageLive in + // predecessors in which it wasn't previously live. + RangeBoundary::Edge(block) => { + emit_storage_live_in_preds( + body, + &mut patcher, + &mut storage_lives, + local, + block, + ); + } + // Otherwise just add `StorageLive` before the statement that + // starts the live range. + RangeBoundary::Loc(location) => storage_lives.push((location, local)), + } + + match end { + // If the range includes the terminator then emit StorageDead in + // successors which the local is dead on entry. + RangeBoundary::Edge(block) => { + emit_storage_dead_in_succs(body, &mut patcher, local, block); + } + // Otherwise just add `StorageDead` after the statement at which + // the live range ends. + RangeBoundary::Loc(location) => { + patcher.add_statement( + location.successor_within_block(), + StatementKind::StorageDead(local), + ); + } + } + } + } + + // Queue all `StorageLive` statements after `StorageDead` so that, when + // both are inserted at the same location, `StorageDead` always precedes + // `StorageLive` to avoid false overlaps. + for (loc, local) in storage_lives { + patcher.add_statement(loc, StatementKind::StorageLive(local)); + } + + patcher.apply(body); +} + //////////////////////////////////////////////////////////////////////////////// // Aliasing assignment fixup // diff --git a/tests/mir-opt/move-elimination/alias_fixup.aggregate_indirect_source_alias.MoveElimination.diff b/tests/mir-opt/move-elimination/alias_fixup.aggregate_indirect_source_alias.MoveElimination.diff index 1b4e6dbba1825..64d0fe11bfa9f 100644 --- a/tests/mir-opt/move-elimination/alias_fixup.aggregate_indirect_source_alias.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/alias_fixup.aggregate_indirect_source_alias.MoveElimination.diff @@ -14,12 +14,14 @@ - _3 = (copy (*_2), move _1); - _0 = copy _3; + (_0.1: u8) = const 1_u8; ++ StorageLive(_2); + _2 = &raw const (_0.1: u8); + StorageLive(_4); + _4 = no_retag copy (*_2); + (_0.0: u8) = no_retag move _4; + nop; + StorageDead(_4); ++ StorageDead(_2); + nop; return; } diff --git a/tests/mir-opt/move-elimination/alias_fixup.aggregate_swap.MoveElimination.diff b/tests/mir-opt/move-elimination/alias_fixup.aggregate_swap.MoveElimination.diff index d958b6d346d35..481d3ef60feb8 100644 --- a/tests/mir-opt/move-elimination/alias_fixup.aggregate_swap.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/alias_fixup.aggregate_swap.MoveElimination.diff @@ -41,15 +41,6 @@ - _7 = copy (_3.1: u8); - StorageLive(_8); - _8 = copy _7; -- StorageLive(_9); -- _9 = copy _6; -- _3 = (move _8, move _9); -- StorageDead(_9); -- StorageDead(_8); -- _0 = copy _3; -- StorageDead(_7); -- StorageDead(_6); -- StorageDead(_3); + nop; + nop; + nop; @@ -59,6 +50,9 @@ + nop; + nop; + nop; + StorageLive(_9); +- _9 = copy _6; +- _3 = (move _8, move _9); + _9 = copy (_0.0: u8); + nop; + nop; @@ -72,6 +66,12 @@ + (_0.1: u8) = no_retag move _9; + nop; + StorageDead(_10); + StorageDead(_9); +- StorageDead(_8); +- _0 = copy _3; +- StorageDead(_7); +- StorageDead(_6); +- StorageDead(_3); + nop; + nop; + nop; diff --git a/tests/mir-opt/move-elimination/alias_fixup.array_swap.MoveElimination.diff b/tests/mir-opt/move-elimination/alias_fixup.array_swap.MoveElimination.diff index f577fe6e46955..571ebeaa3de79 100644 --- a/tests/mir-opt/move-elimination/alias_fixup.array_swap.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/alias_fixup.array_swap.MoveElimination.diff @@ -23,12 +23,10 @@ - _3 = copy _1[1 of 2]; - StorageLive(_4); - _4 = copy _3; -- StorageLive(_5); ++ nop; + StorageLive(_5); - _5 = copy _2; - _1 = [move _4, move _5]; -- StorageDead(_5); -- StorageDead(_4); -+ nop; + _5 = copy _1[0 of 2]; + nop; + nop; @@ -42,6 +40,8 @@ + _1[1 of 2] = no_retag move _5; + nop; + StorageDead(_6); + StorageDead(_5); +- StorageDead(_4); + nop; + nop; _0 = copy _1; diff --git a/tests/mir-opt/move-elimination/alias_fixup.enum_alias.MoveElimination.diff b/tests/mir-opt/move-elimination/alias_fixup.enum_alias.MoveElimination.diff index 0997f614ac55e..4f84e289723dd 100644 --- a/tests/mir-opt/move-elimination/alias_fixup.enum_alias.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/alias_fixup.enum_alias.MoveElimination.diff @@ -60,15 +60,18 @@ bb2: { - _8 = discriminant(_4); ++ StorageLive(_8); + _8 = discriminant(_0); switchInt(move _8) -> [0: bb4, 1: bb5, otherwise: bb3]; } bb3: { ++ StorageDead(_8); unreachable; } bb4: { ++ StorageDead(_8); _0 = PairEnum::Empty; goto -> bb6; } @@ -80,14 +83,11 @@ - _10 = copy ((_4 as Pair).1: u8); - StorageLive(_11); - _11 = copy _10; -- StorageLive(_12); ++ StorageDead(_8); ++ nop; + StorageLive(_12); - _12 = copy _9; - _0 = PairEnum::Pair(move _11, move _12); -- StorageDead(_12); -- StorageDead(_11); -- StorageDead(_10); -- StorageDead(_9); -+ nop; + _12 = copy ((_0 as Pair).0: u8); + nop; + nop; @@ -102,6 +102,10 @@ + discriminant(_0) = 1; + nop; + StorageDead(_13); + StorageDead(_12); +- StorageDead(_11); +- StorageDead(_10); +- StorageDead(_9); + nop; + nop; + nop; diff --git a/tests/mir-opt/move-elimination/alias_fixup.mixed_aggregate_aliasing.MoveElimination.diff b/tests/mir-opt/move-elimination/alias_fixup.mixed_aggregate_aliasing.MoveElimination.diff index 0b3f73be637a0..27f6c10549040 100644 --- a/tests/mir-opt/move-elimination/alias_fixup.mixed_aggregate_aliasing.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/alias_fixup.mixed_aggregate_aliasing.MoveElimination.diff @@ -46,7 +46,8 @@ } bb3: { -- StorageLive(_6); ++ nop; + StorageLive(_6); - _6 = copy (_3.1: u8); - StorageLive(_7); - _7 = copy (_3.0: u8); @@ -55,8 +56,6 @@ - _4 = Triple(move _6, move _7, move _8); - StorageDead(_8); - StorageDead(_7); -- StorageDead(_6); -+ nop; + _6 = copy (_0.1: u8); + nop; + nop; @@ -69,6 +68,7 @@ + (_0.2: u8) = no_retag move _2; + nop; + StorageDead(_9); + StorageDead(_6); + nop; + nop; + nop; diff --git a/tests/mir-opt/move-elimination/alias_fixup.simd_alias.MoveElimination.diff b/tests/mir-opt/move-elimination/alias_fixup.simd_alias.MoveElimination.diff index 7c2b4ebfb7fca..12382bbaa4475 100644 --- a/tests/mir-opt/move-elimination/alias_fixup.simd_alias.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/alias_fixup.simd_alias.MoveElimination.diff @@ -19,19 +19,19 @@ bb0: { - StorageLive(_2); - StorageLive(_3); -- StorageLive(_4); + nop; + nop; + nop; + StorageLive(_4); _4 = [const 0_u32; 4]; - _3 = Vector(move _4); -- StorageDead(_4); ++ (_0.0: Vector) = Vector(move _4); + StorageDead(_4); - _2 = VectorUnion { vector: move _3 }; - StorageDead(_3); - StorageLive(_5); - _5 = copy _1; - switchInt(move _5) -> [0: bb2, otherwise: bb1]; -+ (_0.0: Vector) = Vector(move _4); + nop; + nop; + nop; diff --git a/tests/mir-opt/move-elimination/alias_fixup.simple_partial_alias.MoveElimination.diff b/tests/mir-opt/move-elimination/alias_fixup.simple_partial_alias.MoveElimination.diff index e548e921f3142..af47eb7347d2a 100644 --- a/tests/mir-opt/move-elimination/alias_fixup.simple_partial_alias.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/alias_fixup.simple_partial_alias.MoveElimination.diff @@ -25,23 +25,23 @@ - StorageDead(_3); - StorageLive(_4); - _4 = copy (_2.0: [u8; 4]); -- StorageLive(_5); -- _5 = copy _4; -- (_2.1: [u8; 4]) = move _5; -- StorageDead(_5); -- _0 = move _2; -- StorageDead(_4); -- StorageDead(_2); + nop; + nop; + nop; + _0 = U { a: move _1 }; + nop; + nop; + StorageLive(_5); +- _5 = copy _4; +- (_2.1: [u8; 4]) = move _5; + _5 = copy (_0.0: [u8; 4]); + nop; + nop; + (_0.1: [u8; 4]) = move _5; + StorageDead(_5); +- _0 = move _2; +- StorageDead(_4); +- StorageDead(_2); + nop; + nop; + nop; diff --git a/tests/mir-opt/move-elimination/basic.nrvo_borrowed.MoveElimination.diff b/tests/mir-opt/move-elimination/basic.nrvo_borrowed.MoveElimination.diff index 6762e06029772..8a07af2edceaf 100644 --- a/tests/mir-opt/move-elimination/basic.nrvo_borrowed.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/basic.nrvo_borrowed.MoveElimination.diff @@ -17,24 +17,28 @@ - _1 = [const 1_u8; 8]; - StorageLive(_2); - StorageLive(_3); -- StorageLive(_4); -- _4 = &mut _1; + nop; + _0 = [const 1_u8; 8]; + nop; + nop; + nop; + StorageLive(_4); +- _4 = &mut _1; + _4 = &mut _0; ++ StorageLive(_3); _3 = &mut (*_4); ++ StorageDead(_4); ++ StorageLive(_2); _2 = init(move _3) -> [return: bb1, unwind unreachable]; } bb1: { - StorageDead(_3); - StorageDead(_4); -- StorageDead(_2); + StorageDead(_2); - _0 = move _1; - StorageDead(_1); ++ StorageDead(_3); + nop; + nop; + nop; diff --git a/tests/mir-opt/move-elimination/dse.dse_guard.MoveElimination.diff b/tests/mir-opt/move-elimination/dse.dse_guard.MoveElimination.diff index 2f3019364f343..895dab2802597 100644 --- a/tests/mir-opt/move-elimination/dse.dse_guard.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/dse.dse_guard.MoveElimination.diff @@ -22,12 +22,12 @@ bb0: { - StorageLive(_1); -- StorageLive(_2); -- StorageLive(_3); -- _3 = make_fields(const 0_u8) -> [return: bb1, unwind unreachable]; + nop; + nop; + nop; + StorageLive(_2); +- StorageLive(_3); +- _3 = make_fields(const 0_u8) -> [return: bb1, unwind unreachable]; + _2 = make_fields(const 0_u8) -> [return: bb1, unwind unreachable]; } @@ -39,6 +39,7 @@ + nop; + nop; + nop; ++ StorageLive(_7); + _7 = make_fields(const 1_u8) -> [return: bb2, unwind unreachable]; } @@ -46,45 +47,49 @@ - _1 = move _4; - StorageDead(_4); - StorageLive(_5); -- StorageLive(_6); -- _6 = &raw const _1; + nop; + nop; + nop; + nop; + StorageLive(_6); +- _6 = &raw const _1; + _6 = &raw const _7; ++ StorageLive(_5); _5 = observe(move _6) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_6); -- StorageDead(_5); + StorageDead(_5); - StorageLive(_7); - _7 = move _1; ++ StorageDead(_6); + nop; + nop; + nop; + nop; _2 = move _7; -- StorageDead(_7); + StorageDead(_7); - StorageLive(_8); -- StorageLive(_9); + nop; + nop; + nop; + StorageLive(_9); _9 = &raw const _2; ++ StorageLive(_8); _8 = observe(move _9) -> [return: bb4, unwind unreachable]; } bb4: { - StorageDead(_9); -- StorageDead(_8); + StorageDead(_8); ++ StorageDead(_9); + nop; + nop; _0 = const (); -- StorageDead(_2); -- StorageDead(_1); + nop; + StorageDead(_2); +- StorageDead(_1); + nop; return; } diff --git a/tests/mir-opt/move-elimination/dse.rs b/tests/mir-opt/move-elimination/dse.rs index 77685fb533fdd..268b54fa96ae6 100644 --- a/tests/mir-opt/move-elimination/dse.rs +++ b/tests/mir-opt/move-elimination/dse.rs @@ -19,7 +19,9 @@ pub fn dse_guard() { // CHECK-LABEL: fn dse_guard( // CHECK: debug a => [[a:_.*]]; // CHECK: debug b => [[b:_.*]]; + // CHECK: StorageLive([[b]]); // CHECK: [[b]] = make_fields(const 0_u8) + // CHECK: StorageLive([[a]]); // CHECK: [[a]] = make_fields(const 1_u8) // CHECK: observe(move // CHECK: [[b]] = move [[a]] diff --git a/tests/mir-opt/move-elimination/exclusions.constant_index_projected.MoveElimination.diff b/tests/mir-opt/move-elimination/exclusions.constant_index_projected.MoveElimination.diff index 33b96a7981738..aff9f122c383d 100644 --- a/tests/mir-opt/move-elimination/exclusions.constant_index_projected.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/exclusions.constant_index_projected.MoveElimination.diff @@ -15,21 +15,21 @@ bb0: { - StorageLive(_2); - _2 = copy _1[0 of 2]; -- StorageLive(_3); -- StorageLive(_4); -- _4 = copy _2; -- _3 = std::hint::black_box::<[u8; 4]>(move _4) -> [return: bb1, unwind unreachable]; + nop; + nop; + nop; + nop; + nop; + StorageLive(_3); +- StorageLive(_4); +- _4 = copy _2; +- _3 = std::hint::black_box::<[u8; 4]>(move _4) -> [return: bb1, unwind unreachable]; + _3 = std::hint::black_box::<[u8; 4]>(move _1[0 of 2]) -> [return: bb1, unwind unreachable]; } bb1: { - StorageDead(_4); -- StorageDead(_3); + StorageDead(_3); + nop; + nop; _0 = const (); diff --git a/tests/mir-opt/move-elimination/exclusions.index_local_not_projected.MoveElimination.diff b/tests/mir-opt/move-elimination/exclusions.index_local_not_projected.MoveElimination.diff index 4cdc1d24ee427..6aa7c42a2ba08 100644 --- a/tests/mir-opt/move-elimination/exclusions.index_local_not_projected.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/exclusions.index_local_not_projected.MoveElimination.diff @@ -7,9 +7,13 @@ let mut _3: usize; bb0: { ++ StorageLive(_2); _2 = const 2_usize; ++ StorageLive(_3); _3 = copy _1[_2]; ++ StorageDead(_3); _0 = [copy _2]; ++ StorageDead(_2); return; } } diff --git a/tests/mir-opt/move-elimination/exclusions.overlapping_lifetimes.MoveElimination.diff b/tests/mir-opt/move-elimination/exclusions.overlapping_lifetimes.MoveElimination.diff index ddf8630091e4b..51db7a9e85374 100644 --- a/tests/mir-opt/move-elimination/exclusions.overlapping_lifetimes.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/exclusions.overlapping_lifetimes.MoveElimination.diff @@ -28,28 +28,31 @@ - StorageLive(_2); - _2 = make_fields(const 0_u8) -> [return: bb1, unwind unreachable]; + nop; ++ StorageLive(_10); + _10 = make_fields(const 0_u8) -> [return: bb1, unwind unreachable]; } bb1: { - StorageLive(_3); - StorageLive(_4); -- StorageLive(_5); -- _5 = &raw const _2; + nop; + nop; + nop; + StorageLive(_5); +- _5 = &raw const _2; + _5 = &raw const _10; ++ StorageLive(_4); _4 = observe(move _5) -> [return: bb2, unwind unreachable]; } bb2: { - StorageDead(_5); -- StorageDead(_4); + StorageDead(_4); - StorageLive(_6); - StorageLive(_7); - _7 = copy _1; - switchInt(move _7) -> [0: bb5, otherwise: bb3]; ++ StorageDead(_5); + nop; + nop; + nop; @@ -77,8 +80,11 @@ + nop; + nop; + _0 = move _10; ++ StorageDead(_10); + nop; ++ StorageLive(_6); _6 = const (); ++ StorageDead(_6); goto -> bb6; } @@ -86,12 +92,14 @@ - StorageLive(_10); - _10 = move _2; - _3 = move _10; -- StorageDead(_10); + nop; + nop; + _0 = move _10; + StorageDead(_10); + nop; ++ StorageLive(_6); _6 = const (); ++ StorageDead(_6); goto -> bb6; } @@ -99,22 +107,24 @@ - StorageDead(_7); - StorageDead(_6); - StorageLive(_11); -- StorageLive(_12); -- _12 = &raw const _3; + nop; + nop; + nop; + nop; + StorageLive(_12); +- _12 = &raw const _3; + _12 = &raw const _0; ++ StorageLive(_11); _11 = observe(move _12) -> [return: bb7, unwind unreachable]; } bb7: { - StorageDead(_12); -- StorageDead(_11); + StorageDead(_11); - _0 = move _3; - StorageDead(_3); - StorageDead(_2); ++ StorageDead(_12); + nop; + nop; + nop; diff --git a/tests/mir-opt/move-elimination/exclusions.packed_fields_not_projected.MoveElimination.diff b/tests/mir-opt/move-elimination/exclusions.packed_fields_not_projected.MoveElimination.diff index 6c0bb304d3171..af6d2b34a0439 100644 --- a/tests/mir-opt/move-elimination/exclusions.packed_fields_not_projected.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/exclusions.packed_fields_not_projected.MoveElimination.diff @@ -21,13 +21,13 @@ - _1 = [const 1_u8; 8]; - StorageLive(_2); - _2 = [const 2_u8; 8]; -- StorageLive(_3); -- _3 = copy _1; -- StorageLive(_4); -- _4 = copy _2; + nop; + StorageLive(_3); +- _3 = copy _1; + _3 = [const 1_u8; 8]; + nop; + StorageLive(_4); +- _4 = copy _2; + _4 = [const 2_u8; 8]; + nop; + nop; @@ -35,9 +35,10 @@ + nop; _0 = Packed { a: move _3, b: move _4 }; - StorageDead(_4); -- StorageDead(_3); + StorageDead(_3); - StorageDead(_2); - StorageDead(_1); ++ StorageDead(_4); + nop; + nop; + nop; diff --git a/tests/mir-opt/move-elimination/exclusions.rust_call_tuple_not_projected.MoveElimination.diff b/tests/mir-opt/move-elimination/exclusions.rust_call_tuple_not_projected.MoveElimination.diff index bbde5a905a9e7..fcaf35ddeb8b0 100644 --- a/tests/mir-opt/move-elimination/exclusions.rust_call_tuple_not_projected.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/exclusions.rust_call_tuple_not_projected.MoveElimination.diff @@ -29,13 +29,13 @@ - StorageLive(_5); - _5 = move _1; - StorageLive(_6); -- StorageLive(_7); -- _7 = copy _2; -- StorageLive(_8); -- _8 = copy _3; + nop; + StorageLive(_7); +- _7 = copy _2; + _7 = [const 1_u8; 8]; + nop; + StorageLive(_8); +- _8 = copy _3; + _8 = [const 2_u8; 8]; + nop; + nop; @@ -45,8 +45,12 @@ + nop; + nop; + nop; ++ StorageLive(_6); _6 = (move _7, move _8); - _4 = >::call_once(move _5, move _6) -> [return: bb1, unwind unreachable]; ++ StorageDead(_7); ++ StorageDead(_8); ++ StorageLive(_4); + _4 = >::call_once(move _1, move _6) -> [return: bb1, unwind unreachable]; } @@ -55,7 +59,8 @@ - StorageDead(_7); - StorageDead(_6); - StorageDead(_5); -- StorageDead(_4); + StorageDead(_4); ++ StorageDead(_6); + nop; + nop; + nop; diff --git a/tests/mir-opt/move-elimination/exclusions.simd_field_not_projected.MoveElimination.diff b/tests/mir-opt/move-elimination/exclusions.simd_field_not_projected.MoveElimination.diff index 8d63e07039695..4844207200f34 100644 --- a/tests/mir-opt/move-elimination/exclusions.simd_field_not_projected.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/exclusions.simd_field_not_projected.MoveElimination.diff @@ -13,14 +13,14 @@ bb0: { - StorageLive(_1); - _1 = [const 1_u32, const 2_u32, const 3_u32, const 4_u32]; -- StorageLive(_2); -- _2 = copy _1; + nop; + StorageLive(_2); +- _2 = copy _1; + _2 = [const 1_u32, const 2_u32, const 3_u32, const 4_u32]; + nop; + nop; _0 = U32x4(move _2); -- StorageDead(_2); + StorageDead(_2); - StorageDead(_1); + nop; + nop; diff --git a/tests/mir-opt/move-elimination/exclusions.subslice_not_projected.MoveElimination.diff b/tests/mir-opt/move-elimination/exclusions.subslice_not_projected.MoveElimination.diff index 85b38460d277a..1d3c3b27e81e2 100644 --- a/tests/mir-opt/move-elimination/exclusions.subslice_not_projected.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/exclusions.subslice_not_projected.MoveElimination.diff @@ -16,19 +16,21 @@ - StorageLive(_2); - _2 = copy _1[1..3]; - StorageLive(_3); -- StorageLive(_4); -- _4 = copy _2; + nop; + StorageLive(_4); +- _4 = copy _2; + _4 = copy _1[1..3]; + nop; + nop; + nop; ++ StorageLive(_3); _3 = std::hint::black_box::<[u8; 2]>(move _4) -> [return: bb1, unwind unreachable]; } bb1: { - StorageDead(_4); -- StorageDead(_3); + StorageDead(_3); ++ StorageDead(_4); + nop; + nop; _0 = const (); diff --git a/tests/mir-opt/move-elimination/storage.address_observed_storage_dead_at_end.MoveElimination.diff b/tests/mir-opt/move-elimination/storage.address_observed_storage_dead_at_end.MoveElimination.diff new file mode 100644 index 0000000000000..dfc680dc01ff2 --- /dev/null +++ b/tests/mir-opt/move-elimination/storage.address_observed_storage_dead_at_end.MoveElimination.diff @@ -0,0 +1,61 @@ +- // MIR for `address_observed_storage_dead_at_end` before MoveElimination ++ // MIR for `address_observed_storage_dead_at_end` after MoveElimination + + fn address_observed_storage_dead_at_end(_1: bool) -> () { + debug flag => _1; + let mut _0: (); + let _2: u32; + let mut _3: bool; + let _4: *const u32; + let mut _5: *const u32; + scope 1 { + debug x => _2; + } + + bb0: { +- StorageLive(_2); +- StorageLive(_3); +- _3 = copy _1; +- switchInt(move _3) -> [0: bb3, otherwise: bb1]; ++ nop; ++ nop; ++ nop; ++ switchInt(move _1) -> [0: bb3, otherwise: bb1]; + } + + bb1: { ++ StorageLive(_2); + _2 = const 1_u32; +- StorageLive(_4); ++ nop; ++ nop; + StorageLive(_5); + _5 = &raw const _2; ++ StorageLive(_4); + _4 = opaque::<*const u32>(move _5) -> [return: bb2, unwind unreachable]; + } + + bb2: { +- StorageDead(_5); + StorageDead(_4); ++ StorageDead(_5); ++ nop; ++ nop; + _0 = const (); + goto -> bb4; + } + + bb3: { + _0 = const (); + goto -> bb4; + } + + bb4: { +- StorageDead(_3); ++ nop; ++ nop; + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/move-elimination/storage.borrowed_not_shortened_to_last_direct_use.MoveElimination.diff b/tests/mir-opt/move-elimination/storage.borrowed_not_shortened_to_last_direct_use.MoveElimination.diff new file mode 100644 index 0000000000000..c28ca81eb5b9c --- /dev/null +++ b/tests/mir-opt/move-elimination/storage.borrowed_not_shortened_to_last_direct_use.MoveElimination.diff @@ -0,0 +1,87 @@ +- // MIR for `borrowed_not_shortened_to_last_direct_use` before MoveElimination ++ // MIR for `borrowed_not_shortened_to_last_direct_use` after MoveElimination + + fn borrowed_not_shortened_to_last_direct_use(_1: u32) -> () { + debug x => _1; + let mut _0: (); + let _2: u32; + let mut _3: u32; + let _6: u32; + let mut _7: u32; + let mut _8: u32; + let mut _9: u32; + scope 1 { + debug a => _2; + let _4: &u32; + scope 2 { + debug r => _4; + let _5: u32; + scope 3 { +- debug out => _5; ++ debug out => _9; + } + } + } + + bb0: { ++ nop; ++ nop; ++ nop; + StorageLive(_2); +- StorageLive(_3); +- _3 = copy _1; +- _2 = opaque::(move _3) -> [return: bb1, unwind unreachable]; ++ _2 = opaque::(move _1) -> [return: bb1, unwind unreachable]; + } + + bb1: { +- StorageDead(_3); ++ nop; ++ nop; + StorageLive(_4); + _4 = &_2; +- StorageLive(_5); +- _5 = copy _2; +- StorageLive(_6); +- StorageLive(_7); ++ nop; ++ StorageLive(_9); ++ _9 = copy _2; ++ nop; ++ nop; ++ nop; + StorageLive(_8); + _8 = copy (*_4); +- StorageLive(_9); +- _9 = copy _5; ++ StorageDead(_4); ++ nop; ++ nop; ++ StorageLive(_7); + _7 = Add(move _8, move _9); +- StorageDead(_9); + StorageDead(_8); ++ StorageDead(_9); ++ nop; ++ nop; ++ StorageLive(_6); + _6 = opaque::(move _7) -> [return: bb2, unwind unreachable]; + } + + bb2: { +- StorageDead(_7); + StorageDead(_6); ++ StorageDead(_7); ++ nop; ++ nop; + _0 = const (); +- StorageDead(_5); +- StorageDead(_4); ++ nop; ++ nop; ++ nop; + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/move-elimination/storage.critical_edge_split_for_storage_live.MoveElimination.diff b/tests/mir-opt/move-elimination/storage.critical_edge_split_for_storage_live.MoveElimination.diff new file mode 100644 index 0000000000000..20a5abf62e439 --- /dev/null +++ b/tests/mir-opt/move-elimination/storage.critical_edge_split_for_storage_live.MoveElimination.diff @@ -0,0 +1,35 @@ +- // MIR for `critical_edge_split_for_storage_live` before MoveElimination ++ // MIR for `critical_edge_split_for_storage_live` after MoveElimination + + fn critical_edge_split_for_storage_live(_1: bool) -> () { + debug x => _2; + let mut _0: (); + let mut _2: u32; + let mut _3: *const u32; + + bb0: { +- switchInt(copy _1) -> [1: bb1, otherwise: bb2]; ++ switchInt(copy _1) -> [1: bb1, otherwise: bb3]; + } + + bb1: { ++ StorageLive(_2); + _2 = const 1_u32; ++ StorageLive(_3); + _3 = &raw const _2; + _3 = opaque::<*const u32>(copy _3) -> [return: bb2, unwind unreachable]; + } + + bb2: { ++ StorageDead(_3); + _2 = const 2_u32; ++ StorageDead(_2); + return; ++ } ++ ++ bb3: { ++ StorageLive(_2); ++ goto -> bb2; + } + } + diff --git a/tests/mir-opt/move-elimination/storage.rs b/tests/mir-opt/move-elimination/storage.rs new file mode 100644 index 0000000000000..c699e4591c22c --- /dev/null +++ b/tests/mir-opt/move-elimination/storage.rs @@ -0,0 +1,236 @@ +//@ test-mir-pass: MoveElimination +//@ compile-flags: -Cpanic=abort -Zlint-mir=false + +#![feature(custom_mir, core_intrinsics)] + +use std::intrinsics::mir::*; + +fn opaque(x: T) -> T { + x +} + +// EMIT_MIR storage.shorten_non_borrowed.MoveElimination.diff +pub fn shorten_non_borrowed(x: u32) { + // This checks that reconstruction can shorten a non-borrowed local's + // storage to its last use instead of keeping lexical storage markers. + // CHECK-LABEL: fn shorten_non_borrowed( + // CHECK: debug a => [[short:_.*]]; + // CHECK: debug b => [[short]]; + // CHECK: StorageLive([[short]]); + // CHECK: [[short]] = opaque::( + // CHECK: opaque::(move [[short]]) -> [return: [[short_ret:bb.*]], + // CHECK: [[short_ret]]: { + // CHECK: StorageDead([[short]]); + // CHECK: opaque::( + let a = opaque(x); + let b = a; + opaque(b); + opaque(x); +} + +// EMIT_MIR storage.borrowed_not_shortened_to_last_direct_use.MoveElimination.diff +pub fn borrowed_not_shortened_to_last_direct_use(x: u32) { + // This checks that a borrowed local is not shortened merely to its last + // direct use; the borrow keeps its storage live while the reference exists. + // CHECK-LABEL: fn borrowed_not_shortened_to_last_direct_use( + // CHECK: debug a => [[borrowed:_.*]]; + // CHECK: debug r => [[borrow_ref:_.*]]; + // CHECK: debug out => [[out:_.*]]; + // CHECK: StorageLive([[borrowed]]); + // CHECK: [[borrowed]] = opaque::(move _1) + // CHECK: [[borrow_ref]] = &[[borrowed]]; + // CHECK: [[out]] = copy [[borrowed]]; + // CHECK: copy (*[[borrow_ref]]); + // CHECK: StorageDead([[borrowed]]); + let a = opaque(x); + let r = &a; + let out = a; + opaque(*r + out); +} + +// EMIT_MIR storage.storage_live_moved_to_branch.MoveElimination.diff +pub fn storage_live_moved_to_branch(flag: bool) { + // This checks storage reconstruction can shrink a local declared before a + // branch so its storage is live only on the arm where it is initialized. + // CHECK-LABEL: fn storage_live_moved_to_branch( + // CHECK: debug x => [[branch_tmp:_.*]]; + // CHECK: switchInt(move _1) -> [0: bb3, otherwise: bb1]; + // CHECK: bb1: { + // CHECK: StorageLive([[branch_tmp]]); + // CHECK: [[branch_tmp]] = const 1_u32; + // CHECK: opaque::(move [[branch_tmp]]) -> [return: [[branch_ret:bb.*]], + // CHECK: [[branch_ret]]: { + // CHECK: StorageDead([[branch_tmp]]); + let x: u32; + if flag { + x = 1; + opaque(x); + } +} + +// EMIT_MIR storage.address_observed_storage_dead_at_end.MoveElimination.diff +pub fn address_observed_storage_dead_at_end(flag: bool) { + // This checks that an address-observed local declared before a branch still + // has StorageLive moved into the initialized arm without adding one to the + // uninitialized arm, but StorageDead remains at the end of the function + // instead of being shortened to the last direct use. + // CHECK-LABEL: fn address_observed_storage_dead_at_end( + // CHECK: debug x => [[addr_tmp:_.*]]; + // CHECK: switchInt(move _1) -> [0: [[skip:bb.*]], otherwise: [[init:bb.*]]]; + // CHECK: [[init]]: { + // CHECK: StorageLive([[addr_tmp]]); + // CHECK: [[addr_tmp]] = const 1_u32; + // CHECK: &raw const [[addr_tmp]]; + // CHECK: opaque::<*const u32> + // CHECK-NOT: StorageDead([[addr_tmp]]); + // CHECK: [[skip]]: { + // CHECK-NOT: StorageLive([[addr_tmp]]); + // CHECK: {{bb.*}}: { + // CHECK: StorageDead([[addr_tmp]]); + let x: u32; + if flag { + x = 1; + opaque(&raw const x); + } +} + +// EMIT_MIR storage.terminator_end_storage_dead_in_successor.MoveElimination.diff +pub fn terminator_end_storage_dead_in_successor(x: u32) -> u32 { + // This checks storage reconstruction when the last use of a local is as a + // call argument in a terminator. + // CHECK-LABEL: fn terminator_end_storage_dead_in_successor( + // CHECK: debug tmp => [[term_tmp:_.*]]; + // CHECK: StorageLive([[term_tmp]]); + // CHECK: [[term_tmp]] = opaque::(move _1) + // CHECK: opaque::(move [[term_tmp]]) -> [return: [[term_ret:bb.*]], + // CHECK: [[term_ret]]: { + // CHECK-NEXT: StorageDead([[term_tmp]]); + let tmp = opaque(x); + let out = opaque(tmp); + out +} + +// EMIT_MIR storage.storage_dead_before_return.MoveElimination.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn storage_dead_before_return() -> *const u32 { + // This checks that a borrowed local without input storage statements has + // its reconstructed storage ended before returning. + // CHECK-LABEL: fn storage_dead_before_return( + // CHECK: debug x => [[x:_.*]]; + // CHECK: StorageLive([[x]]); + // CHECK: [[x]] = const 1_u32; + // CHECK: [[ret:_.*]] = &raw const [[x]]; + // CHECK: StorageDead([[x]]); + // CHECK-NEXT: return; + mir! { + let x: u32; + debug x => x; + + { + x = 1; + RET = &raw const x; + Return() + } + } +} + +// EMIT_MIR storage.critical_edge_split_for_storage_live.MoveElimination.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn critical_edge_split_for_storage_live(flag: bool) { + // This checks storage reconstruction on a custom CFG where both branches + // reach a block which initializes a maybe-live local. The local is dead on + // the direct incoming edge, so inserting StorageLive requires splitting the + // critical edge from the entry switch. + // CHECK-LABEL: fn critical_edge_split_for_storage_live( + // CHECK: debug x => [[crit_tmp:_.*]]; + // CHECK: switchInt(copy _1) -> [1: [[init:bb.*]], otherwise: [[split:bb.*]]]; + // CHECK: [[init]]: { + // CHECK: StorageLive([[crit_tmp]]); + // CHECK: [[crit_tmp]] = const 1_u32; + // CHECK: &raw const [[crit_tmp]]; + // CHECK: opaque::<*const u32>{{.*}} -> [return: [[ret:bb.*]], + // CHECK: [[ret]]: { + // CHECK: [[crit_tmp]] = const 2_u32; + // CHECK: [[split]]: { + // CHECK-NEXT: StorageLive([[crit_tmp]]); + // CHECK-NEXT: goto -> [[ret]]; + mir! { + let x: u32; + let ptr: *const u32; + debug x => x; + + { + match flag { + true => init, + _ => ret, + } + } + + init = { + x = 1; + ptr = &raw const x; + Call(ptr = opaque::<*const u32>(ptr), ReturnTo(ret), UnwindUnreachable()) + } + + ret = { + // An initialization forces a StorageLive on both incoming branches, + // which in turn forces a critical edge split. + x = 2; + Return() + } + } +} + +// EMIT_MIR storage.storage_live_elided_on_join_fork.MoveElimination.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn storage_live_elided_on_join_fork(flag: bool) { + // This checks a join-fork CFG where x is live in only one predecessor and + // one successor of the middle block. The flag correlation means the path + // which reads x is only reached after x has been initialized. On the direct + // edge to join, every continuation either reads x before initializing it or + // never accesses it, so no StorageLive or critical-edge split is needed. + // CHECK-LABEL: fn storage_live_elided_on_join_fork( + // CHECK: debug x => [[join_tmp:_.*]]; + // CHECK: switchInt(copy _1) -> [1: [[init:bb.*]], otherwise: [[join:bb.*]]]; + // CHECK: [[init]]: { + // CHECK: StorageLive([[join_tmp]]); + // CHECK: [[join_tmp]] = const 1_u32; + // CHECK: goto -> [[join]]; + // CHECK: [[join]]: { + // CHECK-NOT: StorageLive([[join_tmp]]); + // CHECK: switchInt(copy _1) -> [1: [[use_x:bb.*]], otherwise: [[done:bb.*]]]; + // CHECK: [[use_x]]: { + // CHECK: opaque::(move [[join_tmp]]) -> [return: [[done]], unwind unreachable]; + mir! { + let x: u32; + let out: u32; + debug x => x; + + { + match flag { + true => init, + _ => join, + } + } + + init = { + x = 1; + Goto(join) + } + + join = { + match flag { + true => use_x, + _ => done, + } + } + + use_x = { + Call(out = opaque::(Move(x)), ReturnTo(done), UnwindUnreachable()) + } + + done = { + Return() + } + } +} diff --git a/tests/mir-opt/move-elimination/storage.shorten_non_borrowed.MoveElimination.diff b/tests/mir-opt/move-elimination/storage.shorten_non_borrowed.MoveElimination.diff new file mode 100644 index 0000000000000..67a6b0ef87618 --- /dev/null +++ b/tests/mir-opt/move-elimination/storage.shorten_non_borrowed.MoveElimination.diff @@ -0,0 +1,79 @@ +- // MIR for `shorten_non_borrowed` before MoveElimination ++ // MIR for `shorten_non_borrowed` after MoveElimination + + fn shorten_non_borrowed(_1: u32) -> () { + debug x => _1; + let mut _0: (); + let _2: u32; + let mut _3: u32; + let _5: u32; + let mut _6: u32; + let _7: u32; + let mut _8: u32; + scope 1 { +- debug a => _2; ++ debug a => _6; + let _4: u32; + scope 2 { +- debug b => _4; ++ debug b => _6; + } + } + + bb0: { +- StorageLive(_2); ++ nop; ++ nop; + StorageLive(_3); + _3 = copy _1; +- _2 = opaque::(move _3) -> [return: bb1, unwind unreachable]; ++ StorageLive(_6); ++ _6 = opaque::(move _3) -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_3); +- StorageLive(_4); +- _4 = copy _2; ++ nop; ++ nop; ++ nop; ++ nop; ++ nop; ++ nop; + StorageLive(_5); +- StorageLive(_6); +- _6 = copy _4; + _5 = opaque::(move _6) -> [return: bb2, unwind unreachable]; + } + + bb2: { +- StorageDead(_6); + StorageDead(_5); ++ StorageDead(_6); ++ nop; ++ nop; ++ nop; ++ nop; ++ nop; + StorageLive(_7); +- StorageLive(_8); +- _8 = copy _1; +- _7 = opaque::(move _8) -> [return: bb3, unwind unreachable]; ++ _7 = opaque::(move _1) -> [return: bb3, unwind unreachable]; + } + + bb3: { +- StorageDead(_8); + StorageDead(_7); ++ nop; ++ nop; + _0 = const (); +- StorageDead(_4); +- StorageDead(_2); ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/move-elimination/storage.storage_dead_before_return.MoveElimination.diff b/tests/mir-opt/move-elimination/storage.storage_dead_before_return.MoveElimination.diff new file mode 100644 index 0000000000000..bc6299c97b6be --- /dev/null +++ b/tests/mir-opt/move-elimination/storage.storage_dead_before_return.MoveElimination.diff @@ -0,0 +1,17 @@ +- // MIR for `storage_dead_before_return` before MoveElimination ++ // MIR for `storage_dead_before_return` after MoveElimination + + fn storage_dead_before_return() -> *const u32 { + debug x => _1; + let mut _0: *const u32; + let mut _1: u32; + + bb0: { ++ StorageLive(_1); + _1 = const 1_u32; + _0 = &raw const _1; ++ StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/move-elimination/storage.storage_live_elided_on_join_fork.MoveElimination.diff b/tests/mir-opt/move-elimination/storage.storage_live_elided_on_join_fork.MoveElimination.diff new file mode 100644 index 0000000000000..d5b9552b4fdf9 --- /dev/null +++ b/tests/mir-opt/move-elimination/storage.storage_live_elided_on_join_fork.MoveElimination.diff @@ -0,0 +1,36 @@ +- // MIR for `storage_live_elided_on_join_fork` before MoveElimination ++ // MIR for `storage_live_elided_on_join_fork` after MoveElimination + + fn storage_live_elided_on_join_fork(_1: bool) -> () { + debug x => _2; + let mut _0: (); + let mut _2: u32; + let mut _3: u32; + + bb0: { + switchInt(copy _1) -> [1: bb1, otherwise: bb2]; + } + + bb1: { ++ StorageLive(_2); + _2 = const 1_u32; + goto -> bb2; + } + + bb2: { + switchInt(copy _1) -> [1: bb3, otherwise: bb4]; + } + + bb3: { ++ StorageLive(_3); + _3 = opaque::(move _2) -> [return: bb4, unwind unreachable]; + } + + bb4: { ++ StorageDead(_2); ++ StorageDead(_2); ++ StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/move-elimination/storage.storage_live_moved_to_branch.MoveElimination.diff b/tests/mir-opt/move-elimination/storage.storage_live_moved_to_branch.MoveElimination.diff new file mode 100644 index 0000000000000..9a7c3df9cb98c --- /dev/null +++ b/tests/mir-opt/move-elimination/storage.storage_live_moved_to_branch.MoveElimination.diff @@ -0,0 +1,63 @@ +- // MIR for `storage_live_moved_to_branch` before MoveElimination ++ // MIR for `storage_live_moved_to_branch` after MoveElimination + + fn storage_live_moved_to_branch(_1: bool) -> () { + debug flag => _1; + let mut _0: (); + let _2: u32; + let mut _3: bool; + let _4: u32; + let mut _5: u32; + scope 1 { +- debug x => _2; ++ debug x => _5; + } + + bb0: { +- StorageLive(_2); +- StorageLive(_3); +- _3 = copy _1; +- switchInt(move _3) -> [0: bb3, otherwise: bb1]; ++ nop; ++ nop; ++ nop; ++ switchInt(move _1) -> [0: bb3, otherwise: bb1]; + } + + bb1: { +- _2 = const 1_u32; +- StorageLive(_4); + StorageLive(_5); +- _5 = copy _2; ++ _5 = const 1_u32; ++ nop; ++ nop; ++ nop; ++ StorageLive(_4); + _4 = opaque::(move _5) -> [return: bb2, unwind unreachable]; + } + + bb2: { +- StorageDead(_5); + StorageDead(_4); ++ StorageDead(_5); ++ nop; ++ nop; + _0 = const (); + goto -> bb4; + } + + bb3: { + _0 = const (); + goto -> bb4; + } + + bb4: { +- StorageDead(_3); +- StorageDead(_2); ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/move-elimination/storage.terminator_end_storage_dead_in_successor.MoveElimination.diff b/tests/mir-opt/move-elimination/storage.terminator_end_storage_dead_in_successor.MoveElimination.diff new file mode 100644 index 0000000000000..ffc1f04c58097 --- /dev/null +++ b/tests/mir-opt/move-elimination/storage.terminator_end_storage_dead_in_successor.MoveElimination.diff @@ -0,0 +1,57 @@ +- // MIR for `terminator_end_storage_dead_in_successor` before MoveElimination ++ // MIR for `terminator_end_storage_dead_in_successor` after MoveElimination + + fn terminator_end_storage_dead_in_successor(_1: u32) -> u32 { + debug x => _1; + let mut _0: u32; + let _2: u32; + let mut _3: u32; + let mut _5: u32; + scope 1 { +- debug tmp => _2; ++ debug tmp => _5; + let _4: u32; + scope 2 { +- debug out => _4; ++ debug out => _0; + } + } + + bb0: { +- StorageLive(_2); +- StorageLive(_3); +- _3 = copy _1; +- _2 = opaque::(move _3) -> [return: bb1, unwind unreachable]; ++ nop; ++ nop; ++ nop; ++ StorageLive(_5); ++ _5 = opaque::(move _1) -> [return: bb1, unwind unreachable]; + } + + bb1: { +- StorageDead(_3); +- StorageLive(_4); +- StorageLive(_5); +- _5 = copy _2; +- _4 = opaque::(move _5) -> [return: bb2, unwind unreachable]; ++ nop; ++ nop; ++ nop; ++ nop; ++ _0 = opaque::(move _5) -> [return: bb2, unwind unreachable]; + } + + bb2: { + StorageDead(_5); +- _0 = copy _4; +- StorageDead(_4); +- StorageDead(_2); ++ nop; ++ nop; ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/move-elimination/storage_disabled.disabled.MoveElimination.diff b/tests/mir-opt/move-elimination/storage_disabled.disabled.MoveElimination.diff new file mode 100644 index 0000000000000..a90e345fdb701 --- /dev/null +++ b/tests/mir-opt/move-elimination/storage_disabled.disabled.MoveElimination.diff @@ -0,0 +1,18 @@ +- // MIR for `disabled` before MoveElimination ++ // MIR for `disabled` after MoveElimination + + fn disabled() -> *const u32 { + let mut _0: *const u32; + let mut _1: u32; + + bb0: { +- StorageLive(_1); ++ nop; + _1 = const 1_u32; + _0 = &raw const _1; +- StorageDead(_1); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/move-elimination/storage_disabled.rs b/tests/mir-opt/move-elimination/storage_disabled.rs new file mode 100644 index 0000000000000..223861fd51594 --- /dev/null +++ b/tests/mir-opt/move-elimination/storage_disabled.rs @@ -0,0 +1,30 @@ +//@ test-mir-pass: MoveElimination +//@ compile-flags: -Copt-level=0 -Cpanic=abort + +#![feature(custom_mir, core_intrinsics)] + +use std::intrinsics::mir::*; + +// With storage reconstruction disabled, existing storage markers are removed +// without inserting new ones. +// EMIT_MIR storage_disabled.disabled.MoveElimination.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn disabled() -> *const u32 { + // CHECK-LABEL: fn disabled( + // CHECK: bb0: { + // CHECK-NOT: Storage + // CHECK: [[X:_[0-9]+]] = const 1_u32; + // CHECK-NOT: Storage + // CHECK: return; + // CHECK-NOT: Storage + mir! { + let x: u32; + { + StorageLive(x); + x = 1; + RET = &raw const x; + StorageDead(x); + Return() + } + } +} diff --git a/tests/mir-opt/move-elimination/unwind.call_destination.MoveElimination.diff b/tests/mir-opt/move-elimination/unwind.call_destination.MoveElimination.diff index 7507f7bc81e7e..618e5b531b85e 100644 --- a/tests/mir-opt/move-elimination/unwind.call_destination.MoveElimination.diff +++ b/tests/mir-opt/move-elimination/unwind.call_destination.MoveElimination.diff @@ -8,19 +8,25 @@ let mut _3: *const u32; bb0: { ++ StorageLive(_1); _1 = const 0_u32; ++ StorageLive(_3); _3 = &raw const _1; ++ StorageDead(_3); _1 = opaque() -> [return: bb1, unwind: bb2]; } bb1: { _0 = (); ++ StorageDead(_1); return; } bb2 (cleanup): { ++ StorageLive(_2); _2 = const 2_u32; _1 = move _2; ++ StorageDead(_2); resume; } } From 1ef87c6bcd2880e8d250d708e2b93118d6ae8c35 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 28 Oct 2026 16:58:02 +0100 Subject: [PATCH 16/21] do not prefer trivially true candidates --- .../src/solve/assembly/mod.rs | 4 +- .../src/solve/eval_ctxt/mod.rs | 63 ++++++++++++- .../rustc_next_trait_solver/src/solve/mod.rs | 93 ++++++------------- .../src/solve/trait_goals.rs | 40 ++------ ...-bound-eq-modulo-placeholder-constraint.rs | 29 ++++++ .../cycles/inductive-cycle-but-ok.rs | 44 --------- ...cycle-discarded-coinductive-constraints.rs | 36 ------- 7 files changed, 124 insertions(+), 185 deletions(-) create mode 100644 tests/ui/traits/next-solver/assembly/item-bound-eq-modulo-placeholder-constraint.rs delete mode 100644 tests/ui/traits/next-solver/cycles/inductive-cycle-but-ok.rs delete mode 100644 tests/ui/traits/next-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index dcab703546a7e..a06b0625654c2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -1339,7 +1339,7 @@ where candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_))); } - if let Some((response, _)) = self.try_merge_candidates(&candidates) { + if let Some(response) = self.try_merge_candidates(&candidates) { Ok(response) } else { self.flounder(&candidates).map_err(Into::into) @@ -1361,7 +1361,7 @@ where // means we can just ignore inference constraints and don't have to special-case // constraining the normalized-to `term`. self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates); - if let Some((response, _)) = self.try_merge_candidates(&candidates) { + if let Some(response) = self.try_merge_candidates(&candidates) { Ok(response) } else { self.flounder(&candidates).map_err(Into::into) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 16d8289fc9b2d..13281ad706003 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -18,9 +18,10 @@ use rustc_type_ir::solve::{ }; use rustc_type_ir::transmute::Answer; use rustc_type_ir::{ - self as ty, ClauseKind, Const, InferCtxtLike, Interner, MayBeErased, OpaqueTypeKey, - PredicateKind, PredicateProxy, Region, RegionVid, TypeFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, max_universe, + self as ty, CanonicalQueryInput, ClauseKind, Const, InferCtxtLike, Interner, MayBeErased, + OpaqueTypeKey, PredicateKind, PredicateProxy, Region, RegionVid, TypeFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, + TypingModeEqWrapper, max_universe, }; use thin_vec::ThinVec; use tracing::{Level, debug, instrument, trace, warn}; @@ -583,6 +584,62 @@ where ) } + /// Whether two canonical responses are exactly equal except for their + /// region constraints. + pub(super) fn responses_equal_modulo_region_constraints( + &self, + a: &CanonicalResponse, + b: &CanonicalResponse, + ) -> bool { + if a == b { + return true; + } + + // We currently only ever use this function if `a` doesn't have region constraints. + // This means we only recanonicalize `b`. Could change this if necessary. + debug_assert!(a.value.external_constraints.region_constraints.is_empty()); + + // This is quite ugly. We instantiate the response as if it were a query input + // and then recanonicalize it without its region constraints. + // + // We can't simply compare all other fields of the response here as there can + // be some inference variables and placeholders created inside of a query which + // are only mentioned in the region constraints. This then changes the indices + // of things canonicalized later. + let cx: I = self.cx(); + let input: CanonicalQueryInput> = CanonicalQueryInput { + canonical: b.clone(), + typing_mode: TypingModeEqWrapper(self.typing_mode()), + }; + let (ref delegate, instantiated_response, _) = D::build_with_canonical(cx, &input); + let Response { certainty, var_values, var_values_is_identity, external_constraints } = + instantiated_response; + let ExternalConstraintsData { + region_constraints: _, + opaque_types, + normalization_nested_goals, + } = &*external_constraints; + let b_no_regions = canonicalize_response( + delegate, + ty::UniverseIndex::ROOT, + Response { + certainty, + var_values, + var_values_is_identity, + external_constraints: self.cx().mk_external_constraints(ExternalConstraintsData { + region_constraints: match cx.assumptions_on_binders() { + true => ExternalRegionConstraints::NextGen(RegionConstraint::new_true()), + false => ExternalRegionConstraints::Old(vec![]), + }, + opaque_types: opaque_types.clone(), + normalization_nested_goals: normalization_nested_goals.clone(), + }), + }, + ); + + a == &b_no_regions + } + pub(super) fn ignore_candidate_head_usages(&mut self, usages: CandidateHeadUsages) { self.search_graph.ignore_candidate_head_usages(usages); } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 4225280ea5b93..d46d40a0ec31a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -82,35 +82,6 @@ fn has_only_region_constraints(response: ty::Canonical( - a: &CanonicalResponse, - b: &CanonicalResponse, -) -> bool { - let CanonicalResponse { - max_universe: a_max_universe, - var_kinds: a_var_kinds, - value: - Response { - certainty: a_certainty, - var_values: a_var_values, - var_values_is_identity: _, // ignore because it's purely derived from `var_values` - external_constraints: a_external_constraints, - }, - } = a; - - let ExternalConstraintsData { region_constraints: _, opaque_types, normalization_nested_goals } = - &**a_external_constraints; - - a_max_universe == &b.max_universe - && a_var_kinds == &b.var_kinds - && a_var_values == &b.value.var_values - && a_certainty == &b.value.certainty - && opaque_types == &b.value.external_constraints.opaque_types - && normalization_nested_goals == &b.value.external_constraints.normalization_nested_goals -} - impl<'a, D, I> EvalCtxt<'a, D> where D: SolverDelegate, @@ -310,62 +281,50 @@ where } } -#[derive(Debug)] -enum MergeCandidateInfo { - AlwaysApplicable(usize), - EqualResponse, -} - impl EvalCtxt<'_, D> where D: SolverDelegate, I: Interner, { /// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`. + /// We only merge candidates which are equal modulo region constraints. Long-term, we want to + /// emit an OR of the constraints of all candidates, for now we fail with ambiguity unless all + /// candidates have the same constraints or one candidate has no region constraints at all. /// /// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`. #[instrument(level = "trace", skip(self), ret)] fn try_merge_candidates( &mut self, candidates: &[Candidate], - ) -> Option<(CanonicalResponse, MergeCandidateInfo)> { - if candidates.is_empty() { + ) -> Option> { + let Some((first, rest)) = candidates.split_first() else { return None; - } - - let always_applicable = candidates.iter().enumerate().find(|(_, candidate)| { - candidate.result.value.certainty == Certainty::Yes - && has_no_inference_or_external_constraints(candidate.result) - }); + }; - if let Some((i, candidate)) = always_applicable { - return Some((candidate.result, MergeCandidateInfo::AlwaysApplicable(i))); + // If all candidates are equal, just return that result. + if rest.iter().all(|c| first.result == c.result) { + return Some(first.result); } - let one: CanonicalResponse = candidates[0].result; - - if candidates[1..] + // If not, check whether one of the candidates has no region constraints. + // Long-term we likely want to also handle cases where one candidate has + // strictly weaker region constraints or emit an OR of the region constraints + // if candidates are otherwise equal. + // + // While we could also prefer candidates with weaker non-region constraints, + // that's currently not necessary. We intentionally do not prefer candidates + // if there's still am ambiguous candidate. We do so to potentially better + // handle region uniquification at the start of MIR borrowck, see #163928 for + // more details. + if let Some(choice) = candidates .iter() - .all(|candidate| equal_response_modulo_region_constraints(&one, &candidate.result)) + .find(|c| c.result.value.external_constraints.region_constraints.is_empty()) { - let region_constraints = &one.value.external_constraints.region_constraints; - if candidates[1..].iter().all(|candidate| { - &candidate.result.value.external_constraints.region_constraints - == region_constraints - }) { - return Some((one, MergeCandidateInfo::EqualResponse)); - } - - // If candidates differ only in region constraints, their merged region - // constraints are an `Or` of their respective constraints. If one of them - // has no region constraints, the `Or` constraint evaluates to `true`. - // - // This is a special case of `-Zassumptions-on-binders` and should be - // replaced eventually. - if let Some(candidate) = candidates.iter().find(|candidate| { - candidate.result.value.external_constraints.region_constraints.is_empty() - }) { - return Some((candidate.result, MergeCandidateInfo::EqualResponse)); + if candidates + .iter() + .all(|c| self.responses_equal_modulo_region_constraints(&choice.result, &c.result)) + { + return Some(choice.result); } } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 38bdaa898c455..5ae19b2eb6935 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -24,7 +24,7 @@ use crate::solve::assembly::{ use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, - MergeCandidateInfo, NoSolution, ParamEnvSource, StalledOnCoroutines, flatten_answer_tree, + NoSolution, ParamEnvSource, StalledOnCoroutines, flatten_answer_tree, has_only_region_constraints, }; @@ -1600,7 +1600,7 @@ where .all(|candidate| matches!(candidate.source, CandidateSource::BuiltinImpl(_))); if is_marker || all_builtin { - self.try_merge_candidates(candidates).map(|(response, _)| response) + self.try_merge_candidates(candidates) } else if candidates.len() > 1 { None } else { @@ -1613,7 +1613,7 @@ where candidates: &[Candidate], proven_via: TraitGoalProvenVia, ) -> (CanonicalResponse, Option) { - if let Some((response, _)) = self.try_merge_candidates(candidates) { + if let Some(response) = self.try_merge_candidates(candidates) { (response, Some(proven_via)) } else { (self.bail_with_ambiguity(candidates), None) @@ -1678,6 +1678,7 @@ where let alias_bounds: Vec<_> = candidates .extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound(..))) .collect(); + candidates.into_iter().for_each(|c| self.ignore_candidate_head_usages(c.head_usages)); return Ok(self.merge_candidates_or_bail_with_ambiguity( &alias_bounds, TraitGoalProvenVia::AliasBound, @@ -1693,38 +1694,10 @@ where let where_bounds: Vec<_> = candidates .extract_if(.., |c| matches!(c.source, CandidateSource::ParamEnv(_))) .collect(); - let Some((response, info)) = self.try_merge_candidates(&where_bounds) else { + candidates.into_iter().for_each(|c| self.ignore_candidate_head_usages(c.head_usages)); + let Some(response) = self.try_merge_candidates(&where_bounds) else { return Ok((self.bail_with_ambiguity(&where_bounds), None)); }; - match info { - // If there's an always applicable candidate, the result of all - // other candidates does not matter. This means we can ignore - // them when checking whether we've reached a fixpoint. - // - // We always prefer the first always applicable candidate, even if a - // later candidate is also always applicable and would result in fewer - // reruns. We could slightly improve this by e.g. searching for another - // always applicable candidate which doesn't depend on any cycle heads. - // - // NOTE: This is optimization is observable in case there is an always - // applicable global candidate and another non-global candidate which only - // applies because of a provisional result. I can't even think of a test - // case where this would occur and even then, this would not be unsound. - // Supporting this makes the code more involved, so I am just going to - // ignore this for now. - MergeCandidateInfo::AlwaysApplicable(i) => { - for (j, c) in where_bounds.into_iter().enumerate() { - if i != j { - self.ignore_candidate_head_usages(c.head_usages) - } - } - // If a where-bound does not apply, we don't actually get a - // candidate for it. We manually track the head usages - // of all failed `ParamEnv` candidates instead. - self.ignore_candidate_head_usages(failed_candidate_info.param_env_head_usages); - } - MergeCandidateInfo::EqualResponse => {} - } return Ok((response, Some(TraitGoalProvenVia::ParamEnv))); } @@ -1733,6 +1706,7 @@ where let alias_bounds: Vec<_> = candidates .extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound(_))) .collect(); + candidates.into_iter().for_each(|c| self.ignore_candidate_head_usages(c.head_usages)); return Ok(self.merge_candidates_or_bail_with_ambiguity( &alias_bounds, TraitGoalProvenVia::AliasBound, diff --git a/tests/ui/traits/next-solver/assembly/item-bound-eq-modulo-placeholder-constraint.rs b/tests/ui/traits/next-solver/assembly/item-bound-eq-modulo-placeholder-constraint.rs new file mode 100644 index 0000000000000..7e994a96890fd --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/item-bound-eq-modulo-placeholder-constraint.rs @@ -0,0 +1,29 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// When proving `C::Val<'a>: for<'b> PartialEq>` there are +// two candidates: +// - `C::Val<'a>: PartialEq>` +// - `for<'b> C::Val<'a>: PartialEq>` +// +// The first candidate results in an `'!b == 'a` constraint while the +// second one has no region constraints. The constraint of the first +// candidate means the canonical response has an entry for `'b` in its +// `var_values`. This previously caused us to not prefer the second +// constraint. +// +// FIXME(trait-system-refactor-initiative#305): Now, actually, the first +// candidate should just result in a leak check failure, but doesn't. +// That's a separate issue though :> +trait Cursor { + type Val<'a>: PartialEq + for<'b> PartialEq>; +} +impl Cursor for &C { + // for<'b> C::Val<'a>: PartialEq> + // - C::Val<'a>: PartialEq>` + // - for<'b> C::Val<'a>: PartialEq> + type Val<'a> = C::Val<'a>; +} +fn main() {} diff --git a/tests/ui/traits/next-solver/cycles/inductive-cycle-but-ok.rs b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-ok.rs deleted file mode 100644 index edcf2e5472b49..0000000000000 --- a/tests/ui/traits/next-solver/cycles/inductive-cycle-but-ok.rs +++ /dev/null @@ -1,44 +0,0 @@ -//@ compile-flags: -Znext-solver -//@ check-pass -#![feature(trivial_bounds, marker_trait_attr)] -#![allow(trivial_bounds)] - -// This previously triggered a bug in the provisional cache. -// -// This has the proof tree -// - `Root: Trait` proven via impl -// - `MultipleCandidates: Trait` -// - candidate: overflow-impl -// - `Root: Trait` (inductive cycle ~> OVERFLOW) -// - candidate: trivial-impl ~> YES -// - merge respones ~> YES -// - `MultipleCandidates: Trait` (in provisional cache ~> OVERFLOW) -// -// We previously incorrectly treated the `MultipleCandidates: Trait` as -// overflow because it was in the cache and reached via an inductive cycle. -// It should be `YES`. - -struct Root; -struct MultipleCandidates; - -#[marker] -trait Trait {} -impl Trait for Root -where - MultipleCandidates: Trait, - MultipleCandidates: Trait, -{} - -// overflow-impl -impl Trait for MultipleCandidates -where - Root: Trait, -{} -// trivial-impl -impl Trait for MultipleCandidates {} - -fn impls_trait() {} - -fn main() { - impls_trait::(); -} diff --git a/tests/ui/traits/next-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs b/tests/ui/traits/next-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs deleted file mode 100644 index 527ca812efb8b..0000000000000 --- a/tests/ui/traits/next-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs +++ /dev/null @@ -1,36 +0,0 @@ -//@ check-pass -//@ compile-flags: -Znext-solver -#![feature(rustc_attrs, marker_trait_attr)] -#[rustc_coinductive] -trait Trait {} - -impl Trait for (T, U) -where - (U, T): Trait, - (T, U): Inductive, - (): ConstrainToU32, -{} - -trait ConstrainToU32 {} -impl ConstrainToU32 for () {} - -// We only prefer the candidate without an inductive cycle -// once the inductive cycle has the same constraints as the -// other goal. -#[marker] -trait Inductive {} -impl Inductive for (T, U) -where - (T, U): Trait, -{} - -impl Inductive for (u32, u32) {} - -fn impls_trait() -where - (T, U): Trait, -{} - -fn main() { - impls_trait::<_, _>(); -} From 614730d3aadd116d8a6a7c18dd55de07c24fa79f Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Sat, 26 Sep 2026 01:06:55 +0100 Subject: [PATCH 17/21] Don't hardcode basic block names in hint codegen tests --- .../hint/cold_path-target_feature.rs | 12 +++++----- tests/codegen-llvm/hint/cold_path.rs | 12 +++++----- tests/codegen-llvm/hint/likely.rs | 24 +++++++++---------- tests/codegen-llvm/hint/unlikely.rs | 24 +++++++++---------- tests/codegen-llvm/intrinsics/cold_path2.rs | 6 ++--- tests/codegen-llvm/intrinsics/likely.rs | 6 ++--- tests/codegen-llvm/intrinsics/unlikely.rs | 6 ++--- 7 files changed, 45 insertions(+), 45 deletions(-) diff --git a/tests/codegen-llvm/hint/cold_path-target_feature.rs b/tests/codegen-llvm/hint/cold_path-target_feature.rs index 9e06fd0ceafa1..899f8765ab17b 100644 --- a/tests/codegen-llvm/hint/cold_path-target_feature.rs +++ b/tests/codegen-llvm/hint/cold_path-target_feature.rs @@ -28,10 +28,10 @@ pub fn test1(x: bool) { } // CHECK-LABEL: @test1( - // CHECK: br i1 %x, label %bb1, label %bb2, !prof ![[NUM:[0-9]+]] - // CHECK: bb2: + // CHECK: br i1 %x, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM:[0-9]+]] + // CHECK: [[FALSE]]: // CHECK: path_b - // CHECK: bb1: + // CHECK: [[TRUE]]: // CHECK: path_a } @@ -46,10 +46,10 @@ pub fn with_target_feature(x: bool) { } // CHECK-LABEL: @with_target_feature( - // CHECK: br i1 %x, label %bb1, label %bb2, !prof ![[NUM]] - // CHECK: bb2: + // CHECK: br i1 %x, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM]] + // CHECK: [[FALSE]]: // CHECK: path_b - // CHECK: bb1: + // CHECK: [[TRUE]]: // CHECK: path_a } diff --git a/tests/codegen-llvm/hint/cold_path.rs b/tests/codegen-llvm/hint/cold_path.rs index e6e7379322ddf..c8da7af840790 100644 --- a/tests/codegen-llvm/hint/cold_path.rs +++ b/tests/codegen-llvm/hint/cold_path.rs @@ -25,10 +25,10 @@ pub fn test1(x: bool) { } // CHECK-LABEL: @test1( - // CHECK: br i1 %x, label %bb1, label %bb2, !prof ![[NUM:[0-9]+]] - // CHECK: bb2: + // CHECK: br i1 %x, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM:[0-9]+]] + // CHECK: [[FALSE]]: // CHECK: path_b - // CHECK: bb1: + // CHECK: [[TRUE]]: // CHECK: path_a } @@ -43,10 +43,10 @@ pub fn test2(x: i32) { } // CHECK-LABEL: @test2( - // CHECK: br i1 %_2, label %bb2, label %bb1, !prof ![[NUM]] - // CHECK: bb1: + // CHECK: br i1 %_2, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM]] + // CHECK: [[FALSE]]: // CHECK: path_b - // CHECK: bb2: + // CHECK: [[TRUE]]: // CHECK: path_a } diff --git a/tests/codegen-llvm/hint/likely.rs b/tests/codegen-llvm/hint/likely.rs index 75f9e7aae367d..3f1b2bfbb8091 100644 --- a/tests/codegen-llvm/hint/likely.rs +++ b/tests/codegen-llvm/hint/likely.rs @@ -25,10 +25,10 @@ pub fn test1(x: bool) { } // CHECK-LABEL: @test1( - // CHECK: br i1 %x, label %bb2, label %bb3, !prof ![[NUM:[0-9]+]] - // CHECK: bb3: + // CHECK: br i1 %x, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM:[0-9]+]] + // CHECK: [[FALSE]]: // CHECK: path_b - // CHECK: bb2: + // CHECK: [[TRUE]]: // CHECK: path_a } @@ -40,10 +40,10 @@ pub fn test2(x: i32) { } // CHECK-LABEL: @test2( - // CHECK: br i1 %_2, label %bb2, label %bb3, !prof ![[NUM]] - // CHECK: bb3: + // CHECK: br i1 %_2, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM]] + // CHECK: [[FALSE]]: // CHECK: path_b - // CHECK: bb2: + // CHECK: [[TRUE]]: // CHECK: path_a } @@ -55,10 +55,10 @@ pub fn test3(x: i8) { } // CHECK-LABEL: @test3( - // CHECK: br i1 %_2, label %bb2, label %bb3, !prof ![[NUM]] - // CHECK: bb3: + // CHECK: br i1 %_2, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM]] + // CHECK: [[FALSE]]: // CHECK: path_b - // CHECK: bb2: + // CHECK: [[TRUE]]: // CHECK: path_a } @@ -70,10 +70,10 @@ pub fn test4(x: u64) { } // CHECK-LABEL: @test4( - // CHECK: br i1 %0, label %bb3, label %bb2, !prof ![[NUM2:[0-9]+]] - // CHECK: bb3: + // CHECK: br i1 %0, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM2:[0-9]+]] + // CHECK: [[TRUE]]: // CHECK: path_a - // CHECK: bb2: + // CHECK: [[FALSE]]: // CHECK: path_b } diff --git a/tests/codegen-llvm/hint/unlikely.rs b/tests/codegen-llvm/hint/unlikely.rs index 248b1e2537e96..0376e2ebb23f4 100644 --- a/tests/codegen-llvm/hint/unlikely.rs +++ b/tests/codegen-llvm/hint/unlikely.rs @@ -25,10 +25,10 @@ pub fn test1(x: bool) { } // CHECK-LABEL: @test1( - // CHECK: br i1 %x, label %bb2, label %bb4, !prof ![[NUM:[0-9]+]] - // CHECK: bb4: + // CHECK: br i1 %x, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM:[0-9]+]] + // CHECK: [[FALSE]]: // CHECK: path_b - // CHECK: bb2: + // CHECK: [[TRUE]]: // CHECK: path_a } @@ -40,10 +40,10 @@ pub fn test2(x: i32) { } // CHECK-LABEL: @test2( - // CHECK: br i1 %_2, label %bb2, label %bb4, !prof ![[NUM]] - // CHECK: bb4: + // CHECK: br i1 %_2, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM]] + // CHECK: [[FALSE]]: // CHECK: path_b - // CHECK: bb2: + // CHECK: [[TRUE]]: // CHECK: path_a } @@ -55,10 +55,10 @@ pub fn test3(x: i8) { } // CHECK-LABEL: @test3( - // CHECK: br i1 %_2, label %bb2, label %bb4, !prof ![[NUM]] - // CHECK: bb4: + // CHECK: br i1 %_2, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM]] + // CHECK: [[FALSE]]: // CHECK: path_b - // CHECK: bb2: + // CHECK: [[TRUE]]: // CHECK: path_a } @@ -70,10 +70,10 @@ pub fn test4(x: u64) { } // CHECK-LABEL: @test4( - // CHECK: br i1 %0, label %bb4, label %bb2, !prof ![[NUM2:[0-9]+]] - // CHECK: bb4: + // CHECK: br i1 %0, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM2:[0-9]+]] + // CHECK: [[TRUE]]: // CHECK: path_a - // CHECK: bb2: + // CHECK: [[FALSE]]: // CHECK: path_b } diff --git a/tests/codegen-llvm/intrinsics/cold_path2.rs b/tests/codegen-llvm/intrinsics/cold_path2.rs index 0891c878fd9c5..e22bdbb96437b 100644 --- a/tests/codegen-llvm/intrinsics/cold_path2.rs +++ b/tests/codegen-llvm/intrinsics/cold_path2.rs @@ -27,10 +27,10 @@ pub fn test(x: Option) { // CHECK-LABEL: void @test(i8{{.+}}%x) // CHECK: %[[IS_NONE:.+]] = icmp eq i8 %x, 2 - // CHECK: br i1 %[[IS_NONE]], label %bb2, label %bb1, !prof ![[NUM:[0-9]+]] - // CHECK: bb1: + // CHECK: br i1 %[[IS_NONE]], label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM:[0-9]+]] + // CHECK: [[FALSE]]: // CHECK: path_a - // CHECK: bb2: + // CHECK: [[TRUE]]: // CHECK: path_b } diff --git a/tests/codegen-llvm/intrinsics/likely.rs b/tests/codegen-llvm/intrinsics/likely.rs index c5e3c466f4525..72fbe0c1b4b56 100644 --- a/tests/codegen-llvm/intrinsics/likely.rs +++ b/tests/codegen-llvm/intrinsics/likely.rs @@ -26,10 +26,10 @@ pub fn test_likely(x: bool) { } // CHECK-LABEL: @test_likely( -// CHECK: br i1 %x, label %bb2, label %bb3, !prof ![[NUM:[0-9]+]] -// CHECK: bb3: +// CHECK: br i1 %x, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM:[0-9]+]] +// CHECK: [[FALSE]]: // CHECK-NOT: cold_path // CHECK: path_b -// CHECK: bb2: +// CHECK: [[TRUE]]: // CHECK: path_a // CHECK: ![[NUM]] = !{!"branch_weights", {{(!"expected", )?}}i32 2000, i32 1} diff --git a/tests/codegen-llvm/intrinsics/unlikely.rs b/tests/codegen-llvm/intrinsics/unlikely.rs index 90ebf070d2700..6a95476e626dc 100644 --- a/tests/codegen-llvm/intrinsics/unlikely.rs +++ b/tests/codegen-llvm/intrinsics/unlikely.rs @@ -26,10 +26,10 @@ pub fn test_unlikely(x: bool) { } // CHECK-LABEL: @test_unlikely( -// CHECK: br i1 %x, label %bb2, label %bb4, !prof ![[NUM:[0-9]+]] -// CHECK: bb4: +// CHECK: br i1 %x, label %[[TRUE:bb[0-9]+]], label %[[FALSE:bb[0-9]+]], !prof ![[NUM:[0-9]+]] +// CHECK: [[FALSE]]: // CHECK: path_b -// CHECK: bb2: +// CHECK: [[TRUE]]: // CHECK-NOT: cold_path // CHECK: path_a // CHECK: ![[NUM]] = !{!"branch_weights", {{(!"expected", )?}}i32 1, i32 2000} From 9f6141e78128110ad9b267838be6a09e94551ca1 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Fri, 25 Sep 2026 21:23:05 +0100 Subject: [PATCH 18/21] Initialize ZST results in MIR construction and lowering --- compiler/rustc_mir_build/src/builder/cfg.rs | 6 +-- .../src/builder/expr/as_rvalue.rs | 6 +-- .../rustc_mir_build/src/builder/expr/into.rs | 7 +-- .../rustc_mir_transform/src/coroutine/drop.rs | 12 ++++- .../rustc_mir_transform/src/coroutine/mod.rs | 25 ++++++++-- .../rustc_mir_transform/src/instsimplify.rs | 32 ++++++++++-- .../src/lower_intrinsics.rs | 49 ++++++++++++++----- .../rustc_mir_transform/src/promote_consts.rs | 9 ++-- compiler/rustc_mir_transform/src/shim.rs | 27 +++++++--- .../src/shim/async_destructor_ctor.rs | 11 ++++- ...move.box_new.CleanupPostBorrowck.after.mir | 1 + ...sure#0}.AsyncEnum.MentionedItems.after.mir | 1 + ...-{closure#0}.AsyncEnum.StateTransform.diff | 2 + ...osure#0}.AsyncInt.MentionedItems.after.mir | 1 + ...e-{closure#0}.AsyncInt.StateTransform.diff | 2 + ...syncReference_'__.MentionedItems.after.mir | 1 + ...#0}.AsyncReference_'__.StateTransform.diff | 2 + ...re#0}.AsyncStruct.MentionedItems.after.mir | 1 + ...closure#0}.AsyncStruct.StateTransform.diff | 2 + ...e-{closure#0}.Int.MentionedItems.after.mir | 1 + ..._place-{closure#0}.Int.StateTransform.diff | 2 + ...losure#0}.SyncInt.MentionedItems.after.mir | 1 + ...ce-{closure#0}.SyncInt.StateTransform.diff | 2 + ...#0}.SyncThenAsync.MentionedItems.after.mir | 1 + ...osure#0}.SyncThenAsync.StateTransform.diff | 2 + ...losure#0}.[AsyncInt;2].StateTransform.diff | 2 + ...losure#0}.[Foo;1].MentionedItems.after.mir | 1 + ...losure#0}-{closure#0}.coroutine_drop.0.mir | 1 + ...nc_fn.add-{closure#0}.coroutine_drop.0.mir | 1 + ...aggregate-{closure#0}.coroutine_drop.0.mir | 1 + ...losure#0}-{closure#0}.coroutine_drop.0.mir | 1 + ...nc_fn.foo-{closure#0}.coroutine_drop.0.mir | 1 + ...llo_world-{closure#0}.coroutine_drop.0.mir | 1 + ...losure#0}-{closure#0}.coroutine_drop.0.mir | 1 + ...losure#0}-{closure#1}.coroutine_drop.0.mir | 1 + ...des_never-{closure#0}.coroutine_drop.0.mir | 1 + ...losure#0}-{closure#0}.coroutine_drop.0.mir | 1 + ...tial_init-{closure#0}.coroutine_drop.0.mir | 1 + ...d_variant-{closure#0}.coroutine_drop.0.mir | 1 + ...losure#0}.coroutine_drop.0.panic-abort.mir | 1 + ...osure#0}.coroutine_drop.0.panic-unwind.mir | 1 + ...empty_drop_glue.slice_in_place.Inline.diff | 1 + .../inline_shims.drop.Inline.panic-abort.diff | 2 + ...inline_shims.drop.Inline.panic-unwind.diff | 1 + ...ovable.InstSimplify-after-simplifycfg.diff | 3 ++ ...cs.assume.LowerIntrinsics.panic-abort.diff | 1 + ...s.assume.LowerIntrinsics.panic-unwind.diff | 1 + ...erlapping.LowerIntrinsics.panic-abort.diff | 1 + ...rlapping.LowerIntrinsics.panic-unwind.diff | 1 + tests/mir-opt/lower_intrinsics.rs | 2 + ...a_range.on_assume.SsaRangePropagation.diff | 1 + ...ring;42].AddMovesForPackedDrops.before.mir | 1 + ...[String].AddMovesForPackedDrops.before.mir | 1 + ...Vec_i32_.AddMovesForPackedDrops.before.mir | 1 + 54 files changed, 190 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/cfg.rs b/compiler/rustc_mir_build/src/builder/cfg.rs index 020858d427712..75377f761f401 100644 --- a/compiler/rustc_mir_build/src/builder/cfg.rs +++ b/compiler/rustc_mir_build/src/builder/cfg.rs @@ -75,11 +75,7 @@ impl<'tcx> CFG<'tcx> { source_info, place, Rvalue::Use( - Operand::Constant(Box::new(ConstOperand { - span: source_info.span, - user_ty: None, - const_: Const::zero_sized(tcx.types.unit), - })), + Operand::zero_sized_constant(tcx.types.unit, source_info.span), WithRetag::Yes, ), ); diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index 80e6ed2036798..013be91d1669a 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -347,11 +347,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { block = this.stmt_expr(block, expr_id, None).into_block(); block.and(Rvalue::Use( - Operand::Constant(Box::new(ConstOperand { - span: expr_span, - user_ty: None, - const_: Const::zero_sized(this.tcx.types.unit), - })), + Operand::zero_sized_constant(this.tcx.types.unit, expr_span), WithRetag::Yes, )) } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 167bedfc2101e..86f781f35f60d 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -418,8 +418,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { sym::write_via_move => { // `write_via_move(ptr, val)` becomes `*ptr = val` but without any dropping. - // The destination must have unit type (so we don't actually have to store anything - // into it). + // The destination must have unit type. assert!(destination.ty(&this.local_decls, this.tcx).ty.is_unit()); // Compile this to an assignment of the argument into the destination. @@ -431,7 +430,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span_bug!(expr_span, "invalid write_via_move call") }; let ptr_deref = ptr.project_deeper(&[ProjectionElem::Deref], this.tcx); - this.expr_into_dest(ptr_deref, block, val) + unpack!(block = this.expr_into_dest(ptr_deref, block, val)); + this.cfg.push_assign_unit(block, source_info, destination, this.tcx); + block.unit() } sym::write_box_via_move => { // The signature is: diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs index ce44e47325879..c6bd539eb6eee 100644 --- a/compiler/rustc_mir_transform/src/coroutine/drop.rs +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -208,8 +208,18 @@ pub(super) fn create_coroutine_drop_shim<'tcx>( } } - // Replace the return variable + // Replace the return variable and initialize it once for all return paths. body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(tcx.types.unit, source_info); + body.basic_blocks_mut()[START_BLOCK].statements.push(Statement::new( + source_info, + StatementKind::Assign(Box::new(( + Place::return_place(), + Rvalue::Use( + Operand::zero_sized_constant(tcx.types.unit, source_info.span), + WithRetag::Yes, + ), + ))), + )); make_coroutine_state_argument_indirect(tcx, &mut body); diff --git a/compiler/rustc_mir_transform/src/coroutine/mod.rs b/compiler/rustc_mir_transform/src/coroutine/mod.rs index 1162399b539b4..d3f43b7d298ea 100644 --- a/compiler/rustc_mir_transform/src/coroutine/mod.rs +++ b/compiler/rustc_mir_transform/src/coroutine/mod.rs @@ -749,11 +749,7 @@ fn return_poll_ready_assign<'tcx>(tcx: TyCtxt<'tcx>, source_info: SourceInfo) -> // Poll::Ready(()) let poll_def_id = tcx.require_lang_item(LangItem::Poll, source_info.span); let args = tcx.mk_args(&[tcx.types.unit.into()]); - let val = Operand::Constant(Box::new(ConstOperand { - span: source_info.span, - user_ty: None, - const_: Const::zero_sized(tcx.types.unit), - })); + let val = Operand::zero_sized_constant(tcx.types.unit, source_info.span); let ready_val = Rvalue::Aggregate( Box::new(AggregateKind::Adt(poll_def_id, VariantIdx::from_usize(0), args, None, None)), indexvec![val], @@ -950,6 +946,25 @@ fn create_coroutine_resume_function<'tcx>( } } + // Iterator::next has no resume argument, so initialize the unit resume + // local at the start of each invocation. + if let CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) = transform.coroutine_kind { + let source_info = SourceInfo::outermost(body.span); + body.basic_blocks_mut()[START_BLOCK].statements.insert( + 0, + Statement::new( + source_info, + StatementKind::Assign(Box::new(( + CTX_ARG.into(), + Rvalue::Use( + Operand::zero_sized_constant(tcx.types.unit, source_info.span), + WithRetag::Yes, + ), + ))), + ), + ); + } + // Make sure we remove dead blocks to remove // unrelated code from the drop part of the function simplify::remove_dead_blocks(body); diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index f1a31db86c6dc..bd739026f4283 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -58,7 +58,7 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { ctx.simplify_primitive_clone(terminator, &mut block.statements); ctx.simplify_size_or_align_of_val(terminator, &mut block.statements); ctx.simplify_raw_eq(terminator, &mut block.statements); - ctx.simplify_intrinsic_assert(terminator); + ctx.simplify_intrinsic_assert(terminator, &mut block.statements); ctx.simplify_nounwind_call(terminator); simplify_duplicate_switch_targets(terminator); } @@ -396,9 +396,17 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { } } - fn simplify_intrinsic_assert(&self, terminator: &mut Terminator<'tcx>) { - let TerminatorKind::Call { ref func, target: ref mut target @ Some(target_block), .. } = - terminator.kind + fn simplify_intrinsic_assert( + &self, + terminator: &mut Terminator<'tcx>, + statements: &mut Vec>, + ) { + let TerminatorKind::Call { + ref func, + destination, + target: ref mut target @ Some(target_block), + .. + } = terminator.kind else { return; }; @@ -419,7 +427,21 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { *target = None; } Some(false) => { - // If we know the assert does not panic, turn the call into a Goto + // If we know the assert does not panic, turn the call into a Goto. We still need to + // initialize its unit result. + statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( + destination, + Rvalue::Use( + Operand::zero_sized_constant( + self.tcx.types.unit, + terminator.source_info.span, + ), + WithRetag::Yes, + ), + ))), + )); terminator.kind = TerminatorKind::goto(target_block); } } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 3ee8c405a1d77..369ef0a6849bc 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -47,11 +47,10 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { StatementKind::Assign(Box::new(( *destination, Rvalue::Use( - Operand::Constant(Box::new(ConstOperand { - span: terminator.source_info.span, - user_ty: None, - const_: Const::zero_sized(tcx.types.unit), - })), + Operand::zero_sized_constant( + tcx.types.unit, + terminator.source_info.span, + ), WithRetag::Yes, ), ))), @@ -61,20 +60,31 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { sym::copy_nonoverlapping => { let target = target.unwrap(); let Ok([src, dst, count]) = take_array(args) else { - bug!("Wrong arguments for copy_non_overlapping intrinsic"); + bug!("Wrong arguments for copy_nonoverlapping intrinsic"); }; block.statements.push(Statement::new( terminator.source_info, StatementKind::Intrinsic(Box::new( - NonDivergingIntrinsic::CopyNonOverlapping( - rustc_middle::mir::CopyNonOverlapping { - src: src.node, - dst: dst.node, - count: count.node, - }, - ), + NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { + src: src.node, + dst: dst.node, + count: count.node, + }), )), )); + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( + *destination, + Rvalue::Use( + Operand::zero_sized_constant( + tcx.types.unit, + terminator.source_info.span, + ), + WithRetag::Yes, + ), + ))), + )); terminator.kind = TerminatorKind::goto(target); } sym::assume => { @@ -88,6 +98,19 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { arg.node, ))), )); + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( + *destination, + Rvalue::Use( + Operand::zero_sized_constant( + tcx.types.unit, + terminator.source_info.span, + ), + WithRetag::Yes, + ), + ))), + )); terminator.kind = TerminatorKind::goto(target); } sym::wrapping_add diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 463cca7214edb..7ad164956cbfd 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -814,11 +814,10 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { rhs.clone() } else { let unit = Rvalue::Use( - Operand::Constant(Box::new(ConstOperand { - span: statement.source_info.span, - user_ty: None, - const_: Const::zero_sized(self.tcx.types.unit), - })), + Operand::zero_sized_constant( + self.tcx.types.unit, + statement.source_info.span, + ), WithRetag::Yes, ); mem::replace(rhs, unit) diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index f1d85166653ff..b436e73686a9e 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -278,13 +278,20 @@ pub fn build_drop_shim<'tcx>( let return_block = BasicBlock::new(1); let mut blocks = IndexVec::with_capacity(2); - let block = |blocks: &mut IndexVec<_, _>, kind| { + let block = |blocks: &mut IndexVec<_, _>, kind| -> BasicBlock { blocks.push(BasicBlockData::new(Some(Terminator { source_info, kind }), false)) }; if ty.is_some() { block(&mut blocks, TerminatorKind::goto(return_block)); } - block(&mut blocks, TerminatorKind::Return); + let ret = block(&mut blocks, TerminatorKind::Return); + blocks[ret].statements.push(Statement::new( + source_info, + StatementKind::Assign(Box::new(( + Place::return_place(), + Rvalue::Use(Operand::zero_sized_constant(tcx.types.unit, span), WithRetag::Yes), + ))), + )); let source = MirSource::from_shim(ty::ShimKind::DropGlue(def_id, ty)); let mut body = @@ -699,10 +706,18 @@ impl<'tcx> CloneShimBuilder<'tcx> { unwind } - fn tuple_like_shim(&mut self, dest: Place<'tcx>, src: Place<'tcx>, tys: I) - where - I: IntoIterator>, - { + fn tuple_like_shim( + &mut self, + dest: Place<'tcx>, + src: Place<'tcx>, + tys: &'tcx ty::List>, + ) { + if tys.is_empty() { + // With no fields to clone, initialize the return local directly. + self.copy_shim(); + return; + } + self.block(vec![], TerminatorKind::goto(self.block_index_offset(3)), false); let unwind = self.block(vec![], TerminatorKind::UnwindResume, true); let target = self.block(vec![], TerminatorKind::Return, false); diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index 7530716e8cf39..60a918e6854e1 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -84,7 +84,7 @@ pub(super) fn build_async_drop_shim<'tcx>( let return_block = BasicBlock::new(1); let mut blocks = IndexVec::with_capacity(2); - let block = |blocks: &mut IndexVec<_, _>, kind| { + let block = |blocks: &mut IndexVec<_, _>, kind| -> BasicBlock { blocks.push(BasicBlockData::new(Some(Terminator { source_info, kind }), false)) }; block( @@ -101,7 +101,14 @@ pub(super) fn build_async_drop_shim<'tcx>( TerminatorKind::goto(return_block) }, ); - block(&mut blocks, TerminatorKind::Return); + let ret = block(&mut blocks, TerminatorKind::Return); + blocks[ret].statements.push(Statement::new( + source_info, + StatementKind::Assign(Box::new(( + Place::return_place(), + Rvalue::Use(Operand::zero_sized_constant(tcx.types.unit, span), WithRetag::Yes), + ))), + )); let source = MirSource::from_shim(ty::ShimKind::AsyncDropGlue(def_id, ty)); let mut body = diff --git a/tests/mir-opt/building/write_via_move.box_new.CleanupPostBorrowck.after.mir b/tests/mir-opt/building/write_via_move.box_new.CleanupPostBorrowck.after.mir index 6a6e984023b75..7a13128131664 100644 --- a/tests/mir-opt/building/write_via_move.box_new.CleanupPostBorrowck.after.mir +++ b/tests/mir-opt/building/write_via_move.box_new.CleanupPostBorrowck.after.mir @@ -44,6 +44,7 @@ fn box_new(_1: T) -> Box<[T; 1024]> { _8 = copy _1; (*_7) = [move _8; 1024]; StorageDead(_8); + _6 = const (); StorageDead(_7); StorageDead(_6); StorageLive(_9); diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncEnum.MentionedItems.after.mir b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncEnum.MentionedItems.after.mir index 0041816abd0ff..1932bd634b7b1 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncEnum.MentionedItems.after.mir +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncEnum.MentionedItems.after.mir @@ -59,6 +59,7 @@ yields () } bb1: { + _0 = const (); return; } diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncEnum.StateTransform.diff b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncEnum.StateTransform.diff index 8e6c68598e311..a558009584a82 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncEnum.StateTransform.diff +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncEnum.StateTransform.diff @@ -97,6 +97,8 @@ } bb1: { +- _0 = const (); ++ _51 = const (); + _0 = Poll::<()>::Ready(move _51); + discriminant((*_53)) = 1; return; diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncInt.MentionedItems.after.mir b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncInt.MentionedItems.after.mir index 533dc2634ff2a..c086325336db3 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncInt.MentionedItems.after.mir +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncInt.MentionedItems.after.mir @@ -29,6 +29,7 @@ yields () } bb1: { + _0 = const (); return; } diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncInt.StateTransform.diff b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncInt.StateTransform.diff index 015c275b529f5..57794b4318a5e 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncInt.StateTransform.diff +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncInt.StateTransform.diff @@ -54,6 +54,8 @@ } bb1: { +- _0 = const (); ++ _21 = const (); + _0 = Poll::<()>::Ready(move _21); + discriminant((*_23)) = 1; return; diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncReference_'__.MentionedItems.after.mir b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncReference_'__.MentionedItems.after.mir index fe1e2d66c2cbf..04426e8b66dfe 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncReference_'__.MentionedItems.after.mir +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncReference_'__.MentionedItems.after.mir @@ -29,6 +29,7 @@ yields () } bb1: { + _0 = const (); return; } diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncReference_'__.StateTransform.diff b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncReference_'__.StateTransform.diff index 93b76ad125bb3..c75cf831f9179 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncReference_'__.StateTransform.diff +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncReference_'__.StateTransform.diff @@ -54,6 +54,8 @@ } bb1: { +- _0 = const (); ++ _21 = const (); + _0 = Poll::<()>::Ready(move _21); + discriminant((*_23)) = 1; return; diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncStruct.MentionedItems.after.mir b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncStruct.MentionedItems.after.mir index dcb038b587155..f86d2baa056b0 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncStruct.MentionedItems.after.mir +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncStruct.MentionedItems.after.mir @@ -83,6 +83,7 @@ yields () } bb1: { + _0 = const (); return; } diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncStruct.StateTransform.diff b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncStruct.StateTransform.diff index 339661ef2ce1d..a1de1f8949e74 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncStruct.StateTransform.diff +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.AsyncStruct.StateTransform.diff @@ -124,6 +124,8 @@ } bb1: { +- _0 = const (); ++ _75 = const (); + _0 = Poll::<()>::Ready(move _75); + discriminant((*_77)) = 1; return; diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.Int.MentionedItems.after.mir b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.Int.MentionedItems.after.mir index 7823a862bbded..13d5e10d2855a 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.Int.MentionedItems.after.mir +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.Int.MentionedItems.after.mir @@ -10,6 +10,7 @@ yields () } bb1: { + _0 = const (); return; } } diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.Int.StateTransform.diff b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.Int.StateTransform.diff index be51f11489222..0e2b6b46a91f7 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.Int.StateTransform.diff +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.Int.StateTransform.diff @@ -22,6 +22,7 @@ + let mut _7: std::ptr::NonNull>; bb0: { +- _0 = const (); + _7 = move _2 as std::ptr::NonNull> (Transmute); + _6 = std::future::ResumeTy(move _7); + _5 = copy (_1.0: &mut {async fn body of std::future::async_drop_in_place()}); @@ -39,6 +40,7 @@ + } + + bb3: { ++ _3 = const (); + _0 = Poll::<()>::Ready(move _3); + discriminant((*_5)) = 1; return; diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncInt.MentionedItems.after.mir b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncInt.MentionedItems.after.mir index b958b843c6df0..91efeec1a5977 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncInt.MentionedItems.after.mir +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncInt.MentionedItems.after.mir @@ -12,6 +12,7 @@ yields () } bb1: { + _0 = const (); return; } } diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncInt.StateTransform.diff b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncInt.StateTransform.diff index 2d127c495aba2..cc759752b575b 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncInt.StateTransform.diff +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncInt.StateTransform.diff @@ -33,6 +33,8 @@ } bb1: { +- _0 = const (); ++ _4 = const (); + _0 = Poll::<()>::Ready(move _4); + discriminant((*_6)) = 1; return; diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncThenAsync.MentionedItems.after.mir b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncThenAsync.MentionedItems.after.mir index ad37224d34f9b..4e3b967186dab 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncThenAsync.MentionedItems.after.mir +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncThenAsync.MentionedItems.after.mir @@ -68,6 +68,7 @@ yields () } bb1: { + _0 = const (); return; } diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncThenAsync.StateTransform.diff b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncThenAsync.StateTransform.diff index 6a7ad149f1e82..985870f7e9576 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncThenAsync.StateTransform.diff +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.SyncThenAsync.StateTransform.diff @@ -106,6 +106,8 @@ } bb1: { +- _0 = const (); ++ _60 = const (); + _0 = Poll::<()>::Ready(move _60); + discriminant((*_62)) = 1; return; diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.[AsyncInt;2].StateTransform.diff b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.[AsyncInt;2].StateTransform.diff index 7718554895afd..6bcef9a331526 100644 --- a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.[AsyncInt;2].StateTransform.diff +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.[AsyncInt;2].StateTransform.diff @@ -84,6 +84,8 @@ } bb1: { +- _0 = const (); ++ _41 = const (); + _0 = Poll::<()>::Ready(move _41); + discriminant((*_43)) = 1; return; diff --git a/tests/mir-opt/coroutine/async_drop_mir_pin.core.future-async_drop-async_drop_in_place-{closure#0}.[Foo;1].MentionedItems.after.mir b/tests/mir-opt/coroutine/async_drop_mir_pin.core.future-async_drop-async_drop_in_place-{closure#0}.[Foo;1].MentionedItems.after.mir index e06189e941211..e9036bf114b03 100644 --- a/tests/mir-opt/coroutine/async_drop_mir_pin.core.future-async_drop-async_drop_in_place-{closure#0}.[Foo;1].MentionedItems.after.mir +++ b/tests/mir-opt/coroutine/async_drop_mir_pin.core.future-async_drop-async_drop_in_place-{closure#0}.[Foo;1].MentionedItems.after.mir @@ -49,6 +49,7 @@ yields () } bb1: { + _0 = const (); return; } diff --git a/tests/mir-opt/coroutine/async_fn.add-{closure#0}-{closure#0}.coroutine_drop.0.mir b/tests/mir-opt/coroutine/async_fn.add-{closure#0}-{closure#0}.coroutine_drop.0.mir index 4dec40924719b..ff2b0a4812478 100644 --- a/tests/mir-opt/coroutine/async_fn.add-{closure#0}-{closure#0}.coroutine_drop.0.mir +++ b/tests/mir-opt/coroutine/async_fn.add-{closure#0}-{closure#0}.coroutine_drop.0.mir @@ -15,6 +15,7 @@ fn add::{closure#0}::{closure#0}(_1: &mut {async block@$DIR/async_fn.rs:34:13: 3 bb0: { _8 = discriminant((*_1)); + _0 = const (); switchInt(move _8) -> [0: bb2, otherwise: bb3]; } diff --git a/tests/mir-opt/coroutine/async_fn.add-{closure#0}.coroutine_drop.0.mir b/tests/mir-opt/coroutine/async_fn.add-{closure#0}.coroutine_drop.0.mir index cd2092e97177a..7b7a2469360d2 100644 --- a/tests/mir-opt/coroutine/async_fn.add-{closure#0}.coroutine_drop.0.mir +++ b/tests/mir-opt/coroutine/async_fn.add-{closure#0}.coroutine_drop.0.mir @@ -48,6 +48,7 @@ fn add::{closure#0}(_1: &mut {async fn body of add()}) -> () { bb0: { _26 = discriminant((*_1)); + _0 = const (); switchInt(move _26) -> [0: bb11, 3: bb12, otherwise: bb13]; } diff --git a/tests/mir-opt/coroutine/async_fn.build_aggregate-{closure#0}.coroutine_drop.0.mir b/tests/mir-opt/coroutine/async_fn.build_aggregate-{closure#0}.coroutine_drop.0.mir index d107b3a2dbbbd..5ca617278ce67 100644 --- a/tests/mir-opt/coroutine/async_fn.build_aggregate-{closure#0}.coroutine_drop.0.mir +++ b/tests/mir-opt/coroutine/async_fn.build_aggregate-{closure#0}.coroutine_drop.0.mir @@ -86,6 +86,7 @@ fn build_aggregate::{closure#0}(_1: &mut {async fn body of build_aggregate()}) - bb0: { _50 = discriminant((*_1)); + _0 = const (); switchInt(move _50) -> [0: bb16, 3: bb17, 4: bb18, otherwise: bb19]; } diff --git a/tests/mir-opt/coroutine/async_fn.foo-{closure#0}-{closure#0}.coroutine_drop.0.mir b/tests/mir-opt/coroutine/async_fn.foo-{closure#0}-{closure#0}.coroutine_drop.0.mir index a098d65cbc084..156649c264646 100644 --- a/tests/mir-opt/coroutine/async_fn.foo-{closure#0}-{closure#0}.coroutine_drop.0.mir +++ b/tests/mir-opt/coroutine/async_fn.foo-{closure#0}-{closure#0}.coroutine_drop.0.mir @@ -15,6 +15,7 @@ fn foo::{closure#0}::{closure#0}(_1: &mut {async block@$DIR/async_fn.rs:21:13: 2 bb0: { _8 = discriminant((*_1)); + _0 = const (); switchInt(move _8) -> [0: bb2, otherwise: bb3]; } diff --git a/tests/mir-opt/coroutine/async_fn.foo-{closure#0}.coroutine_drop.0.mir b/tests/mir-opt/coroutine/async_fn.foo-{closure#0}.coroutine_drop.0.mir index d5d9182fb2071..ae051b6b144a0 100644 --- a/tests/mir-opt/coroutine/async_fn.foo-{closure#0}.coroutine_drop.0.mir +++ b/tests/mir-opt/coroutine/async_fn.foo-{closure#0}.coroutine_drop.0.mir @@ -72,6 +72,7 @@ fn foo::{closure#0}(_1: &mut {async fn body of foo()}) -> () { bb0: { _35 = discriminant((*_1)); + _0 = const (); switchInt(move _35) -> [0: bb9, 3: bb10, otherwise: bb11]; } diff --git a/tests/mir-opt/coroutine/async_fn.hello_world-{closure#0}.coroutine_drop.0.mir b/tests/mir-opt/coroutine/async_fn.hello_world-{closure#0}.coroutine_drop.0.mir index b32943d88c10f..8cceffbf2f5bb 100644 --- a/tests/mir-opt/coroutine/async_fn.hello_world-{closure#0}.coroutine_drop.0.mir +++ b/tests/mir-opt/coroutine/async_fn.hello_world-{closure#0}.coroutine_drop.0.mir @@ -54,6 +54,7 @@ fn hello_world::{closure#0}(_1: &mut {async fn body of hello_world()}) -> () { bb0: { _34 = discriminant((*_1)); + _0 = const (); switchInt(move _34) -> [0: bb9, 3: bb10, otherwise: bb11]; } diff --git a/tests/mir-opt/coroutine/async_fn.includes_never-{closure#0}-{closure#0}.coroutine_drop.0.mir b/tests/mir-opt/coroutine/async_fn.includes_never-{closure#0}-{closure#0}.coroutine_drop.0.mir index 686b4323e0185..a3c8802306e2e 100644 --- a/tests/mir-opt/coroutine/async_fn.includes_never-{closure#0}-{closure#0}.coroutine_drop.0.mir +++ b/tests/mir-opt/coroutine/async_fn.includes_never-{closure#0}-{closure#0}.coroutine_drop.0.mir @@ -14,6 +14,7 @@ fn includes_never::{closure#0}::{closure#0}(_1: &mut {async block@$DIR/async_fn. bb0: { _8 = discriminant((*_1)); + _0 = const (); switchInt(move _8) -> [0: bb2, otherwise: bb3]; } diff --git a/tests/mir-opt/coroutine/async_fn.includes_never-{closure#0}-{closure#1}.coroutine_drop.0.mir b/tests/mir-opt/coroutine/async_fn.includes_never-{closure#0}-{closure#1}.coroutine_drop.0.mir index 3ae5c1dc89643..260d18f89feab 100644 --- a/tests/mir-opt/coroutine/async_fn.includes_never-{closure#0}-{closure#1}.coroutine_drop.0.mir +++ b/tests/mir-opt/coroutine/async_fn.includes_never-{closure#0}-{closure#1}.coroutine_drop.0.mir @@ -14,6 +14,7 @@ fn includes_never::{closure#0}::{closure#1}(_1: &mut {async block@$DIR/async_fn. bb0: { _8 = discriminant((*_1)); + _0 = const (); switchInt(move _8) -> [0: bb2, otherwise: bb3]; } diff --git a/tests/mir-opt/coroutine/async_fn.includes_never-{closure#0}.coroutine_drop.0.mir b/tests/mir-opt/coroutine/async_fn.includes_never-{closure#0}.coroutine_drop.0.mir index 97fdec425898f..de0a2475f0458 100644 --- a/tests/mir-opt/coroutine/async_fn.includes_never-{closure#0}.coroutine_drop.0.mir +++ b/tests/mir-opt/coroutine/async_fn.includes_never-{closure#0}.coroutine_drop.0.mir @@ -82,6 +82,7 @@ fn includes_never::{closure#0}(_1: &mut {async fn body of includes_never()}) -> bb0: { _49 = discriminant((*_1)); + _0 = const (); switchInt(move _49) -> [0: bb9, 3: bb10, otherwise: bb11]; } diff --git a/tests/mir-opt/coroutine/async_fn.partial_init-{closure#0}-{closure#0}.coroutine_drop.0.mir b/tests/mir-opt/coroutine/async_fn.partial_init-{closure#0}-{closure#0}.coroutine_drop.0.mir index 7a21ec09f8a10..ae14335b22348 100644 --- a/tests/mir-opt/coroutine/async_fn.partial_init-{closure#0}-{closure#0}.coroutine_drop.0.mir +++ b/tests/mir-opt/coroutine/async_fn.partial_init-{closure#0}-{closure#0}.coroutine_drop.0.mir @@ -14,6 +14,7 @@ fn partial_init::{closure#0}::{closure#0}(_1: &mut {async block@$DIR/async_fn.rs bb0: { _8 = discriminant((*_1)); + _0 = const (); switchInt(move _8) -> [0: bb2, otherwise: bb3]; } diff --git a/tests/mir-opt/coroutine/async_fn.partial_init-{closure#0}.coroutine_drop.0.mir b/tests/mir-opt/coroutine/async_fn.partial_init-{closure#0}.coroutine_drop.0.mir index c76b3ca617f76..b808b5f37e6f1 100644 --- a/tests/mir-opt/coroutine/async_fn.partial_init-{closure#0}.coroutine_drop.0.mir +++ b/tests/mir-opt/coroutine/async_fn.partial_init-{closure#0}.coroutine_drop.0.mir @@ -47,6 +47,7 @@ fn partial_init::{closure#0}(_1: &mut {async fn body of partial_init()}) -> () { bb0: { _27 = discriminant((*_1)); + _0 = const (); switchInt(move _27) -> [0: bb11, 3: bb12, otherwise: bb13]; } diff --git a/tests/mir-opt/coroutine/async_fn.uninhabited_variant-{closure#0}.coroutine_drop.0.mir b/tests/mir-opt/coroutine/async_fn.uninhabited_variant-{closure#0}.coroutine_drop.0.mir index 19cde28ce35fb..e3a25192eb17f 100644 --- a/tests/mir-opt/coroutine/async_fn.uninhabited_variant-{closure#0}.coroutine_drop.0.mir +++ b/tests/mir-opt/coroutine/async_fn.uninhabited_variant-{closure#0}.coroutine_drop.0.mir @@ -68,6 +68,7 @@ fn uninhabited_variant::{closure#0}(_1: &mut {async fn body of uninhabited_varia bb0: { _45 = discriminant((*_1)); + _0 = const (); switchInt(move _45) -> [0: bb22, 3: bb23, 4: bb24, otherwise: bb25]; } diff --git a/tests/mir-opt/coroutine/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir b/tests/mir-opt/coroutine/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir index e8873e341cc77..ec34b8750cc0e 100644 --- a/tests/mir-opt/coroutine/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir +++ b/tests/mir-opt/coroutine/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir @@ -14,6 +14,7 @@ fn main::{closure#0}(_1: &mut {coroutine@$DIR/coroutine_drop_cleanup.rs:12:5: 12 bb0: { _7 = discriminant((*_1)); + _0 = const (); switchInt(move _7) -> [0: bb5, 3: bb6, otherwise: bb7]; } diff --git a/tests/mir-opt/coroutine/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir b/tests/mir-opt/coroutine/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir index 9e450d702cc38..7d2f1c309b416 100644 --- a/tests/mir-opt/coroutine/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir +++ b/tests/mir-opt/coroutine/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir @@ -14,6 +14,7 @@ fn main::{closure#0}(_1: &mut {coroutine@$DIR/coroutine_drop_cleanup.rs:12:5: 12 bb0: { _7 = discriminant((*_1)); + _0 = const (); switchInt(move _7) -> [0: bb7, 3: bb8, otherwise: bb9]; } diff --git a/tests/mir-opt/inline/inline_empty_drop_glue.slice_in_place.Inline.diff b/tests/mir-opt/inline/inline_empty_drop_glue.slice_in_place.Inline.diff index 2b5cbe6e44820..9f8aad1e2350a 100644 --- a/tests/mir-opt/inline/inline_empty_drop_glue.slice_in_place.Inline.diff +++ b/tests/mir-opt/inline/inline_empty_drop_glue.slice_in_place.Inline.diff @@ -20,6 +20,7 @@ - bb1: { + StorageLive(_3); + _3 = &mut (*_2); ++ _0 = const (); + StorageDead(_3); StorageDead(_2); return; diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff index 437745be8bc90..c97f283a53984 100644 --- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff @@ -89,6 +89,7 @@ } bb1: { ++ _3 = const (); + StorageDead(_8); + StorageDead(_7); + StorageDead(_6); @@ -126,6 +127,7 @@ + } + + bb5: { ++ _0 = const (); + StorageDead(_18); + StorageDead(_17); StorageDead(_5); diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-unwind.diff index 2160cd19652e3..d4b1f0623c4fb 100644 --- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-unwind.diff @@ -44,6 +44,7 @@ } bb2: { ++ _0 = const (); + StorageDead(_8); + StorageDead(_7); StorageDead(_5); diff --git a/tests/mir-opt/instsimplify/intrinsic_asserts.removable.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/intrinsic_asserts.removable.InstSimplify-after-simplifycfg.diff index 20e046d8e194b..ef9385b1c8457 100644 --- a/tests/mir-opt/instsimplify/intrinsic_asserts.removable.InstSimplify-after-simplifycfg.diff +++ b/tests/mir-opt/instsimplify/intrinsic_asserts.removable.InstSimplify-after-simplifycfg.diff @@ -10,6 +10,7 @@ bb0: { StorageLive(_1); - _1 = assert_inhabited::<()>() -> [return: bb1, unwind unreachable]; ++ _1 = const (); + goto -> bb1; } @@ -17,6 +18,7 @@ StorageDead(_1); StorageLive(_2); - _2 = assert_zero_valid::() -> [return: bb2, unwind unreachable]; ++ _2 = const (); + goto -> bb2; } @@ -24,6 +26,7 @@ StorageDead(_2); StorageLive(_3); - _3 = assert_mem_uninitialized_valid::() -> [return: bb3, unwind unreachable]; ++ _3 = const (); + goto -> bb3; } diff --git a/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.panic-abort.diff index 6c1f457cb5f50..2c83ba9629c01 100644 --- a/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.panic-abort.diff +++ b/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.panic-abort.diff @@ -9,6 +9,7 @@ StorageLive(_1); - _1 = std::intrinsics::assume(const true) -> [return: bb1, unwind unreachable]; + assume(const true); ++ _1 = const (); + goto -> bb1; } diff --git a/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.panic-unwind.diff index 6c1f457cb5f50..2c83ba9629c01 100644 --- a/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.panic-unwind.diff +++ b/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.panic-unwind.diff @@ -9,6 +9,7 @@ StorageLive(_1); - _1 = std::intrinsics::assume(const true) -> [return: bb1, unwind unreachable]; + assume(const true); ++ _1 = const (); + goto -> bb1; } diff --git a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-abort.diff index d73cc3d214e4c..5e36aaeed3e5f 100644 --- a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-abort.diff +++ b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-abort.diff @@ -47,6 +47,7 @@ StorageDead(_9); - _3 = std::intrinsics::copy_nonoverlapping::(move _4, move _8, const 0_usize) -> [return: bb1, unwind unreachable]; + copy_nonoverlapping(dst = move _8, src = move _4, count = const 0_usize); ++ _3 = const (); + goto -> bb1; } diff --git a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-unwind.diff index d73cc3d214e4c..5e36aaeed3e5f 100644 --- a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-unwind.diff +++ b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.panic-unwind.diff @@ -47,6 +47,7 @@ StorageDead(_9); - _3 = std::intrinsics::copy_nonoverlapping::(move _4, move _8, const 0_usize) -> [return: bb1, unwind unreachable]; + copy_nonoverlapping(dst = move _8, src = move _4, count = const 0_usize); ++ _3 = const (); + goto -> bb1; } diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs index e8a7701477415..b50b95fd1e527 100644 --- a/tests/mir-opt/lower_intrinsics.rs +++ b/tests/mir-opt/lower_intrinsics.rs @@ -147,6 +147,7 @@ pub fn discriminant(t: T) { pub fn f_copy_nonoverlapping() { // CHECK-LABEL: fn f_copy_nonoverlapping( // CHECK: copy_nonoverlapping({{.*}}); + // CHECK-NEXT: {{_[0-9]+}} = const (); let src = (); let mut dst = (); @@ -159,6 +160,7 @@ pub fn f_copy_nonoverlapping() { pub fn assume() { // CHECK-LABEL: fn assume( // CHECK: assume({{.*}}); + // CHECK-NEXT: {{_[0-9]+}} = const (); unsafe { std::intrinsics::assume(true); diff --git a/tests/mir-opt/range/ssa_range.on_assume.SsaRangePropagation.diff b/tests/mir-opt/range/ssa_range.on_assume.SsaRangePropagation.diff index 3be6f083604d3..7552c27cfb800 100644 --- a/tests/mir-opt/range/ssa_range.on_assume.SsaRangePropagation.diff +++ b/tests/mir-opt/range/ssa_range.on_assume.SsaRangePropagation.diff @@ -34,6 +34,7 @@ nop; StorageDead(_6); assume(copy _5); + _4 = const (); nop; StorageDead(_4); _3 = const (); diff --git a/tests/mir-opt/slice_drop_shim.core.ptr-drop_glue.[String;42].AddMovesForPackedDrops.before.mir b/tests/mir-opt/slice_drop_shim.core.ptr-drop_glue.[String;42].AddMovesForPackedDrops.before.mir index 1d4b0f033e0be..b7daabb24ebd0 100644 --- a/tests/mir-opt/slice_drop_shim.core.ptr-drop_glue.[String;42].AddMovesForPackedDrops.before.mir +++ b/tests/mir-opt/slice_drop_shim.core.ptr-drop_glue.[String;42].AddMovesForPackedDrops.before.mir @@ -10,6 +10,7 @@ fn std::ptr::drop_glue(_1: &mut [String; 42]) -> () { } bb1: { + _0 = const (); return; } } diff --git a/tests/mir-opt/slice_drop_shim.core.ptr-drop_glue.[String].AddMovesForPackedDrops.before.mir b/tests/mir-opt/slice_drop_shim.core.ptr-drop_glue.[String].AddMovesForPackedDrops.before.mir index 227f02d4c0e25..0b29f234e1b42 100644 --- a/tests/mir-opt/slice_drop_shim.core.ptr-drop_glue.[String].AddMovesForPackedDrops.before.mir +++ b/tests/mir-opt/slice_drop_shim.core.ptr-drop_glue.[String].AddMovesForPackedDrops.before.mir @@ -14,6 +14,7 @@ fn std::ptr::drop_glue(_1: &mut [String]) -> () { } bb1: { + _0 = const (); return; } diff --git a/tests/mir-opt/unusual_item_types.core.ptr-drop_glue.Vec_i32_.AddMovesForPackedDrops.before.mir b/tests/mir-opt/unusual_item_types.core.ptr-drop_glue.Vec_i32_.AddMovesForPackedDrops.before.mir index 4f124c666bfb0..e4dda75280d14 100644 --- a/tests/mir-opt/unusual_item_types.core.ptr-drop_glue.Vec_i32_.AddMovesForPackedDrops.before.mir +++ b/tests/mir-opt/unusual_item_types.core.ptr-drop_glue.Vec_i32_.AddMovesForPackedDrops.before.mir @@ -10,6 +10,7 @@ fn std::ptr::drop_glue(_1: &mut Vec) -> () { } bb1: { + _0 = const (); return; } From a1136b1b3aa410b0c1bf1e4beef7eb6bd6c69bdc Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Fri, 25 Sep 2026 22:23:01 +0100 Subject: [PATCH 19/21] SimplifyMatch: evaluate all constants instead of just scalars --- .../rustc_mir_transform/src/match_branches.rs | 5 +-- .../match_branch_simplification_zst.rs | 14 +++++++++ ...t.same_unit.MatchBranchSimplification.diff | 31 +++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 tests/mir-opt/match_branch_simplification_zst.rs create mode 100644 tests/mir-opt/match_branch_simplification_zst.same_unit.MatchBranchSimplification.diff diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 48a0caa4979bb..c06f6a89a434a 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -65,9 +65,10 @@ impl<'tcx, 'a> SimplifyMatch<'tcx, 'a> { otherwise: Option<&ConstOperand<'tcx>>, ) -> Option> { let (_, first_const, mut others) = split_first_case(consts, otherwise); - let first_scalar_int = first_const.const_.try_eval_scalar_int(self.tcx, self.typing_env)?; + let first_value = + first_const.const_.eval(self.tcx, self.typing_env, first_const.span).ok()?; if others.all(|const_| { - const_.const_.try_eval_scalar_int(self.tcx, self.typing_env) == Some(first_scalar_int) + const_.const_.eval(self.tcx, self.typing_env, const_.span).ok() == Some(first_value) }) { Some(StatementKind::Assign(Box::new(( dest, diff --git a/tests/mir-opt/match_branch_simplification_zst.rs b/tests/mir-opt/match_branch_simplification_zst.rs new file mode 100644 index 0000000000000..36dae88ce7fef --- /dev/null +++ b/tests/mir-opt/match_branch_simplification_zst.rs @@ -0,0 +1,14 @@ +//@ test-mir-pass: MatchBranchSimplification + +#![crate_type = "lib"] + +// Both arms assign the same unit constant with different source spans. +// EMIT_MIR match_branch_simplification_zst.same_unit.MatchBranchSimplification.diff +pub fn same_unit(condition: bool) { + // CHECK-LABEL: fn same_unit( + // CHECK-NOT: switchInt + // CHECK: _0 = no_retag const (); + // CHECK-NOT: switchInt + // CHECK: return; + if condition {} +} diff --git a/tests/mir-opt/match_branch_simplification_zst.same_unit.MatchBranchSimplification.diff b/tests/mir-opt/match_branch_simplification_zst.same_unit.MatchBranchSimplification.diff new file mode 100644 index 0000000000000..ff35a2a25887f --- /dev/null +++ b/tests/mir-opt/match_branch_simplification_zst.same_unit.MatchBranchSimplification.diff @@ -0,0 +1,31 @@ +- // MIR for `same_unit` before MatchBranchSimplification ++ // MIR for `same_unit` after MatchBranchSimplification + + fn same_unit(_1: bool) -> () { + debug condition => _1; + let mut _0: (); + let mut _2: bool; + + bb0: { + StorageLive(_2); + _2 = copy _1; +- switchInt(move _2) -> [0: bb2, otherwise: bb1]; +- } +- +- bb1: { +- _0 = const (); +- goto -> bb3; +- } +- +- bb2: { +- _0 = const (); +- goto -> bb3; +- } +- +- bb3: { ++ _0 = no_retag const (); + StorageDead(_2); + return; + } + } + From dfe80b8c1dfda7ae59ed502a1e50033f085a1a57 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Fri, 25 Sep 2026 22:29:40 +0100 Subject: [PATCH 20/21] Preserve direct ZST writes in RemoveZsts --- .../rustc_mir_transform/src/remove_zsts.rs | 9 +- tests/incremental/hashes/loop_expressions.rs | 2 +- tests/incremental/hashes/trait_impls.rs | 2 +- tests/incremental/hashes/while_let_loops.rs | 18 ++-- tests/incremental/hashes/while_loops.rs | 2 +- ..._simplification.hello.GVN.panic-abort.diff | 1 + ...simplification.hello.GVN.panic-unwind.diff | 1 + ...lo.runtime-optimized.after.panic-abort.mir | 1 + ...o.runtime-optimized.after.panic-unwind.mir | 1 + .../const_prop/control_flow_simplification.rs | 1 + .../const_prop/invalid_constant.main.GVN.diff | 7 +- .../invalid_constant.main.RemoveZsts.diff | 9 +- tests/mir-opt/const_prop/union.main.GVN.diff | 1 + .../asm_unwind.main.Inline.panic-unwind.diff | 2 + ...ntial_runtime.main.Inline.panic-abort.diff | 60 ++++++----- ...tial_runtime.main.Inline.panic-unwind.diff | 60 ++++++----- ...forced.caller.ForceInline.panic-abort.diff | 1 + ...orced.caller.ForceInline.panic-unwind.diff | 1 + ...r-{closure#0}.ForceInline.panic-abort.diff | 1 + ...-{closure#0}.ForceInline.panic-unwind.diff | 1 + ...r-{closure#0}.ForceInline.panic-abort.diff | 1 + ...-{closure#0}.ForceInline.panic-unwind.diff | 1 + ...d_code.caller.ForceInline.panic-abort.diff | 1 + ..._code.caller.ForceInline.panic-unwind.diff | 1 + ...herent.caller.ForceInline.panic-abort.diff | 1 + ...erent.caller.ForceInline.panic-unwind.diff | 1 + ...iguous.caller.ForceInline.panic-abort.diff | 1 + ...guous.caller.ForceInline.panic-unwind.diff | 1 + ...d_code.caller.ForceInline.panic-abort.diff | 1 + ..._code.caller.ForceInline.panic-unwind.diff | 1 + tests/mir-opt/inline/inline_compatibility.rs | 2 + .../inline_cycle.two.Inline.panic-abort.diff | 2 + .../inline_cycle.two.Inline.panic-unwind.diff | 2 + .../mir-opt/inline/inline_empty_drop_glue.rs | 1 + ...inline_instruction_set.default.Inline.diff | 2 + .../inline_instruction_set.t32.Inline.diff | 2 + ...line_indirect.Inline.after.panic-abort.mir | 1 + ...ine_indirect.Inline.after.panic-unwind.mir | 1 + ...ic_not_inline.Inline.after.panic-abort.mir | 11 ++- ...c_not_inline.Inline.after.panic-unwind.mir | 11 ++- ..._options.main.Inline.after.panic-abort.mir | 19 ++-- ...options.main.Inline.after.panic-unwind.mir | 19 ++-- .../inline_shims.drop.Inline.panic-abort.diff | 99 +++---------------- ...ine_scopes_parenting.main.Inline.after.mir | 1 + .../issue_78442.bar.Inline.panic-abort.diff | 2 + .../issue_78442.bar.Inline.panic-unwind.diff | 2 + ...er.runtime-optimized.after.panic-abort.mir | 1 + ...r.runtime-optimized.after.panic-unwind.mir | 1 + ...implifyComparisonIntegral.panic-abort.diff | 1 + ...mplifyComparisonIntegral.panic-unwind.diff | 1 + ...nators.test.MultipleReturnTerminators.diff | 1 + ...ern_types.main.runtime-optimized.after.mir | 1 + ...ub.runtime-optimized.after.panic-abort.mir | 2 + ...b.runtime-optimized.after.panic-unwind.mir | 2 + ...es.runtime-optimized.after.panic-abort.mir | 1 + ...s.runtime-optimized.after.panic-unwind.mir | 1 + ...ic.runtime-optimized.after.panic-abort.mir | 1 + ...c.runtime-optimized.after.panic-unwind.mir | 1 + ...time-optimized.after.32bit.panic-abort.mir | 1 + ...ime-optimized.after.32bit.panic-unwind.mir | 1 + ...time-optimized.after.64bit.panic-abort.mir | 1 + ...ime-optimized.after.64bit.panic-unwind.mir | 1 + ...trinsics.f_u64.runtime-optimized.after.mir | 1 + ...rinsics.f_unit.runtime-optimized.after.mir | 1 + ...d_constant.main.GVN.32bit.panic-abort.diff | 1 + ..._constant.main.GVN.32bit.panic-unwind.diff | 1 + ...d_constant.main.GVN.64bit.panic-abort.diff | 1 + ..._constant.main.GVN.64bit.panic-unwind.diff | 1 + ....filter_mapped.runtime-optimized.after.mir | 1 + ...oops.int_range.runtime-optimized.after.mir | 1 + .../loops.mapped.runtime-optimized.after.mir | 1 + ...loops.vec_move.runtime-optimized.after.mir | 1 + ...o_variable.main.GVN.32bit.panic-abort.diff | 1 + ..._variable.main.GVN.32bit.panic-unwind.diff | 1 + ...o_variable.main.GVN.64bit.panic-abort.diff | 1 + ..._variable.main.GVN.64bit.panic-unwind.diff | 1 + ...acementOfAggregates.32bit.panic-abort.diff | 1 + ...cementOfAggregates.32bit.panic-unwind.diff | 1 + ...acementOfAggregates.64bit.panic-abort.diff | 1 + ...cementOfAggregates.64bit.panic-unwind.diff | 1 + ...fyLocals-final.after.32bit.panic-abort.mir | 1 + ...yLocals-final.after.32bit.panic-unwind.mir | 1 + ...fyLocals-final.after.64bit.panic-abort.mir | 1 + ...yLocals-final.after.64bit.panic-unwind.mir | 1 + ...time-optimized.after.32bit.panic-abort.mir | 1 + ...ime-optimized.after.32bit.panic-unwind.mir | 1 + ...time-optimized.after.64bit.panic-abort.mir | 1 + ...ime-optimized.after.64bit.panic-unwind.mir | 1 + ...op.runtime-optimized.after.panic-abort.mir | 1 + ...p.runtime-optimized.after.panic-unwind.mir | 1 + ...op.runtime-optimized.after.panic-abort.mir | 1 + ...p.runtime-optimized.after.panic-unwind.mir | 1 + ..._question_mark.runtime-optimized.after.mir | 46 ++++----- ...p.runtime-optimized.after.panic-unwind.mir | 1 + ...op.runtime-optimized.after.panic-abort.mir | 1 + ...p.runtime-optimized.after.panic-unwind.mir | 1 + ...op.runtime-optimized.after.panic-abort.mir | 1 + ...p.runtime-optimized.after.panic-unwind.mir | 1 + ...op.runtime-optimized.after.panic-abort.mir | 1 + ...p.runtime-optimized.after.panic-unwind.mir | 1 + ...forget_at_home.runtime-optimized.after.mir | 1 + tests/mir-opt/pre-codegen/transmutes.rs | 1 + ...d_copy_generic.runtime-optimized.after.mir | 1 + ...ed_copy_manual.runtime-optimized.after.mir | 1 + .../remove_zsts.get_union.RemoveZsts.diff | 5 +- ..._zsts.remove_generic_array.RemoveZsts.diff | 26 +++-- tests/mir-opt/remove_zsts.rs | 6 +- ...e_elimination.discriminant.RemoveZsts.diff | 12 +++ ..._move_elimination.indirect.RemoveZsts.diff | 16 +++ ...sts_move_elimination.local.RemoveZsts.diff | 19 ++++ tests/mir-opt/remove_zsts_move_elimination.rs | 68 +++++++++++++ ...ition-after-inst-simplify.panic-abort.diff | 14 ++- ...tion-after-inst-simplify.panic-unwind.diff | 14 ++- tests/mir-opt/simplify_if.rs | 1 + ....foo.SimplifyLocals-final.panic-abort.diff | 16 ++- ...foo.SimplifyLocals-final.panic-unwind.diff | 16 ++- .../simplify_match.main.GVN.panic-abort.diff | 11 ++- .../simplify_match.main.GVN.panic-unwind.diff | 11 ++- tests/mir-opt/simplify_match.rs | 1 + ...roa.bar.ScalarReplacementOfAggregates.diff | 1 + ...roa.foo.ScalarReplacementOfAggregates.diff | 1 + ...roa.foo.ScalarReplacementOfAggregates.diff | 1 + ...rocess_void.SimplifyLocals-final.after.mir | 1 + ...t_read.main.SimplifyLocals-final.after.mir | 1 + tests/run-make/const_fn_mir/dump.mir | 1 + .../const-expr-generic-err.stderr | 8 ++ 126 files changed, 475 insertions(+), 248 deletions(-) create mode 100644 tests/mir-opt/remove_zsts_move_elimination.discriminant.RemoveZsts.diff create mode 100644 tests/mir-opt/remove_zsts_move_elimination.indirect.RemoveZsts.diff create mode 100644 tests/mir-opt/remove_zsts_move_elimination.local.RemoveZsts.diff create mode 100644 tests/mir-opt/remove_zsts_move_elimination.rs diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 09379fc252072..11d5ad6221d34 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -126,7 +126,10 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { Some(place) } StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { - Some(local.into()) + if self.known_to_be_zst(self.local_decls[local].ty) { + statement.make_nop(true); + } + return; } StatementKind::Coverage(_) | StatementKind::Intrinsic(_) @@ -134,9 +137,13 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::ConstEvalCounter => None, }; + // Under move-elimination semantics, direct ZST writes still serve to + // allocate the base local and give it an address. Genuinely unused + // assignments are later deleted by DSE. if let Some(place_for_ty) = place_for_ty && let ty = place_for_ty.ty(self.local_decls, self.tcx).ty && self.known_to_be_zst(ty) + && place_for_ty.is_indirect() { statement.make_nop(true); } else { diff --git a/tests/incremental/hashes/loop_expressions.rs b/tests/incremental/hashes/loop_expressions.rs index cb21fca7579be..c85748d89f356 100644 --- a/tests/incremental/hashes/loop_expressions.rs +++ b/tests/incremental/hashes/loop_expressions.rs @@ -106,7 +106,7 @@ pub fn add_loop_label_to_break() { #[cfg(not(any(bpass1,bpass4)))] #[rustc_clean(cfg="bpass2", except="hir_owner")] #[rustc_clean(cfg="bpass3")] -#[rustc_clean(cfg="bpass5", except="hir_owner")] +#[rustc_clean(cfg="bpass5", except="hir_owner,optimized_mir")] #[rustc_clean(cfg="bpass6")] pub fn add_loop_label_to_break() { let mut _x = 0; diff --git a/tests/incremental/hashes/trait_impls.rs b/tests/incremental/hashes/trait_impls.rs index 9698ec0473c4b..3bb1e12ed6677 100644 --- a/tests/incremental/hashes/trait_impls.rs +++ b/tests/incremental/hashes/trait_impls.rs @@ -81,7 +81,7 @@ impl ChangeMethodBodyTrait for Foo { impl ChangeMethodBodyTrait for Foo { #[rustc_clean(except="hir_owner,typeck_root", cfg="bpass2")] #[rustc_clean(cfg="bpass3")] - #[rustc_clean(except="hir_owner,typeck_root", cfg="bpass5")] + #[rustc_clean(except="hir_owner,typeck_root,optimized_mir", cfg="bpass5")] #[rustc_clean(cfg="bpass6")] fn method_name() { () diff --git a/tests/incremental/hashes/while_let_loops.rs b/tests/incremental/hashes/while_let_loops.rs index 780775b51930f..18cc32296fa92 100644 --- a/tests/incremental/hashes/while_let_loops.rs +++ b/tests/incremental/hashes/while_let_loops.rs @@ -31,7 +31,7 @@ pub fn change_loop_body() { #[cfg(not(any(bpass1,bpass4)))] #[rustc_clean(cfg="bpass2", except="hir_owner")] #[rustc_clean(cfg="bpass3")] -#[rustc_clean(cfg="bpass5", except="hir_owner")] +#[rustc_clean(cfg="bpass5", except="hir_owner,optimized_mir")] #[rustc_clean(cfg="bpass6")] pub fn change_loop_body() { let mut _x = 0; @@ -56,7 +56,7 @@ pub fn change_loop_condition() { #[cfg(not(any(bpass1,bpass4)))] #[rustc_clean(cfg="bpass2", except="hir_owner")] #[rustc_clean(cfg="bpass3")] -#[rustc_clean(cfg="bpass5", except="hir_owner")] +#[rustc_clean(cfg="bpass5", except="hir_owner,optimized_mir")] #[rustc_clean(cfg="bpass6")] pub fn change_loop_condition() { let mut _x = 0; @@ -81,7 +81,7 @@ pub fn add_break() { #[cfg(not(any(bpass1,bpass4)))] #[rustc_clean(cfg="bpass2", except="hir_owner, typeck_root")] #[rustc_clean(cfg="bpass3")] -#[rustc_clean(cfg="bpass5", except="hir_owner, typeck_root")] +#[rustc_clean(cfg="bpass5", except="hir_owner, typeck_root, optimized_mir")] #[rustc_clean(cfg="bpass6")] pub fn add_break() { let mut _x = 0; @@ -106,7 +106,7 @@ pub fn add_loop_label() { #[cfg(not(any(bpass1,bpass4)))] #[rustc_clean(cfg="bpass2", except="hir_owner")] #[rustc_clean(cfg="bpass3")] -#[rustc_clean(cfg="bpass5", except="hir_owner")] +#[rustc_clean(cfg="bpass5", except="hir_owner,optimized_mir")] #[rustc_clean(cfg="bpass6")] pub fn add_loop_label() { let mut _x = 0; @@ -131,7 +131,7 @@ pub fn add_loop_label_to_break() { #[cfg(not(any(bpass1,bpass4)))] #[rustc_clean(cfg="bpass2", except="hir_owner")] #[rustc_clean(cfg="bpass3")] -#[rustc_clean(cfg="bpass5", except="hir_owner")] +#[rustc_clean(cfg="bpass5", except="hir_owner,optimized_mir")] #[rustc_clean(cfg="bpass6")] pub fn add_loop_label_to_break() { let mut _x = 0; @@ -158,7 +158,7 @@ pub fn change_break_label() { #[cfg(not(any(bpass1,bpass4)))] #[rustc_clean(cfg="bpass2", except="hir_owner")] #[rustc_clean(cfg="bpass3")] -#[rustc_clean(cfg="bpass5", except="hir_owner")] +#[rustc_clean(cfg="bpass5", except="hir_owner,optimized_mir")] #[rustc_clean(cfg="bpass6")] pub fn change_break_label() { let mut _x = 0; @@ -183,7 +183,7 @@ pub fn add_loop_label_to_continue() { #[cfg(not(any(bpass1,bpass4)))] #[rustc_clean(cfg="bpass2", except="hir_owner")] #[rustc_clean(cfg="bpass3")] -#[rustc_clean(cfg="bpass5", except="hir_owner")] +#[rustc_clean(cfg="bpass5", except="hir_owner,optimized_mir")] #[rustc_clean(cfg="bpass6")] pub fn add_loop_label_to_continue() { let mut _x = 0; @@ -210,7 +210,7 @@ pub fn change_continue_label() { #[cfg(not(any(bpass1,bpass4)))] #[rustc_clean(cfg="bpass2", except="hir_owner")] #[rustc_clean(cfg="bpass3")] -#[rustc_clean(cfg="bpass5", except="hir_owner")] +#[rustc_clean(cfg="bpass5", except="hir_owner,optimized_mir")] #[rustc_clean(cfg="bpass6")] pub fn change_continue_label() { let mut _x = 0; @@ -237,7 +237,7 @@ pub fn change_continue_to_break() { #[cfg(not(any(bpass1,bpass4)))] #[rustc_clean(cfg="bpass2", except="hir_owner")] #[rustc_clean(cfg="bpass3")] -#[rustc_clean(cfg="bpass5", except="hir_owner")] +#[rustc_clean(cfg="bpass5", except="hir_owner,optimized_mir")] #[rustc_clean(cfg="bpass6")] pub fn change_continue_to_break() { let mut _x = 0; diff --git a/tests/incremental/hashes/while_loops.rs b/tests/incremental/hashes/while_loops.rs index 4b5af798c0efa..903e1017bd2a4 100644 --- a/tests/incremental/hashes/while_loops.rs +++ b/tests/incremental/hashes/while_loops.rs @@ -131,7 +131,7 @@ pub fn add_loop_label_to_break() { #[cfg(not(any(bpass1,bpass4)))] #[rustc_clean(cfg="bpass2", except="hir_owner")] #[rustc_clean(cfg="bpass3")] -#[rustc_clean(cfg="bpass5", except="hir_owner")] +#[rustc_clean(cfg="bpass5", except="hir_owner,optimized_mir")] #[rustc_clean(cfg="bpass6")] pub fn add_loop_label_to_break() { let mut _x = 0; diff --git a/tests/mir-opt/const_prop/control_flow_simplification.hello.GVN.panic-abort.diff b/tests/mir-opt/const_prop/control_flow_simplification.hello.GVN.panic-abort.diff index 2ecf41638125e..0c257693d4bc3 100644 --- a/tests/mir-opt/const_prop/control_flow_simplification.hello.GVN.panic-abort.diff +++ b/tests/mir-opt/const_prop/control_flow_simplification.hello.GVN.panic-abort.diff @@ -14,6 +14,7 @@ } bb2: { + _0 = const (); return; } } diff --git a/tests/mir-opt/const_prop/control_flow_simplification.hello.GVN.panic-unwind.diff b/tests/mir-opt/const_prop/control_flow_simplification.hello.GVN.panic-unwind.diff index 06287b670dd4f..4be6f1d3ed90a 100644 --- a/tests/mir-opt/const_prop/control_flow_simplification.hello.GVN.panic-unwind.diff +++ b/tests/mir-opt/const_prop/control_flow_simplification.hello.GVN.panic-unwind.diff @@ -14,6 +14,7 @@ } bb2: { + _0 = const (); return; } } diff --git a/tests/mir-opt/const_prop/control_flow_simplification.hello.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/const_prop/control_flow_simplification.hello.runtime-optimized.after.panic-abort.mir index 86eda4942a7cb..b27b099456e47 100644 --- a/tests/mir-opt/const_prop/control_flow_simplification.hello.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/const_prop/control_flow_simplification.hello.runtime-optimized.after.panic-abort.mir @@ -4,6 +4,7 @@ fn hello() -> () { let mut _0: (); bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/const_prop/control_flow_simplification.hello.runtime-optimized.after.panic-unwind.mir b/tests/mir-opt/const_prop/control_flow_simplification.hello.runtime-optimized.after.panic-unwind.mir index 86eda4942a7cb..b27b099456e47 100644 --- a/tests/mir-opt/const_prop/control_flow_simplification.hello.runtime-optimized.after.panic-unwind.mir +++ b/tests/mir-opt/const_prop/control_flow_simplification.hello.runtime-optimized.after.panic-unwind.mir @@ -4,6 +4,7 @@ fn hello() -> () { let mut _0: (); bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/const_prop/control_flow_simplification.rs b/tests/mir-opt/const_prop/control_flow_simplification.rs index 640579a3ebeca..9865c626aaed5 100644 --- a/tests/mir-opt/const_prop/control_flow_simplification.rs +++ b/tests/mir-opt/const_prop/control_flow_simplification.rs @@ -13,6 +13,7 @@ impl NeedsDrop for This {} fn hello() { // CHECK-LABEL: fn hello( // CHECK: bb0: + // CHECK-NEXT: _0 = const (); // CHECK-NEXT: return; if ::NEEDS { panic!() diff --git a/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff b/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff index 3dc6fc9e3f65b..71e63d852ca0c 100644 --- a/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff +++ b/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff @@ -49,12 +49,13 @@ StorageLive(_8); - _8 = NoVariants { int: const 0_u32 }; + _8 = const NoVariants {{ int: 0_u32, empty: ZeroSized: Empty }}; - nop; - nop; + _7 = const ZeroSized: Empty; +- _6 = [const ZeroSized: Empty]; ++ _6 = const [ZeroSized: Empty]; nop; StorageDead(_8); nop; - nop; + _0 = const (); nop; nop; StorageDead(_3); diff --git a/tests/mir-opt/const_prop/invalid_constant.main.RemoveZsts.diff b/tests/mir-opt/const_prop/invalid_constant.main.RemoveZsts.diff index c16cbaf4c0f27..3f0d1de0da83d 100644 --- a/tests/mir-opt/const_prop/invalid_constant.main.RemoveZsts.diff +++ b/tests/mir-opt/const_prop/invalid_constant.main.RemoveZsts.diff @@ -50,17 +50,16 @@ - _7 = copy (_8.1: Empty); - _6 = [move _7]; - StorageDead(_7); -+ nop; -+ nop; ++ _7 = const ZeroSized: Empty; ++ _6 = [const ZeroSized: Empty]; + nop; StorageDead(_8); - StorageLive(_9); -- _0 = const (); ++ nop; + _0 = const (); - StorageDead(_9); - StorageDead(_6); + nop; -+ nop; -+ nop; + nop; StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/const_prop/union.main.GVN.diff b/tests/mir-opt/const_prop/union.main.GVN.diff index 4c3e0bb68fd43..06eb1710921b4 100644 --- a/tests/mir-opt/const_prop/union.main.GVN.diff +++ b/tests/mir-opt/const_prop/union.main.GVN.diff @@ -26,6 +26,7 @@ StorageLive(_4); - _4 = copy (_1.0: u32); + _4 = const 1_u32; + _3 = const (); StorageDead(_4); StorageDead(_3); _0 = const (); diff --git a/tests/mir-opt/inline/asm_unwind.main.Inline.panic-unwind.diff b/tests/mir-opt/inline/asm_unwind.main.Inline.panic-unwind.diff index dc0004105a7b7..60d26b422000d 100644 --- a/tests/mir-opt/inline/asm_unwind.main.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/asm_unwind.main.Inline.panic-unwind.diff @@ -15,6 +15,7 @@ StorageLive(_1); - _1 = foo() -> [return: bb1, unwind continue]; + StorageLive(_2); ++ _2 = const D; + asm!("", options(MAY_UNWIND)) -> [return: bb2, unwind: bb3]; } @@ -26,6 +27,7 @@ + } + + bb2: { ++ _1 = const (); + drop(_2) -> [return: bb1, unwind continue]; + } + diff --git a/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-abort.diff b/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-abort.diff index 3ffe2ee0c0b94..82c1945bbc71c 100644 --- a/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-abort.diff @@ -56,10 +56,15 @@ + StorageLive(_17); + StorageLive(_18); + StorageLive(_19); -+ _17 = <() as A>::call() -> [return: bb12, unwind unreachable]; ++ _17 = <() as A>::call() -> [return: bb11, unwind unreachable]; } bb1: { ++ _4 = <() as F>::call() -> [return: bb2, unwind unreachable]; ++ } ++ ++ bb2: { ++ _1 = const (); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); @@ -68,67 +73,68 @@ return; + } + -+ bb2: { -+ _4 = <() as F>::call() -> [return: bb1, unwind unreachable]; ++ bb3: { ++ _7 = <() as E>::call() -> [return: bb4, unwind unreachable]; + } + -+ bb3: { ++ bb4: { ++ _2 = const (); + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); -+ _3 = <() as F>::call() -> [return: bb2, unwind unreachable]; ++ _3 = <() as F>::call() -> [return: bb1, unwind unreachable]; + } + -+ bb4: { -+ _7 = <() as E>::call() -> [return: bb3, unwind unreachable]; ++ bb5: { ++ _10 = <() as D>::call() -> [return: bb6, unwind unreachable]; + } + -+ bb5: { ++ bb6: { ++ _5 = const (); + StorageDead(_10); + StorageDead(_9); + StorageDead(_8); -+ _6 = <() as E>::call() -> [return: bb4, unwind unreachable]; ++ _6 = <() as E>::call() -> [return: bb3, unwind unreachable]; + } + -+ bb6: { -+ _10 = <() as D>::call() -> [return: bb5, unwind unreachable]; ++ bb7: { ++ _13 = <() as C>::call() -> [return: bb8, unwind unreachable]; + } + -+ bb7: { ++ bb8: { ++ _8 = const (); + StorageDead(_13); + StorageDead(_12); + StorageDead(_11); -+ _9 = <() as D>::call() -> [return: bb6, unwind unreachable]; ++ _9 = <() as D>::call() -> [return: bb5, unwind unreachable]; + } + -+ bb8: { -+ _13 = <() as C>::call() -> [return: bb7, unwind unreachable]; ++ bb9: { ++ _16 = <() as B>::call() -> [return: bb10, unwind unreachable]; + } + -+ bb9: { ++ bb10: { ++ _11 = const (); + StorageDead(_16); + StorageDead(_15); + StorageDead(_14); -+ _12 = <() as C>::call() -> [return: bb8, unwind unreachable]; -+ } -+ -+ bb10: { -+ _16 = <() as B>::call() -> [return: bb9, unwind unreachable]; ++ _12 = <() as C>::call() -> [return: bb7, unwind unreachable]; + } + + bb11: { -+ StorageDead(_19); -+ StorageDead(_18); -+ StorageDead(_17); -+ _15 = <() as B>::call() -> [return: bb10, unwind unreachable]; ++ _18 = <() as A>::call() -> [return: bb12, unwind unreachable]; + } + + bb12: { -+ _18 = <() as A>::call() -> [return: bb13, unwind unreachable]; ++ _19 = <() as A>::call() -> [return: bb13, unwind unreachable]; + } + + bb13: { -+ _19 = <() as A>::call() -> [return: bb11, unwind unreachable]; ++ _14 = const (); ++ StorageDead(_19); ++ StorageDead(_18); ++ StorageDead(_17); ++ _15 = <() as B>::call() -> [return: bb9, unwind unreachable]; } } diff --git a/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-unwind.diff b/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-unwind.diff index 3f334779e18f8..d3a8d4df681cb 100644 --- a/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/exponential_runtime.main.Inline.panic-unwind.diff @@ -56,10 +56,15 @@ + StorageLive(_17); + StorageLive(_18); + StorageLive(_19); -+ _17 = <() as A>::call() -> [return: bb12, unwind continue]; ++ _17 = <() as A>::call() -> [return: bb11, unwind continue]; } bb1: { ++ _4 = <() as F>::call() -> [return: bb2, unwind continue]; ++ } ++ ++ bb2: { ++ _1 = const (); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); @@ -68,67 +73,68 @@ return; + } + -+ bb2: { -+ _4 = <() as F>::call() -> [return: bb1, unwind continue]; ++ bb3: { ++ _7 = <() as E>::call() -> [return: bb4, unwind continue]; + } + -+ bb3: { ++ bb4: { ++ _2 = const (); + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); -+ _3 = <() as F>::call() -> [return: bb2, unwind continue]; ++ _3 = <() as F>::call() -> [return: bb1, unwind continue]; + } + -+ bb4: { -+ _7 = <() as E>::call() -> [return: bb3, unwind continue]; ++ bb5: { ++ _10 = <() as D>::call() -> [return: bb6, unwind continue]; + } + -+ bb5: { ++ bb6: { ++ _5 = const (); + StorageDead(_10); + StorageDead(_9); + StorageDead(_8); -+ _6 = <() as E>::call() -> [return: bb4, unwind continue]; ++ _6 = <() as E>::call() -> [return: bb3, unwind continue]; + } + -+ bb6: { -+ _10 = <() as D>::call() -> [return: bb5, unwind continue]; ++ bb7: { ++ _13 = <() as C>::call() -> [return: bb8, unwind continue]; + } + -+ bb7: { ++ bb8: { ++ _8 = const (); + StorageDead(_13); + StorageDead(_12); + StorageDead(_11); -+ _9 = <() as D>::call() -> [return: bb6, unwind continue]; ++ _9 = <() as D>::call() -> [return: bb5, unwind continue]; + } + -+ bb8: { -+ _13 = <() as C>::call() -> [return: bb7, unwind continue]; ++ bb9: { ++ _16 = <() as B>::call() -> [return: bb10, unwind continue]; + } + -+ bb9: { ++ bb10: { ++ _11 = const (); + StorageDead(_16); + StorageDead(_15); + StorageDead(_14); -+ _12 = <() as C>::call() -> [return: bb8, unwind continue]; -+ } -+ -+ bb10: { -+ _16 = <() as B>::call() -> [return: bb9, unwind continue]; ++ _12 = <() as C>::call() -> [return: bb7, unwind continue]; + } + + bb11: { -+ StorageDead(_19); -+ StorageDead(_18); -+ StorageDead(_17); -+ _15 = <() as B>::call() -> [return: bb10, unwind continue]; ++ _18 = <() as A>::call() -> [return: bb12, unwind continue]; + } + + bb12: { -+ _18 = <() as A>::call() -> [return: bb13, unwind continue]; ++ _19 = <() as A>::call() -> [return: bb13, unwind continue]; + } + + bb13: { -+ _19 = <() as A>::call() -> [return: bb11, unwind continue]; ++ _14 = const (); ++ StorageDead(_19); ++ StorageDead(_18); ++ StorageDead(_17); ++ _15 = <() as B>::call() -> [return: bb9, unwind continue]; } } diff --git a/tests/mir-opt/inline/forced.caller.ForceInline.panic-abort.diff b/tests/mir-opt/inline/forced.caller.ForceInline.panic-abort.diff index f894f06e5a0ab..3e7acb259e35c 100644 --- a/tests/mir-opt/inline/forced.caller.ForceInline.panic-abort.diff +++ b/tests/mir-opt/inline/forced.caller.ForceInline.panic-abort.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _1 = const (); StorageDead(_1); _0 = const (); return; diff --git a/tests/mir-opt/inline/forced.caller.ForceInline.panic-unwind.diff b/tests/mir-opt/inline/forced.caller.ForceInline.panic-unwind.diff index 7b70fd665666a..4fa91544345a8 100644 --- a/tests/mir-opt/inline/forced.caller.ForceInline.panic-unwind.diff +++ b/tests/mir-opt/inline/forced.caller.ForceInline.panic-unwind.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _1 = const (); StorageDead(_1); _0 = const (); return; diff --git a/tests/mir-opt/inline/forced_closure.caller-{closure#0}.ForceInline.panic-abort.diff b/tests/mir-opt/inline/forced_closure.caller-{closure#0}.ForceInline.panic-abort.diff index def2375efd068..97e06736d72f0 100644 --- a/tests/mir-opt/inline/forced_closure.caller-{closure#0}.ForceInline.panic-abort.diff +++ b/tests/mir-opt/inline/forced_closure.caller-{closure#0}.ForceInline.panic-abort.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _2 = const (); StorageDead(_2); _0 = const (); return; diff --git a/tests/mir-opt/inline/forced_closure.caller-{closure#0}.ForceInline.panic-unwind.diff b/tests/mir-opt/inline/forced_closure.caller-{closure#0}.ForceInline.panic-unwind.diff index 8f50c20587d70..ccc33422f7b64 100644 --- a/tests/mir-opt/inline/forced_closure.caller-{closure#0}.ForceInline.panic-unwind.diff +++ b/tests/mir-opt/inline/forced_closure.caller-{closure#0}.ForceInline.panic-unwind.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _2 = const (); StorageDead(_2); _0 = const (); return; diff --git a/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-abort.diff b/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-abort.diff index 8e03432c2af2d..43c43a6d8a977 100644 --- a/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-abort.diff +++ b/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-abort.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _2 = const (); StorageDead(_2); _0 = const (); return; diff --git a/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-unwind.diff b/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-unwind.diff index 0e41fd89dacf7..b9cc22c72ffe3 100644 --- a/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-unwind.diff +++ b/tests/mir-opt/inline/forced_closure_inherent.caller-{closure#0}.ForceInline.panic-unwind.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _2 = const (); StorageDead(_2); _0 = const (); return; diff --git a/tests/mir-opt/inline/forced_dead_code.caller.ForceInline.panic-abort.diff b/tests/mir-opt/inline/forced_dead_code.caller.ForceInline.panic-abort.diff index f894f06e5a0ab..3e7acb259e35c 100644 --- a/tests/mir-opt/inline/forced_dead_code.caller.ForceInline.panic-abort.diff +++ b/tests/mir-opt/inline/forced_dead_code.caller.ForceInline.panic-abort.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _1 = const (); StorageDead(_1); _0 = const (); return; diff --git a/tests/mir-opt/inline/forced_dead_code.caller.ForceInline.panic-unwind.diff b/tests/mir-opt/inline/forced_dead_code.caller.ForceInline.panic-unwind.diff index 7b70fd665666a..4fa91544345a8 100644 --- a/tests/mir-opt/inline/forced_dead_code.caller.ForceInline.panic-unwind.diff +++ b/tests/mir-opt/inline/forced_dead_code.caller.ForceInline.panic-unwind.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _1 = const (); StorageDead(_1); _0 = const (); return; diff --git a/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-abort.diff b/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-abort.diff index 6ea1894af9895..06c16e9528b42 100644 --- a/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-abort.diff +++ b/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-abort.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _1 = const (); StorageDead(_1); _0 = const (); return; diff --git a/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-unwind.diff b/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-unwind.diff index dd91c3387723f..93a7a4b8f7656 100644 --- a/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-unwind.diff +++ b/tests/mir-opt/inline/forced_inherent.caller.ForceInline.panic-unwind.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _1 = const (); StorageDead(_1); _0 = const (); return; diff --git a/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-abort.diff b/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-abort.diff index 6ea1894af9895..06c16e9528b42 100644 --- a/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-abort.diff +++ b/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-abort.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _1 = const (); StorageDead(_1); _0 = const (); return; diff --git a/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-unwind.diff b/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-unwind.diff index dd91c3387723f..93a7a4b8f7656 100644 --- a/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-unwind.diff +++ b/tests/mir-opt/inline/forced_inherent_ambiguous.caller.ForceInline.panic-unwind.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _1 = const (); StorageDead(_1); _0 = const (); return; diff --git a/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-abort.diff b/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-abort.diff index edaf2820d85c3..1f6e8c6331d7c 100644 --- a/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-abort.diff +++ b/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-abort.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _1 = const (); StorageDead(_1); _0 = const (); return; diff --git a/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-unwind.diff b/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-unwind.diff index 22f8b14a724b8..b7f2f0dec6694 100644 --- a/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-unwind.diff +++ b/tests/mir-opt/inline/forced_inherent_dead_code.caller.ForceInline.panic-unwind.diff @@ -13,6 +13,7 @@ - } - - bb1: { ++ _1 = const (); StorageDead(_1); _0 = const (); return; diff --git a/tests/mir-opt/inline/inline_compatibility.rs b/tests/mir-opt/inline/inline_compatibility.rs index 3f72584017abd..43875669be3c7 100644 --- a/tests/mir-opt/inline/inline_compatibility.rs +++ b/tests/mir-opt/inline/inline_compatibility.rs @@ -15,6 +15,7 @@ fn nop() {} // CHECK-LABEL: fn f0() // CHECK: bb0: { +// CHECK-NEXT: _0 = const (); // CHECK-NEXT: return; #[target_feature(enable = "sse2")] pub unsafe fn f0() { @@ -42,6 +43,7 @@ pub unsafe fn sanitize_off() {} // CHECK-LABEL: fn inlined_sanitize_off() // CHECK: bb0: { +// CHECK-NEXT: _0 = const (); // CHECK-NEXT: return; #[sanitize(address = "off")] pub unsafe fn inlined_sanitize_off() { diff --git a/tests/mir-opt/inline/inline_cycle.two.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_cycle.two.Inline.panic-abort.diff index c878b8b632414..341ecf729775e 100644 --- a/tests/mir-opt/inline/inline_cycle.two.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_cycle.two.Inline.panic-abort.diff @@ -29,8 +29,10 @@ } bb1: { ++ _3 = const (); + StorageDead(_5); + StorageDead(_4); ++ _1 = const (); + StorageDead(_3); + StorageDead(_2); StorageDead(_1); diff --git a/tests/mir-opt/inline/inline_cycle.two.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_cycle.two.Inline.panic-unwind.diff index 072b7ab3fa491..494e2b3fa01e6 100644 --- a/tests/mir-opt/inline/inline_cycle.two.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/inline_cycle.two.Inline.panic-unwind.diff @@ -29,8 +29,10 @@ } bb1: { ++ _3 = const (); + StorageDead(_5); + StorageDead(_4); ++ _1 = const (); + StorageDead(_3); + StorageDead(_2); StorageDead(_1); diff --git a/tests/mir-opt/inline/inline_empty_drop_glue.rs b/tests/mir-opt/inline/inline_empty_drop_glue.rs index ad7dbb06da1eb..1f710f7f2716d 100644 --- a/tests/mir-opt/inline/inline_empty_drop_glue.rs +++ b/tests/mir-opt/inline/inline_empty_drop_glue.rs @@ -6,6 +6,7 @@ unsafe fn slice_in_place(ptr: *mut [char]) { // CHECK-LABEL: fn slice_in_place(_1: *mut [char]) // CHECK: bb0: { + // CHECK-NEXT: _0 = const (); // CHECK-NEXT: return; // CHECK-NEXT: } std::ptr::drop_in_place(ptr) diff --git a/tests/mir-opt/inline/inline_instruction_set.default.Inline.diff b/tests/mir-opt/inline/inline_instruction_set.default.Inline.diff index 158cc973779c8..b48fb31e0e199 100644 --- a/tests/mir-opt/inline/inline_instruction_set.default.Inline.diff +++ b/tests/mir-opt/inline/inline_instruction_set.default.Inline.diff @@ -30,6 +30,7 @@ - } - - bb3: { ++ _3 = const (); StorageDead(_3); StorageLive(_4); - _4 = inline_always_and_using_inline_asm() -> [return: bb4, unwind unreachable]; @@ -38,6 +39,7 @@ - bb4: { + bb3: { ++ _4 = const (); StorageDead(_4); _0 = const (); return; diff --git a/tests/mir-opt/inline/inline_instruction_set.t32.Inline.diff b/tests/mir-opt/inline/inline_instruction_set.t32.Inline.diff index 33dc116f622e1..ae528f383929b 100644 --- a/tests/mir-opt/inline/inline_instruction_set.t32.Inline.diff +++ b/tests/mir-opt/inline/inline_instruction_set.t32.Inline.diff @@ -24,12 +24,14 @@ - } - - bb2: { ++ _2 = const (); StorageDead(_2); StorageLive(_3); - _3 = instruction_set_default() -> [return: bb3, unwind unreachable]; - } - - bb3: { ++ _3 = const (); StorageDead(_3); StorageLive(_4); - _4 = inline_always_and_using_inline_asm() -> [return: bb4, unwind unreachable]; diff --git a/tests/mir-opt/inline/inline_more_in_non_inline.marked_inline_indirect.Inline.after.panic-abort.mir b/tests/mir-opt/inline/inline_more_in_non_inline.marked_inline_indirect.Inline.after.panic-abort.mir index 374ed294d8b26..38d32b0f8dde3 100644 --- a/tests/mir-opt/inline/inline_more_in_non_inline.marked_inline_indirect.Inline.after.panic-abort.mir +++ b/tests/mir-opt/inline/inline_more_in_non_inline.marked_inline_indirect.Inline.after.panic-abort.mir @@ -18,6 +18,7 @@ fn marked_inline_indirect(_1: i32) -> () { } bb1: { + _2 = const (); StorageDead(_4); StorageDead(_3); StorageDead(_2); diff --git a/tests/mir-opt/inline/inline_more_in_non_inline.marked_inline_indirect.Inline.after.panic-unwind.mir b/tests/mir-opt/inline/inline_more_in_non_inline.marked_inline_indirect.Inline.after.panic-unwind.mir index 97bed8020bad7..3004e498c7fcc 100644 --- a/tests/mir-opt/inline/inline_more_in_non_inline.marked_inline_indirect.Inline.after.panic-unwind.mir +++ b/tests/mir-opt/inline/inline_more_in_non_inline.marked_inline_indirect.Inline.after.panic-unwind.mir @@ -18,6 +18,7 @@ fn marked_inline_indirect(_1: i32) -> () { } bb1: { + _2 = const (); StorageDead(_4); StorageDead(_3); StorageDead(_2); diff --git a/tests/mir-opt/inline/inline_more_in_non_inline.monomorphic_not_inline.Inline.after.panic-abort.mir b/tests/mir-opt/inline/inline_more_in_non_inline.monomorphic_not_inline.Inline.after.panic-abort.mir index cffe0e96f5c94..0cecc66a32218 100644 --- a/tests/mir-opt/inline/inline_more_in_non_inline.monomorphic_not_inline.Inline.after.panic-abort.mir +++ b/tests/mir-opt/inline/inline_more_in_non_inline.monomorphic_not_inline.Inline.after.panic-abort.mir @@ -16,10 +16,15 @@ fn monomorphic_not_inline(_1: i32) -> () { _3 = copy _1; StorageLive(_4); StorageLive(_5); - _4 = other_thing(copy _3) -> [return: bb2, unwind unreachable]; + _4 = other_thing(copy _3) -> [return: bb1, unwind unreachable]; } bb1: { + _5 = other_thing(move _3) -> [return: bb2, unwind unreachable]; + } + + bb2: { + _2 = const (); StorageDead(_5); StorageDead(_4); StorageDead(_3); @@ -27,8 +32,4 @@ fn monomorphic_not_inline(_1: i32) -> () { _0 = const (); return; } - - bb2: { - _5 = other_thing(move _3) -> [return: bb1, unwind unreachable]; - } } diff --git a/tests/mir-opt/inline/inline_more_in_non_inline.monomorphic_not_inline.Inline.after.panic-unwind.mir b/tests/mir-opt/inline/inline_more_in_non_inline.monomorphic_not_inline.Inline.after.panic-unwind.mir index 7f3a093012bcd..898bbdfc13d04 100644 --- a/tests/mir-opt/inline/inline_more_in_non_inline.monomorphic_not_inline.Inline.after.panic-unwind.mir +++ b/tests/mir-opt/inline/inline_more_in_non_inline.monomorphic_not_inline.Inline.after.panic-unwind.mir @@ -16,10 +16,15 @@ fn monomorphic_not_inline(_1: i32) -> () { _3 = copy _1; StorageLive(_4); StorageLive(_5); - _4 = other_thing(copy _3) -> [return: bb2, unwind continue]; + _4 = other_thing(copy _3) -> [return: bb1, unwind continue]; } bb1: { + _5 = other_thing(move _3) -> [return: bb2, unwind continue]; + } + + bb2: { + _2 = const (); StorageDead(_5); StorageDead(_4); StorageDead(_3); @@ -27,8 +32,4 @@ fn monomorphic_not_inline(_1: i32) -> () { _0 = const (); return; } - - bb2: { - _5 = other_thing(move _3) -> [return: bb1, unwind continue]; - } } diff --git a/tests/mir-opt/inline/inline_options.main.Inline.after.panic-abort.mir b/tests/mir-opt/inline/inline_options.main.Inline.after.panic-abort.mir index 3218c4e567378..8080157239fda 100644 --- a/tests/mir-opt/inline/inline_options.main.Inline.after.panic-abort.mir +++ b/tests/mir-opt/inline/inline_options.main.Inline.after.panic-abort.mir @@ -21,23 +21,24 @@ fn main() -> () { StorageLive(_3); StorageLive(_4); StorageLive(_5); - _3 = g() -> [return: bb3, unwind unreachable]; + _3 = g() -> [return: bb2, unwind unreachable]; } bb2: { - StorageDead(_5); - StorageDead(_4); - StorageDead(_3); - StorageDead(_2); - _0 = const (); - return; + _4 = g() -> [return: bb3, unwind unreachable]; } bb3: { - _4 = g() -> [return: bb4, unwind unreachable]; + _5 = g() -> [return: bb4, unwind unreachable]; } bb4: { - _5 = g() -> [return: bb2, unwind unreachable]; + _2 = const (); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + _0 = const (); + return; } } diff --git a/tests/mir-opt/inline/inline_options.main.Inline.after.panic-unwind.mir b/tests/mir-opt/inline/inline_options.main.Inline.after.panic-unwind.mir index df0cab513eca7..1cec7a885005e 100644 --- a/tests/mir-opt/inline/inline_options.main.Inline.after.panic-unwind.mir +++ b/tests/mir-opt/inline/inline_options.main.Inline.after.panic-unwind.mir @@ -21,23 +21,24 @@ fn main() -> () { StorageLive(_3); StorageLive(_4); StorageLive(_5); - _3 = g() -> [return: bb3, unwind continue]; + _3 = g() -> [return: bb2, unwind continue]; } bb2: { - StorageDead(_5); - StorageDead(_4); - StorageDead(_3); - StorageDead(_2); - _0 = const (); - return; + _4 = g() -> [return: bb3, unwind continue]; } bb3: { - _4 = g() -> [return: bb4, unwind continue]; + _5 = g() -> [return: bb4, unwind continue]; } bb4: { - _5 = g() -> [return: bb2, unwind continue]; + _2 = const (); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + _0 = const (); + return; } } diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff index c97f283a53984..4d138fcbede7d 100644 --- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff @@ -13,49 +13,13 @@ + scope 2 (inlined std::ptr::drop_glue::> - shim(Some(Vec))) { + let mut _7: &mut std::vec::Vec; + let mut _8: (); -+ scope 3 (inlined as Drop>::drop) { -+ let mut _9: *mut [A]; -+ let mut _10: *mut A; -+ let mut _11: usize; -+ scope 4 (inlined Vec::::as_mut_ptr) { -+ scope 5 (inlined alloc::raw_vec::RawVec::::ptr) { -+ scope 6 (inlined alloc::raw_vec::RawVecInner::ptr::) { -+ scope 7 (inlined alloc::raw_vec::RawVecInner::non_null::) { -+ let mut _12: std::ptr::NonNull; -+ scope 8 (inlined NonNull::::cast::) { -+ scope 9 (inlined NonNull::::as_ptr) { -+ } -+ } -+ } -+ scope 10 (inlined NonNull::::as_ptr) { -+ } -+ } -+ } -+ } -+ scope 11 (inlined std::ptr::mut_ptr::::cast_slice) { -+ scope 12 (inlined slice_from_raw_parts_mut::) { -+ scope 13 (inlined std::ptr::from_raw_parts_mut::<[A], A>) { -+ } -+ } -+ } -+ scope 14 (inlined std::ptr::mut_ptr::::drop_in_place) { -+ scope 15 (inlined drop_in_place::<[A]>) { -+ let mut _13: &mut [A]; -+ scope 16 (inlined std::ptr::drop_glue::<[A]> - shim(Some([A]))) { -+ let mut _14: usize; -+ let mut _15: *mut A; -+ let mut _16: bool; -+ } -+ } -+ } -+ } + } + } -+ scope 17 (inlined drop_in_place::>) { -+ let mut _17: &mut std::option::Option; -+ scope 18 (inlined std::ptr::drop_glue::> - shim(Some(Option))) { -+ let mut _18: isize; -+ let mut _19: isize; ++ scope 3 (inlined drop_in_place::>) { ++ let mut _9: &mut std::option::Option; ++ scope 4 (inlined std::ptr::drop_glue::> - shim(Some(Option))) { ++ let mut _10: isize; ++ let mut _11: isize; + } + } @@ -69,23 +33,7 @@ + StorageLive(_7); + StorageLive(_8); + _7 = copy _6; -+ StorageLive(_11); -+ StorageLive(_9); -+ StorageLive(_10); -+ StorageLive(_12); -+ _12 = copy ((((*_7).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::NonNull); -+ _10 = copy _12 as *mut A (Transmute); -+ StorageDead(_12); -+ _11 = copy ((*_7).1: usize); -+ _9 = *mut [A] from (copy _10, copy _11); -+ StorageDead(_10); -+ StorageLive(_13); -+ _13 = &mut (*_9); -+ StorageLive(_14); -+ StorageLive(_15); -+ StorageLive(_16); -+ _14 = const 0_usize; -+ goto -> bb4; ++ _8 = as Drop>::drop(move _7) -> [return: bb2, unwind unreachable]; } bb1: { @@ -98,44 +46,27 @@ StorageLive(_5); _5 = copy _2; - _0 = drop_in_place::>(move _5) -> [return: bb2, unwind unreachable]; -+ StorageLive(_17); -+ _17 = &mut (*_5); -+ StorageLive(_18); -+ _18 = discriminant((*_17)); -+ switchInt(move _18) -> [0: bb5, otherwise: bb6]; ++ StorageLive(_9); ++ _9 = &mut (*_5); ++ StorageLive(_10); ++ _10 = discriminant((*_9)); ++ switchInt(move _10) -> [0: bb3, otherwise: bb4]; } bb2: { -+ StorageDead(_16); -+ StorageDead(_15); -+ StorageDead(_14); -+ StorageDead(_13); -+ StorageDead(_9); -+ StorageDead(_11); + drop(((*_6).0: alloc::raw_vec::RawVec)) -> [return: bb1, unwind unreachable]; + } + + bb3: { -+ _15 = &raw mut (*_13)[_14]; -+ _14 = Add(move _14, const 1_usize); -+ drop((*_15)) -> [return: bb4, unwind unreachable]; -+ } -+ -+ bb4: { -+ _16 = Eq(copy _11, copy _14); -+ switchInt(move _16) -> [0: bb3, otherwise: bb2]; -+ } -+ -+ bb5: { + _0 = const (); -+ StorageDead(_18); -+ StorageDead(_17); ++ StorageDead(_10); ++ StorageDead(_9); StorageDead(_5); return; + } + -+ bb6: { -+ drop((((*_17) as Some).0: B)) -> [return: bb5, unwind unreachable]; ++ bb4: { ++ drop((((*_9) as Some).0: B)) -> [return: bb3, unwind unreachable]; } } diff --git a/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.main.Inline.after.mir b/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.main.Inline.after.mir index 63d3ae2a86987..5b792379254de 100644 --- a/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.main.Inline.after.mir +++ b/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.main.Inline.after.mir @@ -28,6 +28,7 @@ fn main() -> () { _3 = (move _4,); StorageLive(_5); _5 = move (_3.0: ()); + _0 = const (); StorageDead(_5); StorageDead(_4); StorageDead(_3); diff --git a/tests/mir-opt/inline/issue_78442.bar.Inline.panic-abort.diff b/tests/mir-opt/inline/issue_78442.bar.Inline.panic-abort.diff index 14a0c639c3abd..0f16de7ed5f92 100644 --- a/tests/mir-opt/inline/issue_78442.bar.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/issue_78442.bar.Inline.panic-abort.diff @@ -23,6 +23,7 @@ - } - - bb1: { ++ _4 = foo; _3 = &_4; StorageLive(_5); _5 = (); @@ -30,6 +31,7 @@ - } - - bb2: { ++ _2 = const (); StorageDead(_5); StorageDead(_3); StorageDead(_4); diff --git a/tests/mir-opt/inline/issue_78442.bar.Inline.panic-unwind.diff b/tests/mir-opt/inline/issue_78442.bar.Inline.panic-unwind.diff index e0a273eb13f50..d79765ed6a6fa 100644 --- a/tests/mir-opt/inline/issue_78442.bar.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/issue_78442.bar.Inline.panic-unwind.diff @@ -23,6 +23,7 @@ - } - - bb1: { ++ _4 = foo; _3 = &_4; StorageLive(_5); _5 = (); @@ -30,6 +31,7 @@ - } - - bb2: { ++ _2 = const (); StorageDead(_5); StorageDead(_3); StorageDead(_4); diff --git a/tests/mir-opt/inline/rustc_no_mir_inline.caller.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/inline/rustc_no_mir_inline.caller.runtime-optimized.after.panic-abort.mir index f6a6e86bff26a..2b49d78d8c9d4 100644 --- a/tests/mir-opt/inline/rustc_no_mir_inline.caller.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/inline/rustc_no_mir_inline.caller.runtime-optimized.after.panic-abort.mir @@ -9,6 +9,7 @@ fn caller() -> () { } bb1: { + _0 = const (); return; } } diff --git a/tests/mir-opt/inline/rustc_no_mir_inline.caller.runtime-optimized.after.panic-unwind.mir b/tests/mir-opt/inline/rustc_no_mir_inline.caller.runtime-optimized.after.panic-unwind.mir index c8703ce515989..c991c6a3737ea 100644 --- a/tests/mir-opt/inline/rustc_no_mir_inline.caller.runtime-optimized.after.panic-unwind.mir +++ b/tests/mir-opt/inline/rustc_no_mir_inline.caller.runtime-optimized.after.panic-unwind.mir @@ -9,6 +9,7 @@ fn caller() -> () { } bb1: { + _0 = const (); return; } } diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff index 40d1b0e28bf1c..33d3fdb084a2b 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff @@ -38,6 +38,7 @@ // DBG: _5 = &(*_2)[0 of 3]; // DBG: _6 = &(*_2)[1 of 3]; // DBG: _7 = &(*_2)[2 of 3]; + _0 = const (); StorageDead(_4); return; } diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff index a72d0b6c51b3b..78c07efa15615 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff @@ -38,6 +38,7 @@ // DBG: _5 = &(*_2)[0 of 3]; // DBG: _6 = &(*_2)[1 of 3]; // DBG: _7 = &(*_2)[2 of 3]; + _0 = const (); StorageDead(_4); return; } diff --git a/tests/mir-opt/multiple_return_terminators.test.MultipleReturnTerminators.diff b/tests/mir-opt/multiple_return_terminators.test.MultipleReturnTerminators.diff index e28809f38afa6..b9c2de4aaf5f8 100644 --- a/tests/mir-opt/multiple_return_terminators.test.MultipleReturnTerminators.diff +++ b/tests/mir-opt/multiple_return_terminators.test.MultipleReturnTerminators.diff @@ -6,6 +6,7 @@ let mut _0: (); bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/pattern_types.main.runtime-optimized.after.mir b/tests/mir-opt/pattern_types.main.runtime-optimized.after.mir index 541472fc22957..9bc455d98ebbf 100644 --- a/tests/mir-opt/pattern_types.main.runtime-optimized.after.mir +++ b/tests/mir-opt/pattern_types.main.runtime-optimized.after.mir @@ -10,6 +10,7 @@ fn main() -> () { } bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/checked_ops.use_checked_sub.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.use_checked_sub.runtime-optimized.after.panic-abort.mir index f24a8e41b7db5..8e21c607b6fbd 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.use_checked_sub.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.use_checked_sub.runtime-optimized.after.panic-abort.mir @@ -34,12 +34,14 @@ fn use_checked_sub(_1: u32, _2: u32) -> () { } bb2: { + _0 = const (); StorageDead(_6); goto -> bb4; } bb3: { StorageDead(_3); + _0 = const (); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/checked_ops.use_checked_sub.runtime-optimized.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.use_checked_sub.runtime-optimized.after.panic-unwind.mir index 6bcb730be7f14..fef388f87a7ce 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.use_checked_sub.runtime-optimized.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.use_checked_sub.runtime-optimized.after.panic-unwind.mir @@ -34,12 +34,14 @@ fn use_checked_sub(_1: u32, _2: u32) -> () { } bb2: { + _0 = const (); StorageDead(_6); goto -> bb4; } bb3: { StorageDead(_3); + _0 = const (); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_bytes.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_bytes.runtime-optimized.after.panic-abort.mir index 054477656373e..19aae0107b701 100644 --- a/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_bytes.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_bytes.runtime-optimized.after.panic-abort.mir @@ -78,6 +78,7 @@ fn drop_bytes(_1: *mut Box<[u8; 1024]>) -> () { bb1: { StorageDead(_2); StorageDead(_3); + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_bytes.runtime-optimized.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_bytes.runtime-optimized.after.panic-unwind.mir index 054477656373e..19aae0107b701 100644 --- a/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_bytes.runtime-optimized.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_bytes.runtime-optimized.after.panic-unwind.mir @@ -78,6 +78,7 @@ fn drop_bytes(_1: *mut Box<[u8; 1024]>) -> () { bb1: { StorageDead(_2); StorageDead(_3); + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_generic.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_generic.runtime-optimized.after.panic-abort.mir index cc83646ff6c4e..1ecc8d42fae53 100644 --- a/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_generic.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_generic.runtime-optimized.after.panic-abort.mir @@ -88,6 +88,7 @@ fn drop_generic(_1: *mut Box) -> () { bb3: { StorageDead(_2); StorageDead(_4); + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_generic.runtime-optimized.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_generic.runtime-optimized.after.panic-unwind.mir index cc83646ff6c4e..1ecc8d42fae53 100644 --- a/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_generic.runtime-optimized.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_box_of_sized.drop_generic.runtime-optimized.after.panic-unwind.mir @@ -88,6 +88,7 @@ fn drop_generic(_1: *mut Box) -> () { bb3: { StorageDead(_2); StorageDead(_4); + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.32bit.panic-abort.mir index 243673e0e9f65..20eb569735778 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.32bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.32bit.panic-abort.mir @@ -103,6 +103,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageDead(_8); StorageDead(_5); StorageDead(_3); + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.32bit.panic-unwind.mir index 243673e0e9f65..20eb569735778 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.32bit.panic-unwind.mir @@ -103,6 +103,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageDead(_8); StorageDead(_5); StorageDead(_3); + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.64bit.panic-abort.mir index 243673e0e9f65..20eb569735778 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.64bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.64bit.panic-abort.mir @@ -103,6 +103,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageDead(_8); StorageDead(_5); StorageDead(_3); + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.64bit.panic-unwind.mir index 243673e0e9f65..20eb569735778 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.runtime-optimized.after.64bit.panic-unwind.mir @@ -103,6 +103,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageDead(_8); StorageDead(_5); StorageDead(_3); + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/intrinsics.f_u64.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/intrinsics.f_u64.runtime-optimized.after.mir index 0a5fa6daa9285..07ee6eb87e301 100644 --- a/tests/mir-opt/pre-codegen/intrinsics.f_u64.runtime-optimized.after.mir +++ b/tests/mir-opt/pre-codegen/intrinsics.f_u64.runtime-optimized.after.mir @@ -13,6 +13,7 @@ fn f_u64() -> () { } bb1: { + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/intrinsics.f_unit.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/intrinsics.f_unit.runtime-optimized.after.mir index 7c35963169d7c..4359dcc520fc1 100644 --- a/tests/mir-opt/pre-codegen/intrinsics.f_unit.runtime-optimized.after.mir +++ b/tests/mir-opt/pre-codegen/intrinsics.f_unit.runtime-optimized.after.mir @@ -13,6 +13,7 @@ fn f_unit() -> () { } bb1: { + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff index 9db63b97aa8a5..d8c2c5653c876 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff @@ -93,6 +93,7 @@ _3 = copy _4 as *mut u8 (PtrToPtr); StorageDead(_4); StorageDead(_3); + _0 = const (); StorageDead(_1); return; } diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff index 72ac52df3337f..faf7fe86d542d 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff @@ -93,6 +93,7 @@ _3 = copy _4 as *mut u8 (PtrToPtr); StorageDead(_4); StorageDead(_3); + _0 = const (); StorageDead(_1); return; } diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff index a53e3d63d224e..b99794698a810 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff @@ -93,6 +93,7 @@ _3 = copy _4 as *mut u8 (PtrToPtr); StorageDead(_4); StorageDead(_3); + _0 = const (); StorageDead(_1); return; } diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff index d0e4c43f6460f..d7128d473856b 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff @@ -93,6 +93,7 @@ _3 = copy _4 as *mut u8 (PtrToPtr); StorageDead(_4); StorageDead(_3); + _0 = const (); StorageDead(_1); return; } diff --git a/tests/mir-opt/pre-codegen/loops.filter_mapped.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/loops.filter_mapped.runtime-optimized.after.mir index b3d38472a6a72..6643d93afd2ec 100644 --- a/tests/mir-opt/pre-codegen/loops.filter_mapped.runtime-optimized.after.mir +++ b/tests/mir-opt/pre-codegen/loops.filter_mapped.runtime-optimized.after.mir @@ -54,6 +54,7 @@ fn filter_mapped(_1: impl Iterator, _2: impl Fn(T) -> Option) -> () } bb4: { + _0 = const (); StorageDead(_7); drop(_4) -> [return: bb5, unwind continue]; } diff --git a/tests/mir-opt/pre-codegen/loops.int_range.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/loops.int_range.runtime-optimized.after.mir index cd146079c6328..7de0f54f28861 100644 --- a/tests/mir-opt/pre-codegen/loops.int_range.runtime-optimized.after.mir +++ b/tests/mir-opt/pre-codegen/loops.int_range.runtime-optimized.after.mir @@ -74,6 +74,7 @@ fn int_range(_1: usize, _2: usize) -> () { bb2: { StorageDead(_6); + _0 = const (); StorageDead(_9); return; } diff --git a/tests/mir-opt/pre-codegen/loops.mapped.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/loops.mapped.runtime-optimized.after.mir index f7c294bc1feb2..256d933552d1f 100644 --- a/tests/mir-opt/pre-codegen/loops.mapped.runtime-optimized.after.mir +++ b/tests/mir-opt/pre-codegen/loops.mapped.runtime-optimized.after.mir @@ -73,6 +73,7 @@ fn mapped(_1: impl Iterator, _2: impl Fn(T) -> U) -> () { StorageDead(_8); StorageDead(_7); StorageDead(_6); + _0 = const (); StorageDead(_12); drop(_4) -> [return: bb5, unwind continue]; } diff --git a/tests/mir-opt/pre-codegen/loops.vec_move.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/loops.vec_move.runtime-optimized.after.mir index 6815c22fc9552..3504a13795ff5 100644 --- a/tests/mir-opt/pre-codegen/loops.vec_move.runtime-optimized.after.mir +++ b/tests/mir-opt/pre-codegen/loops.vec_move.runtime-optimized.after.mir @@ -281,6 +281,7 @@ fn vec_move(_1: Vec) -> () { } bb10: { + _0 = const (); StorageDead(_23); StorageDead(_24); drop(_22) -> [return: bb11, unwind continue]; diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-abort.diff index 5b39e45806e33..8f68119c7266d 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-abort.diff @@ -54,6 +54,7 @@ - _7 = copy _8; + _7 = const 42_u32; StorageDead(_8); + _0 = const (); StorageDead(_7); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-unwind.diff index ea2742a6471a1..d3fa5ff864c14 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-unwind.diff @@ -54,6 +54,7 @@ - _7 = copy _8; + _7 = const 42_u32; StorageDead(_8); + _0 = const (); StorageDead(_7); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-abort.diff index 5b39e45806e33..8f68119c7266d 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-abort.diff @@ -54,6 +54,7 @@ - _7 = copy _8; + _7 = const 42_u32; StorageDead(_8); + _0 = const (); StorageDead(_7); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-unwind.diff index ea2742a6471a1..d3fa5ff864c14 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-unwind.diff @@ -54,6 +54,7 @@ - _7 = copy _8; + _7 = const 42_u32; StorageDead(_8); + _0 = const (); StorageDead(_7); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff index f7fe08831b97d..59571d145c2ef 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff @@ -59,6 +59,7 @@ + StorageDead(_9); + StorageDead(_10); + nop; + _0 = const (); StorageDead(_7); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff index 6e36386bea601..b877371d2723e 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff @@ -59,6 +59,7 @@ + StorageDead(_9); + StorageDead(_10); + nop; + _0 = const (); StorageDead(_7); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff index f7fe08831b97d..59571d145c2ef 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff @@ -59,6 +59,7 @@ + StorageDead(_9); + StorageDead(_10); + nop; + _0 = const (); StorageDead(_7); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff index 6e36386bea601..b877371d2723e 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff @@ -59,6 +59,7 @@ + StorageDead(_9); + StorageDead(_10); + nop; + _0 = const (); StorageDead(_7); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.32bit.panic-abort.mir index 425b95db3363a..cc7ed0843fef7 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.32bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.32bit.panic-abort.mir @@ -13,6 +13,7 @@ fn main() -> () { } bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.32bit.panic-unwind.mir index 425b95db3363a..cc7ed0843fef7 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.32bit.panic-unwind.mir @@ -13,6 +13,7 @@ fn main() -> () { } bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.64bit.panic-abort.mir index 425b95db3363a..cc7ed0843fef7 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.64bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.64bit.panic-abort.mir @@ -13,6 +13,7 @@ fn main() -> () { } bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.64bit.panic-unwind.mir index 425b95db3363a..cc7ed0843fef7 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.SimplifyLocals-final.after.64bit.panic-unwind.mir @@ -13,6 +13,7 @@ fn main() -> () { } bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.32bit.panic-abort.mir index 1be0cd30068eb..d682c76299850 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.32bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.32bit.panic-abort.mir @@ -13,6 +13,7 @@ fn main() -> () { } bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.32bit.panic-unwind.mir index 1be0cd30068eb..d682c76299850 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.32bit.panic-unwind.mir @@ -13,6 +13,7 @@ fn main() -> () { } bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.64bit.panic-abort.mir index 1be0cd30068eb..d682c76299850 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.64bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.64bit.panic-abort.mir @@ -13,6 +13,7 @@ fn main() -> () { } bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.64bit.panic-unwind.mir index 1be0cd30068eb..d682c76299850 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.runtime-optimized.after.64bit.panic-unwind.mir @@ -13,6 +13,7 @@ fn main() -> () { } bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.runtime-optimized.after.panic-abort.mir index 47c78d4e80138..17f5f5f8e613d 100644 --- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.runtime-optimized.after.panic-abort.mir @@ -55,6 +55,7 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { bb2: { StorageDead(_5); + _0 = const (); StorageDead(_7); drop(_3) -> [return: bb3, unwind unreachable]; } diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.runtime-optimized.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.runtime-optimized.after.panic-unwind.mir index 5dfcba18be02f..f50919caab65d 100644 --- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.runtime-optimized.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.runtime-optimized.after.panic-unwind.mir @@ -55,6 +55,7 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { bb2: { StorageDead(_5); + _0 = const (); StorageDead(_7); drop(_3) -> [return: bb3, unwind continue]; } diff --git a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.runtime-optimized.after.panic-abort.mir index 8e89ffefe8ee7..67bc7b5666e49 100644 --- a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.runtime-optimized.after.panic-abort.mir @@ -45,6 +45,7 @@ fn inclusive_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb3: { + _0 = const (); StorageDead(_6); StorageDead(_7); StorageDead(_5); diff --git a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.runtime-optimized.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.runtime-optimized.after.panic-unwind.mir index 084708b53c933..338c1cfcae8f5 100644 --- a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.runtime-optimized.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.runtime-optimized.after.panic-unwind.mir @@ -45,6 +45,7 @@ fn inclusive_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb3: { + _0 = const (); StorageDead(_6); StorageDead(_7); StorageDead(_5); diff --git a/tests/mir-opt/pre-codegen/simple_option_map.map_via_question_mark.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/simple_option_map.map_via_question_mark.runtime-optimized.after.mir index cde5538f54ad5..10f3bbea6bc9b 100644 --- a/tests/mir-opt/pre-codegen/simple_option_map.map_via_question_mark.runtime-optimized.after.mir +++ b/tests/mir-opt/pre-codegen/simple_option_map.map_via_question_mark.runtime-optimized.after.mir @@ -3,30 +3,27 @@ fn map_via_question_mark(_1: Option) -> Option { debug x => _1; let mut _0: std::option::Option; - let mut _4: std::option::Option; - let mut _7: std::ops::ControlFlow, i32>; - let _8: i32; - let mut _9: i32; + let mut _4: std::ops::ControlFlow, i32>; + let _5: i32; + let mut _6: i32; scope 1 { debug residual => const Option::::None; scope 5 (inlined as FromResidual>>::from_residual) { - let mut _3: isize; - let mut _5: bool; } } scope 2 { - debug val => _8; + debug val => _5; } scope 3 (inlined as Try>::branch) { let mut _2: isize; - let _6: i32; + let _3: i32; scope 4 { } } bb0: { - StorageLive(_9); - StorageLive(_7); + StorageLive(_6); + StorageLive(_4); StorageLive(_2); _2 = discriminant(_1); switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb4]; @@ -34,30 +31,23 @@ fn map_via_question_mark(_1: Option) -> Option { bb1: { StorageDead(_2); - StorageLive(_3); - StorageLive(_5); - _3 = discriminant(_4); - _5 = Eq(copy _3, const 0_isize); - assume(move _5); _0 = const Option::::None; - StorageDead(_5); - StorageDead(_3); - StorageDead(_9); - StorageDead(_7); + StorageDead(_6); + StorageDead(_4); goto -> bb3; } bb2: { - StorageLive(_6); - _6 = copy ((_1 as Some).0: i32); - _7 = ControlFlow::, i32>::Continue(copy _6); - StorageDead(_6); + StorageLive(_3); + _3 = copy ((_1 as Some).0: i32); + _4 = ControlFlow::, i32>::Continue(copy _3); + StorageDead(_3); StorageDead(_2); - _8 = copy ((_7 as Continue).0: i32); - _9 = Add(copy _8, const 1_i32); - _0 = Option::::Some(move _9); - StorageDead(_9); - StorageDead(_7); + _5 = copy ((_4 as Continue).0: i32); + _6 = Add(copy _5, const 1_i32); + _0 = Option::::Some(move _6); + StorageDead(_6); + StorageDead(_4); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-unwind.mir index bdb72abeac7e4..9cf28f01c2342 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-unwind.mir @@ -125,6 +125,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb6: { + _0 = const (); StorageDead(_15); StorageDead(_16); StorageDead(_14); diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.runtime-optimized.after.panic-abort.mir index 2e662fc36ac67..6f991a484bb07 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.runtime-optimized.after.panic-abort.mir @@ -195,6 +195,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageDead(_12); StorageDead(_15); StorageDead(_20); + _0 = const (); StorageDead(_23); drop(_2) -> [return: bb11, unwind unreachable]; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.runtime-optimized.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.runtime-optimized.after.panic-unwind.mir index dc5feaa1344b0..bd1010ec1690b 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.runtime-optimized.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.runtime-optimized.after.panic-unwind.mir @@ -195,6 +195,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageDead(_12); StorageDead(_15); StorageDead(_20); + _0 = const (); StorageDead(_23); drop(_2) -> [return: bb11, unwind continue]; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.runtime-optimized.after.panic-abort.mir index e817d9cd87289..1baee184e2958 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.runtime-optimized.after.panic-abort.mir @@ -63,6 +63,7 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb2: { StorageDead(_6); + _0 = const (); StorageDead(_8); drop(_2) -> [return: bb3, unwind unreachable]; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.runtime-optimized.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.runtime-optimized.after.panic-unwind.mir index b780f686570f8..d7b8a08e00316 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.runtime-optimized.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.runtime-optimized.after.panic-unwind.mir @@ -63,6 +63,7 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb2: { StorageDead(_6); + _0 = const (); StorageDead(_8); drop(_2) -> [return: bb3, unwind continue]; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.runtime-optimized.after.panic-abort.mir index c2c521729b75c..e4c82f9276034 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.runtime-optimized.after.panic-abort.mir @@ -303,6 +303,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb15: { StorageDead(_20); + _0 = const (); StorageDead(_35); StorageDead(_14); drop(_2) -> [return: bb16, unwind unreachable]; diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.runtime-optimized.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.runtime-optimized.after.panic-unwind.mir index c11eab18521dd..75782845f14d1 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.runtime-optimized.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.runtime-optimized.after.panic-unwind.mir @@ -311,6 +311,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { bb17: { StorageDead(_20); + _0 = const (); StorageDead(_35); StorageDead(_14); drop(_2) -> [return: bb18, unwind continue]; diff --git a/tests/mir-opt/pre-codegen/transmutes.forget_at_home.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/transmutes.forget_at_home.runtime-optimized.after.mir index 80ad68c8a48bb..6bd43650734c0 100644 --- a/tests/mir-opt/pre-codegen/transmutes.forget_at_home.runtime-optimized.after.mir +++ b/tests/mir-opt/pre-codegen/transmutes.forget_at_home.runtime-optimized.after.mir @@ -15,6 +15,7 @@ fn forget_at_home(_1: String) -> () { } bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/pre-codegen/transmutes.rs b/tests/mir-opt/pre-codegen/transmutes.rs index d8369e839b6ba..6b1129d68134c 100644 --- a/tests/mir-opt/pre-codegen/transmutes.rs +++ b/tests/mir-opt/pre-codegen/transmutes.rs @@ -45,6 +45,7 @@ pub fn pad_for_alignment(x: u32) -> Align64 { pub fn forget_at_home(x: String) { // CHECK-LABEL: fn forget_at_home // CHECK: bb0: + // CHECK-NEXT: _0 = const (); // CHECK-NEXT: return; unsafe { transmute_prefix(x) } } diff --git a/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir index ffb1fe334b201..6ce5a410ff666 100644 --- a/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir +++ b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir @@ -51,6 +51,7 @@ fn unaligned_copy_generic(_1: *const T, _2: *mut T) -> () { (*_6) = copy _7; StorageDead(_7); StorageDead(_6); + _0 = const (); StorageDead(_5); return; } diff --git a/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_manual.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_manual.runtime-optimized.after.mir index 450df935a0fb3..b3b4365bc46ad 100644 --- a/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_manual.runtime-optimized.after.mir +++ b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_manual.runtime-optimized.after.mir @@ -27,6 +27,7 @@ fn unaligned_copy_manual(_1: *const u128, _2: *mut u128) -> () { _5 = copy ((*_3).0: u128); ((*_4).0: u128) = move _5; StorageDead(_5); + _0 = const (); StorageDead(_4); StorageDead(_3); return; diff --git a/tests/mir-opt/remove_zsts.get_union.RemoveZsts.diff b/tests/mir-opt/remove_zsts.get_union.RemoveZsts.diff index 748c18ba7da97..ed338d69906c3 100644 --- a/tests/mir-opt/remove_zsts.get_union.RemoveZsts.diff +++ b/tests/mir-opt/remove_zsts.get_union.RemoveZsts.diff @@ -7,11 +7,10 @@ bb0: { - StorageLive(_1); -- _1 = (); ++ nop; + _1 = (); - _0 = Foo { x: move _1 }; - StorageDead(_1); -+ nop; -+ nop; + _0 = Foo { x: const () }; + nop; return; diff --git a/tests/mir-opt/remove_zsts.remove_generic_array.RemoveZsts.diff b/tests/mir-opt/remove_zsts.remove_generic_array.RemoveZsts.diff index 2ac944a6c6bc5..3241d40363753 100644 --- a/tests/mir-opt/remove_zsts.remove_generic_array.RemoveZsts.diff +++ b/tests/mir-opt/remove_zsts.remove_generic_array.RemoveZsts.diff @@ -1,12 +1,14 @@ - // MIR for `remove_generic_array` before RemoveZsts + // MIR for `remove_generic_array` after RemoveZsts - fn remove_generic_array(_1: T) -> () { + fn remove_generic_array(_1: T) -> ([T; 0], [T; 0]) { debug x => _1; - let mut _0: (); + let mut _0: ([T; 0], [T; 0]); let _2: [T; 0]; let mut _3: T; let mut _5: T; + let mut _6: [T; 0]; + let mut _7: [T; 0]; scope 1 { - debug a => _2; + debug a => const ZeroSized: [T; 0]; @@ -22,20 +24,30 @@ + nop; StorageLive(_3); _3 = copy _1; -- _2 = []; -+ nop; + _2 = []; StorageDead(_3); - StorageLive(_4); + nop; StorageLive(_5); _5 = copy _1; -- _4 = []; -+ nop; + _4 = []; StorageDead(_5); -- _0 = const (); +- StorageLive(_6); +- _6 = copy _2; +- StorageLive(_7); +- _7 = copy _4; +- _0 = (move _6, move _7); +- StorageDead(_7); +- StorageDead(_6); - StorageDead(_4); - StorageDead(_2); + nop; ++ _6 = const ZeroSized: [T; 0]; ++ nop; ++ _7 = const ZeroSized: [T; 0]; ++ _0 = (const ZeroSized: [T; 0], const ZeroSized: [T; 0]); ++ nop; ++ nop; + nop; + nop; return; diff --git a/tests/mir-opt/remove_zsts.rs b/tests/mir-opt/remove_zsts.rs index baf9d8ece266d..ff80edda68e77 100644 --- a/tests/mir-opt/remove_zsts.rs +++ b/tests/mir-opt/remove_zsts.rs @@ -15,14 +15,14 @@ fn get_union() -> Foo { const MYSTERY: usize = 280_usize.isqrt() - 260_usize.isqrt(); // EMIT_MIR remove_zsts.remove_generic_array.RemoveZsts.diff -fn remove_generic_array(x: T) { +fn remove_generic_array(x: T) -> ([T; 0], [T; MYSTERY]) { // CHECK-LABEL: fn remove_generic_array // CHECK: debug a => const ZeroSized: [T; 0]; // CHECK: debug b => const ZeroSized: [T; 0]; - // CHECK-NOT: = []; - // CHECK-NOT: ; 1] + // CHECK: _0 = (const ZeroSized: [T; 0], const ZeroSized: [T; 0]); let a = [x; 0]; let b = [x; MYSTERY]; + (a, b) } fn main() { diff --git a/tests/mir-opt/remove_zsts_move_elimination.discriminant.RemoveZsts.diff b/tests/mir-opt/remove_zsts_move_elimination.discriminant.RemoveZsts.diff new file mode 100644 index 0000000000000..7c2b40b6cc33e --- /dev/null +++ b/tests/mir-opt/remove_zsts_move_elimination.discriminant.RemoveZsts.diff @@ -0,0 +1,12 @@ +- // MIR for `discriminant` before RemoveZsts ++ // MIR for `discriminant` after RemoveZsts + + fn discriminant() -> Unit { + let mut _0: Unit; + + bb0: { + discriminant(_0) = 0; + return; + } + } + diff --git a/tests/mir-opt/remove_zsts_move_elimination.indirect.RemoveZsts.diff b/tests/mir-opt/remove_zsts_move_elimination.indirect.RemoveZsts.diff new file mode 100644 index 0000000000000..2d6fc87bbd906 --- /dev/null +++ b/tests/mir-opt/remove_zsts_move_elimination.indirect.RemoveZsts.diff @@ -0,0 +1,16 @@ +- // MIR for `indirect` before RemoveZsts ++ // MIR for `indirect` after RemoveZsts + + fn indirect(_1: *mut (), _2: *mut Unit) -> () { + let mut _0: (); + + bb0: { +- (*_1) = (); +- discriminant((*_2)) = 0; ++ nop; ++ nop; + _0 = (); + return; + } + } + diff --git a/tests/mir-opt/remove_zsts_move_elimination.local.RemoveZsts.diff b/tests/mir-opt/remove_zsts_move_elimination.local.RemoveZsts.diff new file mode 100644 index 0000000000000..4212876542ec5 --- /dev/null +++ b/tests/mir-opt/remove_zsts_move_elimination.local.RemoveZsts.diff @@ -0,0 +1,19 @@ +- // MIR for `local` before RemoveZsts ++ // MIR for `local` after RemoveZsts + + fn local() -> () { + let mut _0: (); + let mut _1: (); + + bb0: { +- StorageLive(_1); ++ nop; + _1 = (); +- _0 = move _1; +- StorageDead(_1); ++ _0 = const (); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/remove_zsts_move_elimination.rs b/tests/mir-opt/remove_zsts_move_elimination.rs new file mode 100644 index 0000000000000..2065c1f7c58ba --- /dev/null +++ b/tests/mir-opt/remove_zsts_move_elimination.rs @@ -0,0 +1,68 @@ +//@ test-mir-pass: RemoveZsts + +#![crate_type = "lib"] +#![feature(custom_mir, core_intrinsics)] +use std::intrinsics::mir::*; + +// Under move elimination semantics we can't remove direct assignments since they have +// the side effect of creating an allocation for a local. If they are truly +// dead then they will be eliminated later optimization passes. + +// Preserve ZST writes while removing storage markers and replacing operands with constants. +// EMIT_MIR remove_zsts_move_elimination.local.RemoveZsts.diff +#[custom_mir(dialect = "runtime", phase = "initial")] +pub fn local() { + // CHECK-LABEL: fn local( + // CHECK-NOT: StorageLive + // CHECK: {{_[0-9]+}} = (); + // CHECK-NEXT: _0 = const (); + // CHECK-NOT: StorageDead + // CHECK: return; + mir! { + let value: (); + { + StorageLive(value); + value = (); + RET = Move(value); + StorageDead(value); + Return() + } + } +} + +pub enum Unit { + Value, +} + +// Preserve ZST SetDiscriminant since it allocates the destination local. +// EMIT_MIR remove_zsts_move_elimination.discriminant.RemoveZsts.diff +#[custom_mir(dialect = "runtime", phase = "initial")] +pub fn discriminant() -> Unit { + // CHECK-LABEL: fn discriminant( + // CHECK: discriminant(_0) = 0; + // CHECK-NEXT: return; + mir! { + { + SetDiscriminant(RET, 0); + Return() + } + } +} + +// Indirect ZST writes cannot allocate a local and can still be removed. +// EMIT_MIR remove_zsts_move_elimination.indirect.RemoveZsts.diff +#[custom_mir(dialect = "runtime", phase = "initial")] +pub unsafe fn indirect(value: *mut (), discriminant: *mut Unit) { + // CHECK-LABEL: fn indirect( + // CHECK-NOT: (* + // CHECK: _0 = (); + // CHECK-NEXT: return; + mir! { + { + *value = (); + SetDiscriminant(*discriminant, 0); + RET = (); + Return() + } + } +} diff --git a/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-inst-simplify.panic-abort.diff b/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-inst-simplify.panic-abort.diff index c67fd69235b47..60eb2add6cb5c 100644 --- a/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-inst-simplify.panic-abort.diff +++ b/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-inst-simplify.panic-abort.diff @@ -9,8 +9,8 @@ bb0: { StorageLive(_1); _1 = const false; -- switchInt(move _1) -> [0: bb2, otherwise: bb1]; -+ goto -> bb2; +- switchInt(move _1) -> [0: bb3, otherwise: bb1]; ++ goto -> bb3; } bb1: { @@ -18,6 +18,16 @@ } bb2: { + _0 = const (); + goto -> bb4; + } + + bb3: { + _0 = const (); + goto -> bb4; + } + + bb4: { StorageDead(_1); return; } diff --git a/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-inst-simplify.panic-unwind.diff b/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-inst-simplify.panic-unwind.diff index 07f179dfdd454..ff6f4f5d397c1 100644 --- a/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-inst-simplify.panic-unwind.diff +++ b/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-inst-simplify.panic-unwind.diff @@ -9,8 +9,8 @@ bb0: { StorageLive(_1); _1 = const false; -- switchInt(move _1) -> [0: bb2, otherwise: bb1]; -+ goto -> bb2; +- switchInt(move _1) -> [0: bb3, otherwise: bb1]; ++ goto -> bb3; } bb1: { @@ -18,6 +18,16 @@ } bb2: { + _0 = const (); + goto -> bb4; + } + + bb3: { + _0 = const (); + goto -> bb4; + } + + bb4: { StorageDead(_1); return; } diff --git a/tests/mir-opt/simplify_if.rs b/tests/mir-opt/simplify_if.rs index 6aca233655544..e86f19a7823a8 100644 --- a/tests/mir-opt/simplify_if.rs +++ b/tests/mir-opt/simplify_if.rs @@ -7,6 +7,7 @@ fn main() { // CHECK-LABEL: fn main( // CHECK: bb0: { + // CHECK-NEXT: _0 = const (); // CHECK-NEXT: return; if false { noop(); diff --git a/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-abort.diff b/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-abort.diff index 98727b80ca12e..2a2ce9ddb81fd 100644 --- a/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-abort.diff +++ b/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-abort.diff @@ -25,31 +25,37 @@ StorageDead(_3); StorageDead(_2); _5 = discriminant((_1.0: std::option::Option)); - switchInt(move _5) -> [1: bb1, 0: bb3, otherwise: bb5]; + switchInt(move _5) -> [1: bb1, 0: bb3, otherwise: bb6]; } bb1: { _4 = discriminant((_1.1: std::option::Option)); - switchInt(move _4) -> [0: bb2, 1: bb3, otherwise: bb5]; + switchInt(move _4) -> [0: bb2, 1: bb3, otherwise: bb6]; } bb2: { StorageLive(_6); _6 = copy (((_1.0: std::option::Option) as Some).0: u8); + _0 = no_retag const (); StorageDead(_6); - goto -> bb3; + goto -> bb4; } bb3: { - drop(_1) -> [return: bb4, unwind unreachable]; + _0 = const (); + goto -> bb4; } bb4: { + drop(_1) -> [return: bb5, unwind unreachable]; + } + + bb5: { StorageDead(_1); return; } - bb5: { + bb6: { unreachable; } } diff --git a/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-unwind.diff b/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-unwind.diff index 12b0a7f4ba535..1517268b7df5a 100644 --- a/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-unwind.diff +++ b/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-unwind.diff @@ -25,31 +25,37 @@ StorageDead(_3); StorageDead(_2); _5 = discriminant((_1.0: std::option::Option)); - switchInt(move _5) -> [1: bb1, 0: bb3, otherwise: bb5]; + switchInt(move _5) -> [1: bb1, 0: bb3, otherwise: bb6]; } bb1: { _4 = discriminant((_1.1: std::option::Option)); - switchInt(move _4) -> [0: bb2, 1: bb3, otherwise: bb5]; + switchInt(move _4) -> [0: bb2, 1: bb3, otherwise: bb6]; } bb2: { StorageLive(_6); _6 = copy (((_1.0: std::option::Option) as Some).0: u8); + _0 = no_retag const (); StorageDead(_6); - goto -> bb3; + goto -> bb4; } bb3: { - drop(_1) -> [return: bb4, unwind continue]; + _0 = const (); + goto -> bb4; } bb4: { + drop(_1) -> [return: bb5, unwind continue]; + } + + bb5: { StorageDead(_1); return; } - bb5: { + bb6: { unreachable; } } diff --git a/tests/mir-opt/simplify_match.main.GVN.panic-abort.diff b/tests/mir-opt/simplify_match.main.GVN.panic-abort.diff index 30c3b0bcfa1d7..6674b425bd492 100644 --- a/tests/mir-opt/simplify_match.main.GVN.panic-abort.diff +++ b/tests/mir-opt/simplify_match.main.GVN.panic-abort.diff @@ -16,15 +16,20 @@ - _1 = copy _2; + _1 = const false; StorageDead(_2); -- switchInt(copy _1) -> [0: bb2, otherwise: bb1]; -+ switchInt(const false) -> [0: bb2, otherwise: bb1]; +- switchInt(copy _1) -> [0: bb1, otherwise: bb2]; ++ switchInt(const false) -> [0: bb1, otherwise: bb2]; } bb1: { - _0 = noop() -> [return: bb2, unwind unreachable]; + _0 = const (); + goto -> bb3; } bb2: { + _0 = noop() -> [return: bb3, unwind unreachable]; + } + + bb3: { StorageDead(_1); return; } diff --git a/tests/mir-opt/simplify_match.main.GVN.panic-unwind.diff b/tests/mir-opt/simplify_match.main.GVN.panic-unwind.diff index 7923d3210d83f..2cef773f0d1c4 100644 --- a/tests/mir-opt/simplify_match.main.GVN.panic-unwind.diff +++ b/tests/mir-opt/simplify_match.main.GVN.panic-unwind.diff @@ -16,15 +16,20 @@ - _1 = copy _2; + _1 = const false; StorageDead(_2); -- switchInt(copy _1) -> [0: bb2, otherwise: bb1]; -+ switchInt(const false) -> [0: bb2, otherwise: bb1]; +- switchInt(copy _1) -> [0: bb1, otherwise: bb2]; ++ switchInt(const false) -> [0: bb1, otherwise: bb2]; } bb1: { - _0 = noop() -> [return: bb2, unwind continue]; + _0 = const (); + goto -> bb3; } bb2: { + _0 = noop() -> [return: bb3, unwind continue]; + } + + bb3: { StorageDead(_1); return; } diff --git a/tests/mir-opt/simplify_match.rs b/tests/mir-opt/simplify_match.rs index ca9dac40aa917..1776379086cab 100644 --- a/tests/mir-opt/simplify_match.rs +++ b/tests/mir-opt/simplify_match.rs @@ -9,6 +9,7 @@ fn noop() {} // CHECK: debug x => const false; // CHECK-NOT: switchInt // CHECK: bb0: { +// CHECK-NEXT: _0 = const (); // CHECK-NEXT: return; fn main() { match { diff --git a/tests/mir-opt/sroa/scalable_sroa.bar.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/scalable_sroa.bar.ScalarReplacementOfAggregates.diff index fa4b14620b10e..5dc6a85ab5725 100644 --- a/tests/mir-opt/sroa/scalable_sroa.bar.ScalarReplacementOfAggregates.diff +++ b/tests/mir-opt/sroa/scalable_sroa.bar.ScalarReplacementOfAggregates.diff @@ -25,6 +25,7 @@ bb1: { _3 = copy (*_1)[_4]; StorageDead(_4); + _0 = const (); StorageDead(_3); return; } diff --git a/tests/mir-opt/sroa/scalable_sroa.foo.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/scalable_sroa.foo.ScalarReplacementOfAggregates.diff index 0aceea57e17a6..ee8f64fe33327 100644 --- a/tests/mir-opt/sroa/scalable_sroa.foo.ScalarReplacementOfAggregates.diff +++ b/tests/mir-opt/sroa/scalable_sroa.foo.ScalarReplacementOfAggregates.diff @@ -25,6 +25,7 @@ bb1: { _3 = copy (*_1)[_4]; StorageDead(_4); + _0 = const (); StorageDead(_3); return; } diff --git a/tests/mir-opt/sroa/simd_sroa.foo.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/simd_sroa.foo.ScalarReplacementOfAggregates.diff index 744032471089d..477d05b90702a 100644 --- a/tests/mir-opt/sroa/simd_sroa.foo.ScalarReplacementOfAggregates.diff +++ b/tests/mir-opt/sroa/simd_sroa.foo.ScalarReplacementOfAggregates.diff @@ -25,6 +25,7 @@ bb1: { _3 = copy (*_1)[_4]; StorageDead(_4); + _0 = const (); StorageDead(_3); return; } diff --git a/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir index 64711755f7394..585c6f3fdb06a 100644 --- a/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir +++ b/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir @@ -8,6 +8,7 @@ fn process_void(_1: *const Void) -> () { } bb0: { + _0 = const (); return; } } diff --git a/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir index 89f4cbc2edea5..ecea1ced574f9 100644 --- a/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir +++ b/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir @@ -43,6 +43,7 @@ fn main() -> () { _5 = copy _6 as *const ! (PtrToPtr); StorageDead(_6); StorageDead(_5); + _0 = const (); StorageDead(_4); return; } diff --git a/tests/run-make/const_fn_mir/dump.mir b/tests/run-make/const_fn_mir/dump.mir index 2b5c684bbef00..f758dea568e8c 100644 --- a/tests/run-make/const_fn_mir/dump.mir +++ b/tests/run-make/const_fn_mir/dump.mir @@ -41,6 +41,7 @@ fn main() -> () { } bb1: { + _0 = const (); return; } } diff --git a/tests/ui/inline-const/const-expr-generic-err.stderr b/tests/ui/inline-const/const-expr-generic-err.stderr index b2f22407e189a..a1f5e335c4603 100644 --- a/tests/ui/inline-const/const-expr-generic-err.stderr +++ b/tests/ui/inline-const/const-expr-generic-err.stderr @@ -11,6 +11,14 @@ note: erroneous constant encountered LL | const { assert!(std::mem::size_of::() == 0); } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: erroneous constant encountered + --> $DIR/const-expr-generic-err.rs:4:5 + | +LL | const { assert!(std::mem::size_of::() == 0); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + note: the above error was encountered while instantiating `fn foo::` --> $DIR/const-expr-generic-err.rs:12:5 | From 7350c9f93402a051966762b0ad2ee03090cd0965 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Sat, 26 Sep 2026 05:47:34 +0100 Subject: [PATCH 21/21] Ignore ZST assignments in the inliner cost model --- .../rustc_mir_transform/src/cost_checker.rs | 5 + .../inline_shims.drop.Inline.panic-abort.diff | 100 +++++++++++++++--- 2 files changed, 90 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index 32eef00812436..c9420b7b116fe 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -89,6 +89,11 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { NonDivergingIntrinsic::CopyNonOverlapping(..) => CALL_PENALTY, }; } + StatementKind::Assign((_, Rvalue::Use(Operand::Constant(ref constant), _))) + if matches!(constant.const_, Const::Val(ConstValue::ZeroSized, _)) => + { + // Assigning a ZST constant emits no code. + } StatementKind::Assign(..) => self.penalty += INSTR_COST, _ => {} } diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff index 4d138fcbede7d..68708958ab5b4 100644 --- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff @@ -13,13 +13,49 @@ + scope 2 (inlined std::ptr::drop_glue::> - shim(Some(Vec))) { + let mut _7: &mut std::vec::Vec; + let mut _8: (); ++ scope 3 (inlined as Drop>::drop) { ++ let mut _9: *mut [A]; ++ let mut _10: *mut A; ++ let mut _11: usize; ++ scope 4 (inlined Vec::::as_mut_ptr) { ++ scope 5 (inlined alloc::raw_vec::RawVec::::ptr) { ++ scope 6 (inlined alloc::raw_vec::RawVecInner::ptr::) { ++ scope 7 (inlined alloc::raw_vec::RawVecInner::non_null::) { ++ let mut _12: std::ptr::NonNull; ++ scope 8 (inlined NonNull::::cast::) { ++ scope 9 (inlined NonNull::::as_ptr) { ++ } ++ } ++ } ++ scope 10 (inlined NonNull::::as_ptr) { ++ } ++ } ++ } ++ } ++ scope 11 (inlined std::ptr::mut_ptr::::cast_slice) { ++ scope 12 (inlined slice_from_raw_parts_mut::) { ++ scope 13 (inlined std::ptr::from_raw_parts_mut::<[A], A>) { ++ } ++ } ++ } ++ scope 14 (inlined std::ptr::mut_ptr::::drop_in_place) { ++ scope 15 (inlined drop_in_place::<[A]>) { ++ let mut _13: &mut [A]; ++ scope 16 (inlined std::ptr::drop_glue::<[A]> - shim(Some([A]))) { ++ let mut _14: usize; ++ let mut _15: *mut A; ++ let mut _16: bool; ++ } ++ } ++ } ++ } + } + } -+ scope 3 (inlined drop_in_place::>) { -+ let mut _9: &mut std::option::Option; -+ scope 4 (inlined std::ptr::drop_glue::> - shim(Some(Option))) { -+ let mut _10: isize; -+ let mut _11: isize; ++ scope 17 (inlined drop_in_place::>) { ++ let mut _17: &mut std::option::Option; ++ scope 18 (inlined std::ptr::drop_glue::> - shim(Some(Option))) { ++ let mut _18: isize; ++ let mut _19: isize; + } + } @@ -33,7 +69,23 @@ + StorageLive(_7); + StorageLive(_8); + _7 = copy _6; -+ _8 = as Drop>::drop(move _7) -> [return: bb2, unwind unreachable]; ++ StorageLive(_11); ++ StorageLive(_9); ++ StorageLive(_10); ++ StorageLive(_12); ++ _12 = copy ((((*_7).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::NonNull); ++ _10 = copy _12 as *mut A (Transmute); ++ StorageDead(_12); ++ _11 = copy ((*_7).1: usize); ++ _9 = *mut [A] from (copy _10, copy _11); ++ StorageDead(_10); ++ StorageLive(_13); ++ _13 = &mut (*_9); ++ StorageLive(_14); ++ StorageLive(_15); ++ StorageLive(_16); ++ _14 = const 0_usize; ++ goto -> bb4; } bb1: { @@ -46,27 +98,45 @@ StorageLive(_5); _5 = copy _2; - _0 = drop_in_place::>(move _5) -> [return: bb2, unwind unreachable]; -+ StorageLive(_9); -+ _9 = &mut (*_5); -+ StorageLive(_10); -+ _10 = discriminant((*_9)); -+ switchInt(move _10) -> [0: bb3, otherwise: bb4]; ++ StorageLive(_17); ++ _17 = &mut (*_5); ++ StorageLive(_18); ++ _18 = discriminant((*_17)); ++ switchInt(move _18) -> [0: bb5, otherwise: bb6]; } bb2: { ++ _8 = const (); ++ StorageDead(_16); ++ StorageDead(_15); ++ StorageDead(_14); ++ StorageDead(_13); ++ StorageDead(_9); ++ StorageDead(_11); + drop(((*_6).0: alloc::raw_vec::RawVec)) -> [return: bb1, unwind unreachable]; + } + + bb3: { ++ _15 = &raw mut (*_13)[_14]; ++ _14 = Add(move _14, const 1_usize); ++ drop((*_15)) -> [return: bb4, unwind unreachable]; ++ } ++ ++ bb4: { ++ _16 = Eq(copy _11, copy _14); ++ switchInt(move _16) -> [0: bb3, otherwise: bb2]; ++ } ++ ++ bb5: { + _0 = const (); -+ StorageDead(_10); -+ StorageDead(_9); ++ StorageDead(_18); ++ StorageDead(_17); StorageDead(_5); return; + } + -+ bb4: { -+ drop((((*_9) as Some).0: B)) -> [return: bb3, unwind unreachable]; ++ bb6: { ++ drop((((*_17) as Some).0: B)) -> [return: bb5, unwind unreachable]; } }